r31820 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r31819‎ | r31820 | r31821 >
Date:21:06, 11 March 2008
Author:evan
Status:old
Tags:
Comment:
Re-tagging new version 0.8.0.
Modified paths:
  • /tags/extensions/OpenID/REL_0_8_0/OpenID (added) (history)
  • /tags/extensions/OpenID/REL_0_8_0/OpenID (added) (history)

Diff [purge]

Index: tags/extensions/OpenID/REL_0_8_0/OpenID/SpecialOpenID.body.php
@@ -0,0 +1,360 @@
 2+<?php
 3+/**
 4+ * SpecialOpenID.body.php -- Superclass for all
 5+ * Copyright 2006,2007 Internet Brands (http://www.internetbrands.com/)
 6+ * Copyright 2008 by Evan Prodromou (http://evan.prodromou.name/)
 7+ *
 8+ * This program is free software; you can redistribute it and/or modify
 9+ * it under the terms of the GNU General Public License as published by
 10+ * the Free Software Foundation; either version 2 of the License, or
 11+ * (at your option) any later version.
 12+ *
 13+ * This program is distributed in the hope that it will be useful,
 14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 16+ * GNU General Public License for more details.
 17+ *
 18+ * You should have received a copy of the GNU General Public License
 19+ * along with this program; if not, write to the Free Software
 20+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 21+ *
 22+ * @author Evan Prodromou <evan@prodromou.name>
 23+ * @addtogroup Extensions
 24+ */
 25+
 26+# FIXME: for login(); figure out better way to share this code
 27+# between Login and Convert
 28+
 29+require_once("Auth/OpenID/Consumer.php");
 30+require_once("Auth/OpenID/SReg.php");
 31+require_once("Auth/OpenID/FileStore.php");
 32+
 33+class SpecialOpenID extends SpecialPage {
 34+
 35+ function getOpenIDStore($storeType, $prefix, $options) {
 36+ global $wgOut;
 37+
 38+ # FIXME: support other kinds of store
 39+ # XXX: used to support memc, now use memcached from php-openid
 40+
 41+ switch ($storeType) {
 42+
 43+ case 'file':
 44+ # Auto-create path if it doesn't exist
 45+ if (!is_dir($options['path'])) {
 46+ if (!mkdir($options['path'], 0770, true)) {
 47+ $wgOut->showErrorPage('openidconfigerror', 'openidconfigerrortext');
 48+ return NULL;
 49+ }
 50+ }
 51+ return new Auth_OpenID_FileStore($options['path']);
 52+
 53+ default:
 54+ $wgOut->showErrorPage('openidconfigerror', 'openidconfigerrortext');
 55+ }
 56+ }
 57+
 58+ function xriBase($xri) {
 59+ if (substr($xri, 0, 6) == 'xri://') {
 60+ return substr($xri, 6);
 61+ } else {
 62+ return $xri;
 63+ }
 64+ }
 65+
 66+ function xriToUrl($xri) {
 67+ return 'http://xri.net/' . OpenIDXriBase($xri);
 68+ }
 69+
 70+ function OpenIDToUrl($openid) {
 71+ /* ID is either an URL already or an i-name */
 72+ if (Auth_Yadis_identifierScheme($openid) == 'XRI') {
 73+ return OpenIDXriToUrl($openid);
 74+ } else {
 75+ return $openid;
 76+ }
 77+ }
 78+
 79+ function LocalizedPageName(&$specialPageArray, $code) {
 80+
 81+ # The localized title of the special page is among the messages of the extension:
 82+ SpecialOpenID::loadMessages();
 83+ $text = wfMsg('openidlogin');
 84+
 85+ # Convert from title in text form to DBKey and put it into the alias array:
 86+ $title = Title::newFromText($text);
 87+ $specialPageArray['Form'][] = $title->getDBKey();
 88+
 89+ return true;
 90+ }
 91+
 92+ function loadMessages() {
 93+ static $messagesLoaded = false;
 94+ global $wgMessageCache;
 95+
 96+ if ( $messagesLoaded ) return true;
 97+
 98+ require( dirname( __FILE__ ) . '/OpenID.i18n.php' );
 99+ foreach ( $OpenIDMessages as $lang => $langMessages ) {
 100+ $wgMessageCache->addMessages( $langMessages, $lang );
 101+ }
 102+
 103+ $messagesLoaded = true;
 104+
 105+ return true;
 106+ }
 107+
 108+ function interwikiExpand($openid_url) {
 109+ # try to make it into a title object
 110+ $nt = Title::newFromText($openid_url);
 111+ # If it's got an iw, return that
 112+ if (!is_null($nt) && !is_null($nt->getInterwiki())
 113+ && strlen($nt->getInterwiki()) > 0) {
 114+ return $nt->getFullUrl();
 115+ } else {
 116+ return $openid_url;
 117+ }
 118+ }
 119+
 120+ function getUserUrl($user) {
 121+ $openid_url = null;
 122+
 123+ if (isset($user) && $user->getId() != 0) {
 124+ global $wgSharedDB, $wgDBprefix;
 125+ if (isset($wgSharedDB)) {
 126+ $tableName = "`${wgSharedDB}`.${wgDBprefix}user_openid";
 127+ } else {
 128+ $tableName = 'user_openid';
 129+ }
 130+
 131+ $dbr =& wfGetDB( DB_SLAVE );
 132+ $res = $dbr->select(array($tableName),
 133+ array('uoi_openid'),
 134+ array('uoi_user' => $user->getId()),
 135+ 'OpenIDGetUserUrl');
 136+
 137+ # This should return 0 or 1 result, since user is unique
 138+ # in the table.
 139+
 140+ while ($res && $row = $dbr->fetchObject($res)) {
 141+ $openid_url = $row->uoi_openid;
 142+ }
 143+ $dbr->freeResult($res);
 144+ }
 145+ return $openid_url;
 146+ }
 147+
 148+ # Login, Finish
 149+
 150+ function getConsumer() {
 151+ global $wgOpenIDConsumerStoreType, $wgOpenIDConsumerStorePath;
 152+
 153+ $store = $this->getOpenIDStore($wgOpenIDConsumerStoreType,
 154+ 'consumer',
 155+ array('path' => $wgOpenIDConsumerStorePath));
 156+
 157+ return new Auth_OpenID_Consumer($store);
 158+ }
 159+
 160+ function fullUrl($title) {
 161+ $nt = Title::makeTitleSafe(NS_SPECIAL, $title);
 162+ if (isset($nt)) {
 163+ return $nt->getFullURL();
 164+ } else {
 165+ return NULL;
 166+ }
 167+ }
 168+
 169+ function scriptUrl($title) {
 170+ global $wgServer, $wgScript;
 171+ $nt = Title::makeTitleSafe(NS_SPECIAL, $title);
 172+ if (isset($nt)) {
 173+ $dbkey = wfUrlencode( $nt->getPrefixedDBkey() );
 174+ return "{$wgServer}{$wgScript}?title={$dbkey}";
 175+ } else {
 176+ return $url;
 177+ }
 178+ }
 179+
 180+ function canLogin($openid_url) {
 181+
 182+ global $wgOpenIDConsumerDenyByDefault, $wgOpenIDConsumerAllow, $wgOpenIDConsumerDeny;
 183+
 184+ if ($this->isLocalUrl($openid_url)) {
 185+ return false;
 186+ }
 187+
 188+ if ($wgOpenIDConsumerDenyByDefault) {
 189+ $canLogin = false;
 190+ foreach ($wgOpenIDConsumerAllow as $allow) {
 191+ if (preg_match($allow, $openid_url)) {
 192+ wfDebug("OpenID: $openid_url matched allow pattern $allow.\n");
 193+ $canLogin = true;
 194+ foreach ($wgOpenIDConsumerDeny as $deny) {
 195+ if (preg_match($deny, $openid_url)) {
 196+ wfDebug("OpenID: $openid_url matched deny pattern $deny.\n");
 197+ $canLogin = false;
 198+ break;
 199+ }
 200+ }
 201+ break;
 202+ }
 203+ }
 204+ } else {
 205+ $canLogin = true;
 206+ foreach ($wgOpenIDConsumerDeny as $deny) {
 207+ if (preg_match($deny, $openid_url)) {
 208+ wfDebug("OpenID: $openid_url matched deny pattern $deny.\n");
 209+ $canLogin = false;
 210+ foreach ($wgOpenIDConsumerAllow as $allow) {
 211+ if (preg_match($allow, $openid_url)) {
 212+ wfDebug("OpenID: $openid_url matched allow pattern $allow.\n");
 213+ $canLogin = true;
 214+ break;
 215+ }
 216+ }
 217+ break;
 218+ }
 219+ }
 220+ }
 221+ return $canLogin;
 222+ }
 223+
 224+ function isLocalUrl($url) {
 225+
 226+ global $wgServer, $wgArticlePath;
 227+
 228+ $pattern = $wgServer . $wgArticlePath;
 229+ $pattern = str_replace('$1', '(.*)', $pattern);
 230+ $pattern = str_replace('?', '\?', $pattern);
 231+
 232+ return preg_match('|^' . $pattern . '$|', $url);
 233+ }
 234+
 235+ # Find the user with the given openid, if any
 236+
 237+ function getUser($openid) {
 238+ global $wgSharedDB, $wgDBprefix;
 239+
 240+ if (isset($wgSharedDB)) {
 241+ $tableName = "`$wgSharedDB`.${wgDBprefix}user_openid";
 242+ } else {
 243+ $tableName = 'user_openid';
 244+ }
 245+
 246+ $dbr =& wfGetDB( DB_SLAVE );
 247+ $id = $dbr->selectField($tableName, 'uoi_user',
 248+ array('uoi_openid' => $openid));
 249+ if ($id) {
 250+ $name = User::whoIs($id);
 251+ return User::newFromName($name);
 252+ } else {
 253+ return NULL;
 254+ }
 255+ }
 256+ function login($openid_url, $finish_page = 'OpenIDFinish') {
 257+
 258+ global $wgUser, $wgTrustRoot, $wgOut;
 259+
 260+ # If it's an interwiki link, expand it
 261+
 262+ $openid_url = $this->interwikiExpand($openid_url);
 263+
 264+ # Check if the URL is allowed
 265+
 266+ if (!$this->canLogin($openid_url)) {
 267+ $wgOut->showErrorPage('openidpermission', 'openidpermissiontext');
 268+ return;
 269+ }
 270+
 271+ $sk = $wgUser->getSkin();
 272+
 273+ if (isset($wgTrustRoot)) {
 274+ $trust_root = $wgTrustRoot;
 275+ } else {
 276+ global $wgArticlePath, $wgServer;
 277+ $root_article = str_replace('$1', '', $wgArticlePath);
 278+ $trust_root = $wgServer . $root_article;
 279+ }
 280+
 281+ $consumer = $this->getConsumer();
 282+
 283+ if (!$consumer) {
 284+ $wgOut->showErrorPage('openiderror', 'openiderrortext');
 285+ return;
 286+ }
 287+
 288+ # Make sure the user has a session!
 289+
 290+ global $wgSessionStarted;
 291+
 292+ if (!$wgSessionStarted) {
 293+ $wgUser->SetupSession();
 294+ }
 295+
 296+ $auth_request = $consumer->begin($openid_url);
 297+
 298+ // Handle failure status return values.
 299+ if (!$auth_request) {
 300+ $wgOut->showErrorPage('openiderror', 'openiderrortext');
 301+ return;
 302+ }
 303+
 304+ # Check the processed URLs, too
 305+
 306+ $endpoint = $auth_request->endpoint;
 307+
 308+ if (isset($endpoint)) {
 309+ # Check if the URL is allowed
 310+
 311+ if (isset($endpoint->identity_url) && !$this->canLogin($endpoint->identity_url)) {
 312+ $wgOut->showErrorPage('openidpermission', 'openidpermissiontext');
 313+ return;
 314+ }
 315+
 316+ if (isset($endpoint->delegate) && !$this->canLogin($endpoint->delegate)) {
 317+ $wgOut->showErrorPage('openidpermission', 'openidpermissiontext');
 318+ return;
 319+ }
 320+ }
 321+
 322+ $sreg_request = Auth_OpenID_SRegRequest::build(
 323+ // Required
 324+ array(),
 325+ // Optional
 326+ array('nickname','email',
 327+ 'fullname','language','timezone'));
 328+
 329+ if ($sreg_request) {
 330+ $auth_request->addExtension($sreg_request);
 331+ }
 332+
 333+ $process_url = $this->scriptUrl($finish_page);
 334+
 335+ if ($auth_request->shouldSendRedirect()) {
 336+ $redirect_url = $auth_request->redirectURL($trust_root,
 337+ $process_url);
 338+ if (Auth_OpenID::isFailure($redirect_url)) {
 339+ displayError("Could not redirect to server: " . $redirect_url->message);
 340+ } else {
 341+ # OK, now go
 342+ $wgOut->redirect($redirect_url);
 343+ }
 344+ } else {
 345+ // Generate form markup and render it.
 346+ $form_id = 'openid_message';
 347+ $form_html = $auth_request->formMarkup($trust_root, $process_url,
 348+ false, array('id' => $form_id));
 349+
 350+ // Display an error if the form markup couldn't be generated;
 351+ // otherwise, render the HTML.
 352+ if (Auth_OpenID::isFailure($form_html)) {
 353+ displayError("Could not redirect to server: " . $form_html->message);
 354+ } else {
 355+ $wgOut->addHtml("<p>" . wfMsg("openidautosubmit") . "</p>");
 356+ $wgOut->addHtml($form_html);
 357+ $wgOut->addInlineScript("function submitOpenIDForm() {\n document.getElementById(\"".$form_id."\").submit()\n }\nhookEvent(\"load\", submitOpenIDForm);\n");
 358+ }
 359+ }
 360+ }
 361+}
Index: tags/extensions/OpenID/REL_0_8_0/OpenID/OpenID.i18n.php
@@ -0,0 +1,103 @@
 2+<?php
 3+/**
 4+ * OpenID.i18n.php -- Interface messages for OpenID for MediaWiki
 5+ * Copyright 2006,2007 Internet Brands (http://www.internetbrands.com/)
 6+ * Copyright 2007,2008 Evan Prodromou <evan@prodromou.name>
 7+ *
 8+ * This program is free software; you can redistribute it and/or modify
 9+ * it under the terms of the GNU General Public License as published by
 10+ * the Free Software Foundation; either version 2 of the License, or
 11+ * (at your option) any later version.
 12+ *
 13+ * This program is distributed in the hope that it will be useful,
 14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 16+ * GNU General Public License for more details.
 17+ *
 18+ * You should have received a copy of the GNU General Public License
 19+ * along with this program; if not, write to the Free Software
 20+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 21+ *
 22+ * @author Evan Prodromou <evan@prodromou.name>
 23+ * @addtogroup Extensions
 24+ */
 25+
 26+if (!defined('MEDIAWIKI')) {
 27+ exit( 1 );
 28+}
 29+
 30+$OpenIDMessages =array(
 31+ 'en' => array('openidlogin' => 'Login with OpenID',
 32+ 'openidfinish' => 'Finish OpenID login',
 33+ 'openidserver' => 'OpenID server',
 34+ 'openidconvert' => 'OpenID converter',
 35+ 'openiderror' => 'Verification error',
 36+ 'openiderrortext' => 'An error occured during verification of the OpenID URL.',
 37+ 'openidconfigerror' => 'OpenID Configuration Error',
 38+ 'openidconfigerrortext' => 'The OpenID storage configuration for this wiki is invalid. Please consult this site\'s administrator.',
 39+ 'openidpermission' => 'OpenID permissions error',
 40+ 'openidpermissiontext' => 'The OpenID you provided is not allowed to login to this server.',
 41+ 'openidcancel' => 'Verification cancelled',
 42+ 'openidcanceltext' => 'Verification of the OpenID URL was cancelled.',
 43+ 'openidfailure' => 'Verification failed',
 44+ 'openidfailuretext' => 'Verification of the OpenID URL failed. Error message: "$1"',
 45+ 'openidsuccess' => 'Verification succeeded',
 46+ 'openidsuccesstext' => 'Verification of the OpenID URL succeeded.',
 47+ 'openidusernameprefix' => 'OpenIDUser',
 48+ 'openidserverlogininstructions' => 'Enter your password below to log in to $3 as user $2 (user page $1).',
 49+ 'openidtrustinstructions' => 'Check if you want to share data with $1.',
 50+ 'openidallowtrust' => 'Allow $1 to trust this user account.',
 51+ 'openidnopolicy' => 'Site has not specified a privacy policy.',
 52+ 'openidpolicy' => 'Check the <a target="_new" href="$1">privacy policy</a> for more information.',
 53+ 'openidoptional' => 'Optional',
 54+ 'openidrequired' => 'Required',
 55+ 'openidnickname' => 'Nickname',
 56+ 'openidfullname' => 'Fullname',
 57+ 'openidemail' => 'Email address',
 58+ 'openidlanguage' => 'Language',
 59+ 'openidnotavailable' => 'Your preferred nickname ($1) is already in use by another user.',
 60+ 'openidnotprovided' => 'Your OpenID server did not provide a nickname (either because it can\'t, or because you told it not to).',
 61+ 'openidchooseinstructions' => 'All users need a nickname; you can choose one from the options below.',
 62+ 'openidchoosefull' => 'Your full name ($1)',
 63+ 'openidchooseurl' => 'A name picked from your OpenID ($1)',
 64+ 'openidchooseauto' => 'An auto-generated name ($1)',
 65+ 'openidchoosemanual' => 'A name of your choice: ',
 66+ 'openidconvertinstructions' => 'This form lets you change your user account to use an OpenID URL.',
 67+ 'openidconvertsuccess' => 'Successfully converted to OpenID',
 68+ 'openidconvertsuccesstext' => 'You have successfully converted your OpenID to $1.',
 69+ 'openidconvertyourstext' => 'That is already your OpenID.',
 70+ 'openidconvertothertext' => 'That is someone else\'s OpenID.',
 71+ 'openidalreadyloggedin' => '<strong>User $1, you are already logged in!</strong>',
 72+ 'tog-hideopenid' => 'Hide your <a href="http://openid.net/">OpenID</a> on your user page, if you log in with OpenID.',
 73+ 'openidnousername' => 'No username specified.',
 74+ 'openidbadusername' => 'Bad username specified.',
 75+ 'openidautosubmit' => 'This page includes a form that should be automatically submitted if you have JavaScript enabled. If not, try the "Continue" button.',
 76+ 'openidloginlabel' => 'OpenID URL',
 77+ 'openidlogininstructions' =>
 78+ '{{SITENAME}} supports the [http://openid.net/ OpenID] standard for single signon between Web sites. ' .
 79+ 'OpenID lets you log into many different Web sites without using a different password for each. (See [http://en.wikipedia.org/wiki/OpenID Wikipedia\'s OpenID article] for more information.) ' .
 80+ "\n\n" .
 81+ 'If you already have an account on {{SITENAME}}, you can [[Special:Userlogin|log in]] with your username and password as usual. To use OpenID in the future, you can [[Special:OpenIDConvert|convert your account to OpenID]] after you\'ve logged in normally.' .
 82+ "\n\n" .
 83+ 'There are many [http://wiki.openid.net/Public_OpenID_providers Public OpenID providers], and you may already have an OpenID-enabled account on another service. ' .
 84+ "\n\n" .
 85+ '; Other wikis : If you have an account on an OpenID-enabled wiki, ' .
 86+ 'like [http://wikitravel.org/ Wikitravel], [http://www.wikihow.com/ wikiHow], ' .
 87+ '[http://vinismo.com/ Vinismo], [http://aboutus.org/ AboutUs] or [http://kei.ki/ Keiki], ' .
 88+ 'you can log in to {{SITENAME}} by entering the \'\'\'full URL\'\'\' of your user page on that other wiki in the box above. For example, \'\'<nowiki>http://kei.ki/en/User:Evan</nowiki>\'\'. ' .
 89+ "\n" .
 90+ '; [http://openid.yahoo.com/ Yahoo!] : If you have an account with Yahoo!, ' .
 91+ 'you can log in to this site by entering your Yahoo!-provided OpenID ' .
 92+ 'in the box above. Yahoo! OpenID URLs have the form ' .
 93+ '\'\'<nowiki>https://me.yahoo.com/yourusername</nowiki>\'\'. ' .
 94+ "\n" .
 95+ '; [http://dev.aol.com/aol-and-63-million-openids AOL] : If you have an account with [http://www.aol.com/ AOL], ' .
 96+ 'like an [http://www.aim.com/ AIM] account, you can log in to {{SITENAME}} by entering your AOL-provided OpenID ' .
 97+ 'in the box above. AOL OpenID URLs have the form ' .
 98+ '\'\'<nowiki>http://openid.aol.com/yourusername</nowiki>\'\'. Your username should be all lowercase, no spaces.' .
 99+ "\n" .
 100+ '; [http://bloggerindraft.blogspot.com/2008/01/new-feature-blogger-as-openid-provider.html Blogger], [http://faq.wordpress.com/2007/03/06/what-is-openid/ Wordpress.com], [http://www.livejournal.com/openid/about.bml LiveJournal], [http://bradfitz.vox.com/library/post/openid-for-vox.html Vox] : ' .
 101+ 'If you have a blog on any of these services, enter your blog URL in the box above. ' .
 102+ 'For example, \'\'<nowiki>http://yourusername.blogspot.com/</nowiki>\'\', \'\'<nowiki>http://yourusername.wordpress.com/</nowiki>\'\', \'\'<nowiki>http://yourusername.livejournal.com/</nowiki>\'\', or \'\'<nowiki>http://yourusername.vox.com/</nowiki>\'\'.'
 103+ )
 104+);
Index: tags/extensions/OpenID/REL_0_8_0/OpenID/SpecialOpenIDConvert.body.php
@@ -0,0 +1,168 @@
 2+<?php
 3+/**
 4+ * SpecialOpenIDConvert.body.php -- Convert existing account to OpenID account
 5+ * Copyright 2006,2007 Internet Brands (http://www.internetbrands.com/)
 6+ * Copyright 2007,2008 Evan Prodromou <evan@prodromou.name>
 7+ *
 8+ * This program is free software; you can redistribute it and/or modify
 9+ * it under the terms of the GNU General Public License as published by
 10+ * the Free Software Foundation; either version 2 of the License, or
 11+ * (at your option) any later version.
 12+ *
 13+ * This program is distributed in the hope that it will be useful,
 14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 16+ * GNU General Public License for more details.
 17+ *
 18+ * You should have received a copy of the GNU General Public License
 19+ * along with this program; if not, write to the Free Software
 20+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 21+ *
 22+ * @author Evan Prodromou <evan@prodromou.name>
 23+ * @addtogroup Extensions
 24+ */
 25+
 26+if (!defined('MEDIAWIKI')) {
 27+ exit( 1 );
 28+}
 29+
 30+class SpecialOpenIDConvert extends SpecialOpenID {
 31+
 32+ function SpecialOpenIDConvert() {
 33+ SpecialPage::SpecialPage("OpenIDConvert");
 34+ self::loadMessages();
 35+ }
 36+
 37+ function execute($par) {
 38+
 39+ global $wgRequest, $wgUser, $wgOut;
 40+
 41+ $this->setHeaders();
 42+
 43+ if ($wgUser->getID() == 0) {
 44+ $wgOut->showErrorPage('openiderror', 'notloggedin');
 45+ return;
 46+ }
 47+
 48+ switch ($par) {
 49+ case 'Finish':
 50+ $this->finish();
 51+ break;
 52+ default:
 53+ $openid_url = $wgRequest->getText('openid_url');
 54+ if (isset($openid_url) && strlen($openid_url) > 0) {
 55+ $this->convert($openid_url);
 56+ } else {
 57+ $this->Form();
 58+ }
 59+ }
 60+ }
 61+
 62+ function convert($openid_url) {
 63+
 64+ global $wgUser, $wgOut;
 65+
 66+ # Expand Interwiki
 67+
 68+ $openid_url = $this->interwikiExpand($openid_url);
 69+
 70+ # Is this ID allowed to log in?
 71+
 72+ if (!$this->CanLogin($openid_url)) {
 73+ $wgOut->showErrorPage('openidpermission', 'openidpermissiontext');
 74+ return;
 75+ }
 76+
 77+ # Is this ID already taken?
 78+
 79+ $other = $this->getUser($openid_url);
 80+
 81+ if (isset($other)) {
 82+ if ($other->getId() == $wgUser->getID()) {
 83+ $wgOut->showErrorPage('openiderror', 'openidconvertyourstext');
 84+ } else {
 85+ $wgOut->showErrorPage('openiderror', 'openidconvertothertext');
 86+ }
 87+ return;
 88+ }
 89+
 90+ # If we're OK to here, let the user go log in
 91+
 92+ $this->Login($openid_url, 'OpenIDConvert/Finish');
 93+ }
 94+
 95+ function form() {
 96+ global $wgOut, $wgUser;
 97+ $sk = $wgUser->getSkin();
 98+ $url = $this->GetUserUrl($wgUser);
 99+ if (is_null($url)) {
 100+ $url = '';
 101+ }
 102+
 103+ $ok = wfMsg('ok');
 104+ $instructions = wfMsg('openidconvertinstructions');
 105+ $wgOut->addHTML("<p>{$instructions}</p>" .
 106+ '<form action="' . $sk->makeSpecialUrl('OpenIDConvert') . '" method="POST">' .
 107+ '<input type="text" name="openid_url" size=30 ' .
 108+ ' style="background: url(http://www.openid.net/login-bg.gif) ' .
 109+ ' no-repeat; background-color: #fff; background-position: 0 50%; ' .
 110+ ' color: #000; padding-left: 18px;" value="' . $url . '" />' .
 111+ '<input type="submit" value="' . $ok . '" />' .
 112+ '</form>');
 113+ }
 114+
 115+ function finish() {
 116+
 117+ global $wgUser, $wgOut;
 118+
 119+ $consumer = $this->getConsumer();
 120+
 121+ $response = $consumer->complete($this->scriptUrl('OpenIDConvert/Finish'));
 122+
 123+ if (!isset($response)) {
 124+ $wgOut->showErrorPage('openiderror', 'openiderrortext');
 125+ return;
 126+ }
 127+
 128+ switch ($response->status) {
 129+ case Auth_OpenID_CANCEL:
 130+ // This means the authentication was cancelled.
 131+ $wgOut->showErrorPage('openidcancel', 'openidcanceltext');
 132+ break;
 133+ case Auth_OpenID_FAILURE:
 134+ wfDebug("OpenID: error in convert: '" . $response->message . "'\n");
 135+ $wgOut->showErrorPage('openidfailure', 'openidfailuretext', array($response->message));
 136+ break;
 137+ case Auth_OpenID_SUCCESS:
 138+ // This means the authentication succeeded.
 139+ $openid_url = $response->identity_url;
 140+
 141+ if (!isset($openid_url)) {
 142+ $wgOut->showErrorPage('openiderror', 'openiderrortext');
 143+ return;
 144+ }
 145+
 146+ # We check again for dupes; this may be normalized or
 147+ # reformatted by the server.
 148+
 149+ $other = $this->getUser($openid_url);
 150+
 151+ if (isset($other)) {
 152+ if ($other->getId() == $wgUser->getID()) {
 153+ $wgOut->showErrorPage('openiderror', 'openidconvertyourstext');
 154+ } else {
 155+ $wgOut->showErrorPage('openiderror', 'openidconvertothertext');
 156+ }
 157+ return;
 158+ }
 159+
 160+ $this->setUserUrl($wgUser, $openid_url);
 161+
 162+ $wgOut->setPageTitle( wfMsg( 'openidconvertsuccess' ) );
 163+ $wgOut->setRobotpolicy( 'noindex,nofollow' );
 164+ $wgOut->setArticleRelated( false );
 165+ $wgOut->addWikiText( wfMsg( 'openidconvertsuccesstext', $openid_url ) );
 166+ $wgOut->returnToMain( );
 167+ }
 168+ }
 169+}
Property changes on: tags/extensions/OpenID/REL_0_8_0/OpenID/SpecialOpenIDConvert.body.php
___________________________________________________________________
Added: svn:eol-style
1170 + native
Index: tags/extensions/OpenID/REL_0_8_0/OpenID/SpecialOpenIDFinish.body.php
@@ -0,0 +1,637 @@
 2+<?php
 3+/**
 4+ * SpecialOpenIDFinish.body.php -- Finish logging into an OpenID site
 5+ * Copyright 2006,2007 Internet Brands (http://www.internetbrands.com/)
 6+ * Copyright 2007,2008 Evan Prodromou <evan@prodromou.name>
 7+ *
 8+ * This program is free software; you can redistribute it and/or modify
 9+ * it under the terms of the GNU General Public License as published by
 10+ * the Free Software Foundation; either version 2 of the License, or
 11+ * (at your option) any later version.
 12+ *
 13+ * This program is distributed in the hope that it will be useful,
 14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 16+ * GNU General Public License for more details.
 17+ *
 18+ * You should have received a copy of the GNU General Public License
 19+ * along with this program; if not, write to the Free Software
 20+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 21+ *
 22+ * @author Evan Prodromou <evan@prodromou.name>
 23+ * @addtogroup Extensions
 24+ */
 25+
 26+if (!defined('MEDIAWIKI'))
 27+ exit(1);
 28+
 29+require_once("Auth/OpenID/Consumer.php");
 30+require_once("Auth/OpenID/SReg.php");
 31+require_once("Auth/Yadis/XRI.php");
 32+
 33+class SpecialOpenIDFinish extends SpecialOpenID {
 34+
 35+ function SpecialOpenIDFinish() {
 36+ SpecialPage::SpecialPage("OpenIDFinish", '', false);
 37+ self::loadMessages();
 38+ }
 39+
 40+ function execute($par) {
 41+
 42+ global $wgUser, $wgOut, $wgRequest;
 43+
 44+ $this->setHeaders();
 45+
 46+ # Shouldn't work if you're already logged in.
 47+
 48+ if ($wgUser->getID() != 0) {
 49+ $this->alreadyLoggedIn();
 50+ return;
 51+ }
 52+
 53+ $consumer = $this->getConsumer();
 54+
 55+ switch ($par) {
 56+ case 'ChooseName':
 57+ list($openid, $sreg) = $this->fetchValues();
 58+ if (!isset($openid)) {
 59+ wfDebug("OpenID: aborting in ChooseName because identity_url is missing\n");
 60+ $this->clearValues();
 61+ # No messing around, here
 62+ $wgOut->showErrorPage('openiderror', 'openiderrortext');
 63+ return;
 64+ }
 65+
 66+ if ($wgRequest->getCheck('wpCancel')) {
 67+ $this->clearValues();
 68+ $wgOut->showErrorPage('openidcancel', 'openidcanceltext');
 69+ return;
 70+ }
 71+
 72+ $choice = $wgRequest->getText('wpNameChoice');
 73+ $nameValue = $wgRequest->getText('wpNameValue');
 74+ wfDebug("OpenID: Got form values '$choice' and '$nameValue'\n");
 75+
 76+ $name = $this->getUserName($openid, $sreg, $choice, $nameValue);
 77+
 78+ if (!$name || !$this->userNameOK($name)) {
 79+ wfDebug("OpenID: Name not OK: '$name'\n");
 80+ $this->chooseNameForm($openid, $sreg);
 81+ return;
 82+ }
 83+
 84+ $user = $this->createUser($openid, $sreg, $name);
 85+
 86+ if (!isset($user)) {
 87+ $this->clearValues();
 88+ $wgOut->showErrorPage('openiderror', 'openiderrortext');
 89+ return;
 90+ }
 91+
 92+ $wgUser = $user;
 93+
 94+ $this->clearValues();
 95+
 96+ $this->finishLogin($response->identity_url);
 97+ break;
 98+
 99+ default: # No parameter, returning from a server
 100+
 101+ $response = $consumer->complete($this->scriptUrl('OpenIDFinish'));
 102+
 103+ if (!isset($response)) {
 104+ $wgOut->showErrorPage('openiderror', 'openiderrortext');
 105+ return;
 106+ }
 107+
 108+ switch ($response->status) {
 109+ case Auth_OpenID_CANCEL:
 110+ // This means the authentication was cancelled.
 111+ $wgOut->showErrorPage('openidcancel', 'openidcanceltext');
 112+ break;
 113+ case Auth_OpenID_FAILURE:
 114+ wfDebug("OpenID: error message '" . $response->message . "'\n");
 115+ $wgOut->showErrorPage('openidfailure', 'openidfailuretext',
 116+ array(($response->message) ? $response->message : ''));
 117+ break;
 118+ case Auth_OpenID_SUCCESS:
 119+ // This means the authentication succeeded.
 120+ $openid = $response->getDisplayIdentifier();
 121+ $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response);
 122+ $sreg = $sreg_resp->contents();
 123+
 124+ if (!isset($openid)) {
 125+ $wgOut->showErrorPage('openiderror', 'openiderrortext');
 126+ return;
 127+ }
 128+
 129+ $user = $this->getUser($openid);
 130+
 131+ if (isset($user)) {
 132+ $this->updateUser($user, $sreg); # update from server
 133+ } else {
 134+ # For easy names
 135+ $name = $this->createName($openid, $sreg);
 136+ if ($name) {
 137+ $user = $this->createUser($openid, $sreg, $name);
 138+ } else {
 139+ # For hard names
 140+ $this->saveValues($openid, $sreg);
 141+ $this->chooseNameForm($openid, $sreg);
 142+ return;
 143+ }
 144+ }
 145+
 146+ if (!isset($user)) {
 147+ $wgOut->showErrorPage('openiderror', 'openiderrortext');
 148+ } else {
 149+ $wgUser = $user;
 150+ $this->finishLogin($openid);
 151+ }
 152+ }
 153+ }
 154+ }
 155+
 156+ function finishLogin($openid) {
 157+
 158+ global $wgUser, $wgOut;
 159+
 160+ $wgUser->SetupSession();
 161+ $wgUser->SetCookies();
 162+
 163+ # Run any hooks; ignore results
 164+
 165+ wfRunHooks('UserLoginComplete', array(&$wgUser));
 166+
 167+ # Set a cookie for later check-immediate use
 168+
 169+ $this->loginSetCookie($openid);
 170+
 171+ $wgOut->setPageTitle( wfMsg( 'openidsuccess' ) );
 172+ $wgOut->setRobotpolicy( 'noindex,nofollow' );
 173+ $wgOut->setArticleRelated( false );
 174+ $wgOut->addWikiText( wfMsg( 'openidsuccess', $wgUser->getName(), $openid ) );
 175+ $wgOut->returnToMain(false, $this->returnTo());
 176+ }
 177+
 178+ function loginSetCookie($openid) {
 179+ global $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookiePrefix;
 180+ global $wgOpenIDCookieExpiration;
 181+
 182+ $exp = time() + $wgOpenIDCookieExpiration;
 183+
 184+ setcookie($wgCookiePrefix.'OpenID', $openid, $exp, $wgCookiePath, $wgCookieDomain, $wgCookieSecure);
 185+ }
 186+
 187+ function chooseNameForm($openid, $sreg) {
 188+
 189+ global $wgOut, $wgUser;
 190+ $sk = $wgUser->getSkin();
 191+ if (array_key_exists('nickname', $sreg)) {
 192+ $message = wfMsg('openidnotavailable', $sreg['nickname']);
 193+ } else {
 194+ $message = wfMsg('openidnotprovided');
 195+ }
 196+ $instructions = wfMsg('openidchooseinstructions');
 197+ $wgOut->addHTML("<p>{$message}</p>" .
 198+ "<p>{$instructions}</p>" .
 199+ '<form action="' . $sk->makeSpecialUrl('OpenIDFinish/ChooseName') . '" method="POST">');
 200+ $def = false;
 201+
 202+ # These options won't exist if we can't get them.
 203+
 204+ if (array_key_exists('fullname', $sreg) && $this->userNameOK($sreg['fullname'])) {
 205+ $wgOut->addHTML("<input type='radio' name='wpNameChoice' id='wpNameChoiceFull' value='full' " .
 206+ ((!$def) ? "checked = 'checked'" : "") . " />" .
 207+ "<label for='wpNameChoiceFull'>" . wfMsg("openidchoosefull", $sreg['fullname']) . "</label><br />");
 208+ $def = true;
 209+ }
 210+
 211+ $idname = $this->toUserName($openid);
 212+
 213+ if ($idname && $this->userNameOK($idname)) {
 214+ $wgOut->addHTML("<input type='radio' name='wpNameChoice' id='wpNameChoiceUrl' value='url' " .
 215+ ((!$def) ? "checked = 'checked'" : "") . " />" .
 216+ "<label for='wpNameChoiceUrl'>" . wfMsg("openidchooseurl", $idname) . "</label><br />");
 217+ $def = true;
 218+ }
 219+
 220+ # These are always available
 221+
 222+ $wgOut->addHTML("<input type='radio' name='wpNameChoice' id='wpNameChoiceAuto' value='auto' " .
 223+ ((!$def) ? "checked = 'checked'" : "") . " />" .
 224+ "<label for='wpNameChoiceAuto'>" . wfMsg("openidchooseauto", $this->automaticName($sreg)) . "</label><br />");
 225+
 226+ $def = true;
 227+
 228+ $wgOut->addHTML("<input type='radio' name='wpNameChoice' id='wpNameChoiceManual' value='manual' " .
 229+ " checked='off' />" .
 230+ "<label for='wpNameChoiceManual'>" . wfMsg("openidchoosemanual") . "</label> " .
 231+ "<input type='text' name='wpNameValue' id='wpNameChoice' size='30' /><br />");
 232+
 233+ $ok = wfMsg('login');
 234+ $cancel = wfMsg('cancel');
 235+
 236+ $wgOut->addHTML("<input type='submit' name='wpOK' value='{$ok}' /> <input type='submit' name='wpCancel' value='{$cancel}' />");
 237+ $wgOut->addHTML("</form>");
 238+ }
 239+
 240+ function canLogin($openid_url) {
 241+
 242+ global $wgOpenIDConsumerDenyByDefault, $wgOpenIDConsumerAllow, $wgOpenIDConsumerDeny;
 243+
 244+ if ($this->isLocalUrl($openid_url)) {
 245+ return false;
 246+ }
 247+
 248+ if ($wgOpenIDConsumerDenyByDefault) {
 249+ $canLogin = false;
 250+ foreach ($wgOpenIDConsumerAllow as $allow) {
 251+ if (preg_match($allow, $openid_url)) {
 252+ wfDebug("OpenID: $openid_url matched allow pattern $allow.\n");
 253+ $canLogin = true;
 254+ foreach ($wgOpenIDConsumerDeny as $deny) {
 255+ if (preg_match($deny, $openid_url)) {
 256+ wfDebug("OpenID: $openid_url matched deny pattern $deny.\n");
 257+ $canLogin = false;
 258+ break;
 259+ }
 260+ }
 261+ break;
 262+ }
 263+ }
 264+ } else {
 265+ $canLogin = true;
 266+ foreach ($wgOpenIDConsumerDeny as $deny) {
 267+ if (preg_match($deny, $openid_url)) {
 268+ wfDebug("OpenID: $openid_url matched deny pattern $deny.\n");
 269+ $canLogin = false;
 270+ foreach ($wgOpenIDConsumerAllow as $allow) {
 271+ if (preg_match($allow, $openid_url)) {
 272+ wfDebug("OpenID: $openid_url matched allow pattern $allow.\n");
 273+ $canLogin = true;
 274+ break;
 275+ }
 276+ }
 277+ break;
 278+ }
 279+ }
 280+ }
 281+ return $canLogin;
 282+ }
 283+
 284+ function isLocalUrl($url) {
 285+
 286+ global $wgServer, $wgArticlePath;
 287+
 288+ $pattern = $wgServer . $wgArticlePath;
 289+ $pattern = str_replace('$1', '(.*)', $pattern);
 290+ $pattern = str_replace('?', '\?', $pattern);
 291+
 292+ return preg_match('|^' . $pattern . '$|', $url);
 293+ }
 294+
 295+ # Find the user with the given openid, if any
 296+
 297+ function getUser($openid) {
 298+ global $wgSharedDB, $wgDBprefix;
 299+
 300+ if (isset($wgSharedDB)) {
 301+ $tableName = "`$wgSharedDB`.${wgDBprefix}user_openid";
 302+ } else {
 303+ $tableName = 'user_openid';
 304+ }
 305+
 306+ $dbr =& wfGetDB( DB_SLAVE );
 307+ $id = $dbr->selectField($tableName, 'uoi_user',
 308+ array('uoi_openid' => $openid));
 309+ if ($id) {
 310+ $name = User::whoIs($id);
 311+ return User::newFromName($name);
 312+ } else {
 313+ return NULL;
 314+ }
 315+ }
 316+
 317+ function updateUser($user, $sreg) {
 318+ global $wgAllowRealName;
 319+
 320+ # FIXME: only update if there's been a change
 321+
 322+ if (array_key_exists('nickname', $sreg)) {
 323+ $user->setOption('nickname', $sreg['nickname']);
 324+ } else {
 325+ $user->setOption('nickname', '');
 326+ }
 327+
 328+ if (array_key_exists('email', $sreg)) {
 329+ $user->setEmail( $sreg['email'] );
 330+ } else {
 331+ $user->setEmail(NULL);
 332+ }
 333+
 334+ if (array_key_exists('fullname', $sreg) && $wgAllowRealName) {
 335+ $user->setRealName($sreg['fullname']);
 336+ } else {
 337+ $user->setRealName(NULL);
 338+ }
 339+
 340+ if (array_key_exists('language', $sreg)) {
 341+ # FIXME: check and make sure the language exists
 342+ $user->setOption('language', $sreg['language']);
 343+ } else {
 344+ $user->setOption('language', NULL);
 345+ }
 346+
 347+ if (array_key_exists('timezone', $sreg)) {
 348+ # FIXME: do something with it.
 349+ # $offset = OpenIDTimezoneToTzoffset($sreg['timezone']);
 350+ # $user->setOption('timecorrection', $offset);
 351+ } else {
 352+ # $user->setOption('timecorrection', NULL);
 353+ }
 354+
 355+ $user->saveSettings();
 356+ }
 357+
 358+ function createUser($openid, $sreg, $name) {
 359+
 360+ global $wgAuth, $wgAllowRealName;
 361+
 362+ $user = User::newFromName($name);
 363+
 364+ $user->addToDatabase();
 365+
 366+ if (!$user->getId()) {
 367+ wfDebug("OpenID: Error adding new user.\n");
 368+ } else {
 369+
 370+ $this->insertUserUrl($user, $openid);
 371+
 372+ if (array_key_exists('nickname', $sreg)) {
 373+ $user->setOption('nickname', $sreg['nickname']);
 374+ }
 375+ if (array_key_exists('email', $sreg)) {
 376+ $user->setEmail( $sreg['email'] );
 377+ }
 378+ if ($wgAllowRealName && array_key_exists('fullname', $sreg)) {
 379+ $user->setRealName($sreg['fullname']);
 380+ }
 381+ if (array_key_exists('language', $sreg)) {
 382+ # FIXME: check and make sure the language exists
 383+ $user->setOption('language', $sreg['language']);
 384+ }
 385+ if (array_key_exists('timezone', $sreg)) {
 386+ # FIXME: do something with it.
 387+ # $offset = OpenIDTimezoneToTzoffset($sreg['timezone']);
 388+ # $user->setOption('timecorrection', $offset);
 389+ }
 390+ $user->saveSettings();
 391+ return $user;
 392+ }
 393+ }
 394+
 395+ function createName($openid, $sreg) {
 396+
 397+ if (array_key_exists('nickname', $sreg) && # try nickname
 398+ $this->userNameOK($sreg['nickname']))
 399+ {
 400+ return $sreg['nickname'];
 401+ }
 402+ }
 403+
 404+ function toUserName($openid) {
 405+ if (Auth_Yadis_identifierScheme($openid) == 'XRI') {
 406+ return $this->toUserNameXri($openid);
 407+ } else {
 408+ return $this->toUserNameUrl($openid);
 409+ }
 410+ }
 411+
 412+ # We try to use an OpenID URL as a legal MediaWiki user name in this order
 413+ # 1. Plain hostname, like http://evanp.myopenid.com/
 414+ # 2. One element in path, like http://profile.typekey.com/EvanProdromou/
 415+ # or http://getopenid.com/evanprodromou
 416+
 417+ function toUserNameUrl($openid) {
 418+ static $bad = array('query', 'user', 'password', 'port', 'fragment');
 419+
 420+ $parts = parse_url($openid);
 421+
 422+ # If any of these parts exist, this won't work
 423+
 424+ foreach ($bad as $badpart) {
 425+ if (array_key_exists($badpart, $parts)) {
 426+ return NULL;
 427+ }
 428+ }
 429+
 430+ # We just have host and/or path
 431+
 432+ # If it's just a host...
 433+ if (array_key_exists('host', $parts) &&
 434+ (!array_key_exists('path', $parts) || strcmp($parts['path'], '/') == 0))
 435+ {
 436+ $hostparts = explode('.', $parts['host']);
 437+
 438+ # Try to catch common idiom of nickname.service.tld
 439+
 440+ if ((count($hostparts) > 2) &&
 441+ (strlen($hostparts[count($hostparts) - 2]) > 3) && # try to skip .co.uk, .com.au
 442+ (strcmp($hostparts[0], 'www') != 0))
 443+ {
 444+ return $hostparts[0];
 445+ } else {
 446+ # Do the whole hostname
 447+ return $parts['host'];
 448+ }
 449+ } else {
 450+ if (array_key_exists('path', $parts)) {
 451+ # Strip starting, ending slashes
 452+ $path = preg_replace('@/$@', '', $parts['path']);
 453+ $path = preg_replace('@^/@', '', $path);
 454+ if (strpos($path, '/') === false) {
 455+ return $path;
 456+ }
 457+ }
 458+ }
 459+
 460+ return NULL;
 461+ }
 462+
 463+ function toUserNameXri($xri) {
 464+ $base = $this->xriBase($xri);
 465+
 466+ if (!$base) {
 467+ return NULL;
 468+ } else {
 469+ # =evan.prodromou
 470+ # or @gratis*evan.prodromou
 471+ $parts = explode('*', substr($base, 1));
 472+ return array_pop($parts);
 473+ }
 474+ }
 475+
 476+ # Is this name OK to use as a user name?
 477+
 478+ function userNameOK($name) {
 479+ global $wgReservedUsernames;
 480+ return (0 == User::idFromName($name) &&
 481+ !in_array( $name, $wgReservedUsernames ));
 482+ }
 483+
 484+ # Get an auto-incremented name
 485+
 486+ function firstAvailable($prefix) {
 487+ for ($i = 2; ; $i++) { # FIXME: this is the DUMB WAY to do this
 488+ $name = "$prefix$i";
 489+ if ($this->userNameOK($name)) {
 490+ return $name;
 491+ }
 492+ }
 493+ }
 494+
 495+ function alreadyLoggedIn() {
 496+
 497+ global $wgUser, $wgOut;
 498+
 499+ $wgOut->setPageTitle( wfMsg( 'openiderror' ) );
 500+ $wgOut->setRobotpolicy( 'noindex,nofollow' );
 501+ $wgOut->setArticleRelated( false );
 502+ $wgOut->addWikiText( wfMsg( 'openidalreadyloggedin', $wgUser->getName() ) );
 503+ $wgOut->returnToMain(false, $this->returnTo() );
 504+ }
 505+
 506+ function getUserUrl($user) {
 507+ $openid_url = null;
 508+
 509+ if (isset($user) && $user->getId() != 0) {
 510+ global $wgSharedDB, $wgDBprefix;
 511+ if (isset($wgSharedDB)) {
 512+ $tableName = "`${wgSharedDB}`.${wgDBprefix}user_openid";
 513+ } else {
 514+ $tableName = 'user_openid';
 515+ }
 516+
 517+ $dbr =& wfGetDB( DB_SLAVE );
 518+ $res = $dbr->select(array($tableName),
 519+ array('uoi_openid'),
 520+ array('uoi_user' => $user->getId()),
 521+ 'SpecialOpenIDFinish::getUserUrl');
 522+
 523+ # This should return 0 or 1 result, since user is unique
 524+ # in the table.
 525+
 526+ while ($res && $row = $dbr->fetchObject($res)) {
 527+ $openid_url = $row->uoi_openid;
 528+ }
 529+ $dbr->freeResult($res);
 530+ }
 531+ return $openid_url;
 532+ }
 533+
 534+ function setUserUrl($user, $url) {
 535+ $other = $this->getUserUrl($user);
 536+ if (isset($other)) {
 537+ $this->updateUserUrl($user, $url);
 538+ } else {
 539+ $this->insertUserUrl($user, $url);
 540+ }
 541+ }
 542+
 543+ function insertUserUrl($user, $url) {
 544+ global $wgSharedDB, $wgDBname;
 545+ $dbw =& wfGetDB( DB_MASTER );
 546+
 547+ if (isset($wgSharedDB)) {
 548+ # It would be nicer to get the existing dbname
 549+ # and save it, but it's not possible
 550+ $dbw->selectDB($wgSharedDB);
 551+ }
 552+
 553+ $dbw->insert('user_openid', array('uoi_user' => $user->getId(),
 554+ 'uoi_openid' => $url));
 555+
 556+ if (isset($wgSharedDB)) {
 557+ $dbw->selectDB($wgDBname);
 558+ }
 559+ }
 560+
 561+ function updateUserUrl($user, $url) {
 562+ global $wgSharedDB, $wgDBname;
 563+ $dbw =& wfGetDB( DB_MASTER );
 564+
 565+ if (isset($wgSharedDB)) {
 566+ # It would be nicer to get the existing dbname
 567+ # and save it, but it's not possible
 568+ $dbw->selectDB($wgSharedDB);
 569+ }
 570+
 571+ $dbw->set('user_openid', 'uoi_openid', $url,
 572+ 'uoi_user = ' . $user->getID());
 573+
 574+ if (isset($wgSharedDB)) {
 575+ $dbw->selectDB($wgDBname);
 576+ }
 577+ }
 578+
 579+ function saveValues($openid, $sreg) {
 580+ global $wgSessionStarted, $wgUser;
 581+
 582+ if (!$wgSessionStarted) {
 583+ $wgUser->SetupSession();
 584+ }
 585+
 586+ $_SESSION['openid_consumer_identity'] = $openid;
 587+ $_SESSION['openid_consumer_sreg'] = $sreg;
 588+
 589+ return true;
 590+ }
 591+
 592+ function clearValues() {
 593+ unset($_SESSION['openid_consumer_identity']);
 594+ unset($_SESSION['openid_consumer_sreg']);
 595+ return true;
 596+ }
 597+
 598+ function fetchValues() {
 599+ return array($_SESSION['openid_consumer_identity'], $_SESSION['openid_consumer_sreg']);
 600+ }
 601+
 602+ function returnTo() {
 603+ return $_SESSION['openid_consumer_returnto'];
 604+ }
 605+
 606+ function setReturnTo($returnto) {
 607+ $_SESSION['openid_consumer_returnto'] = $returnto;
 608+ }
 609+
 610+ function getUserName($openid, $sreg, $choice, $nameValue) {
 611+ switch ($choice) {
 612+ case 'full':
 613+ return ((array_key_exists('fullname', $sreg)) ? $sreg['fullname'] : null);
 614+ break;
 615+ case 'url':
 616+ return $this->toUserName($openid);
 617+ break;
 618+ case 'auto':
 619+ return $this->automaticName($sreg);
 620+ break;
 621+ case 'manual':
 622+ return $nameValue;
 623+ default:
 624+ return null;
 625+ }
 626+ }
 627+
 628+ function automaticName($sreg) {
 629+ if (array_key_exists('nickname', $sreg) && # try auto-generated from nickname
 630+ strlen($sreg['nickname']) > 0) {
 631+ return $this->firstAvailable($sreg['nickname']);
 632+ } else { # try auto-generated
 633+ return $this->firstAvailable(wfMsg('openidusernameprefix'));
 634+ }
 635+ }
 636+}
 637+
 638+?>
Index: tags/extensions/OpenID/REL_0_8_0/OpenID/SpecialOpenIDXRDS.body.php
@@ -0,0 +1,105 @@
 2+<?php
 3+/**
 4+ * SpecialOpenIDXRDS.body.php -- Server side of OpenID site
 5+ * Copyright 2006,2007 Internet Brands (http://www.internetbrands.com/)
 6+ * Copyright 2007,2008 Evan Prodromou <evan@prodromou.name>
 7+ *
 8+ * This program is free software; you can redistribute it and/or modify
 9+ * it under the terms of the GNU General Public License as published by
 10+ * the Free Software Foundation; either version 2 of the License, or
 11+ * (at your option) any later version.
 12+ *
 13+ * This program is distributed in the hope that it will be useful,
 14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 16+ * GNU General Public License for more details.
 17+ *
 18+ * You should have received a copy of the GNU General Public License
 19+ * along with this program; if not, write to the Free Software
 20+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 21+ *
 22+ * @author Evan Prodromou <evan@prodromou.name>
 23+ * @addtogroup Extensions
 24+ */
 25+
 26+if (!defined('MEDIAWIKI'))
 27+ exit(1);
 28+
 29+# Outputs a Yadis (http://yadis.org/) XRDS file, saying that this server
 30+# supports OpenID and lots of other jazz.
 31+
 32+class SpecialOpenIDXRDS extends SpecialOpenID {
 33+
 34+ function SpecialOpenIDXRDS() {
 35+ SpecialPage::SpecialPage("OpenIDXRDS", '', false);
 36+ self::loadMessages();
 37+ }
 38+
 39+ # $par is a user name
 40+
 41+ function execute($par) {
 42+ global $wgOut;
 43+
 44+ // XRDS preamble XML.
 45+ $xml_template = array('<?xml version="1.0" encoding="UTF-8"?>',
 46+ '<xrds:XRDS',
 47+ ' xmlns:xrds="xri://\$xrds"',
 48+ ' xmlns:openid="http://openid.net/xmlns/1.0"',
 49+ ' xmlns="xri://$xrd*($v*2.0)">',
 50+ '<XRD>');
 51+
 52+ # Check to see if the parameter is really a user name
 53+
 54+ if (!$par) {
 55+ wfHttpError(404, "Not Found", wfMsg('openidnousername'));
 56+ }
 57+
 58+ $user = User::newFromName($par);
 59+
 60+ if (!$user || $user->getID() == 0) {
 61+ wfHttpError(404, "Not Found", wfMsg('openidbadusername'));
 62+ }
 63+
 64+ // Generate the user page URL.
 65+
 66+ $user_title = Title::makeTitle(NS_USER, $user->getName());
 67+ $user_url = $user_title->getFullURL();
 68+
 69+ // Generate the OpenID server endpoint URL.
 70+ $server_title = Title::makeTitle(NS_SPECIAL, 'OpenIDServer');
 71+ $server_url = $server_title->getFullURL();
 72+
 73+ // Define array of Yadis services to be included in
 74+ // the XRDS output.
 75+ $services = array(
 76+ array('uri' => $server_url,
 77+ 'priority' => '0',
 78+ 'types' => array('http://openid.net/signon/1.0',
 79+ 'http://openid.net/sreg/1.0',
 80+ 'http://specs.openid.net/auth/2.0/signon'),
 81+ 'delegate' => $user_url),
 82+ );
 83+
 84+ // Generate <Service> elements into $service_text.
 85+ $service_text = "\n";
 86+ foreach ($services as $service) {
 87+ $types = array();
 88+ foreach ($service['types'] as $type_uri) {
 89+ $types[] = ' <Type>'.$type_uri.'</Type>';
 90+ }
 91+ $service_text .= implode("\n",
 92+ array(' <Service priority="'.$service['priority'].'">',
 93+ ' <URI>'.$server_url.'</URI>',
 94+ implode("\n", $types),
 95+ ' </Service>'));
 96+ }
 97+
 98+ $wgOut->disable();
 99+
 100+ // Print content-type and XRDS XML.
 101+ header("Content-Type", "application/xrds+xml");
 102+ print implode("\n", $xml_template);
 103+ print $service_text;
 104+ print implode("\n", array("</XRD>", "</xrds:XRDS>"));
 105+ }
 106+}
\ No newline at end of file
Index: tags/extensions/OpenID/REL_0_8_0/OpenID/OpenID.setup.php
@@ -0,0 +1,241 @@
 2+<?php
 3+/**
 4+ * OpenID.setup.php -- Make MediaWiki an OpenID consumer and server
 5+ * Copyright 2006,2007 Internet Brands (http://www.internetbrands.com/)
 6+ * Copyright 2007,2008 Evan Prodromou <evan@prodromou.name>
 7+ *
 8+ * This program is free software; you can redistribute it and/or modify
 9+ * it under the terms of the GNU General Public License as published by
 10+ * the Free Software Foundation; either version 2 of the License, or
 11+ * (at your option) any later version.
 12+ *
 13+ * This program is distributed in the hope that it will be useful,
 14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 16+ * GNU General Public License for more details.
 17+ *
 18+ * You should have received a copy of the GNU General Public License
 19+ * along with this program; if not, write to the Free Software
 20+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 21+ *
 22+ * @author Evan Prodromou <evan@prodromou.name>
 23+ * @addtogroup Extensions
 24+ */
 25+
 26+if (!defined('MEDIAWIKI')) {
 27+ exit( 1 );
 28+}
 29+
 30+define('MEDIAWIKI_OPENID_VERSION', '0.8.0');
 31+
 32+# CONFIGURATION VARIABLES
 33+
 34+# Whether to hide the "Login with OpenID link" link; set to true if you already have this link in your skin.
 35+
 36+$wgHideOpenIDLoginLink = false;
 37+
 38+# Location of the OpenID login logo. You can copy this to your server if you want.
 39+
 40+$wgOpenIDLoginLogoUrl = 'http://www.openid.net/login-bg.gif';
 41+
 42+# Whether to show the OpenID identity URL on a user's home page. Possible values are 'always', 'never', or 'user'
 43+# 'user' lets the user decide.
 44+
 45+$wgOpenIDShowUrlOnUserPage = 'user';
 46+
 47+# These are trust roots that we don't bother asking users
 48+# whether the trust root is allowed to trust; typically
 49+# for closely-linked partner sites.
 50+
 51+$wgOpenIDServerForceAllowTrust = array();
 52+
 53+# Where to store transitory data. Only supported type is 'file'.
 54+
 55+$wgOpenIDServerStoreType = 'file';
 56+
 57+# If the store type is set to 'file', this is is the name of a
 58+# directory to store the data in.
 59+
 60+$wgOpenIDServerStorePath = "/tmp/$wgDBname/openidserver/";
 61+
 62+# Defines the trust root for this server
 63+# If null, we make a guess
 64+
 65+$wgTrustRoot = null;
 66+
 67+# When using deny and allow arrays, defines how the security works.
 68+# If true, works like "Order Allow,Deny" in Apache; deny by default,
 69+# allow items that match allow that don't match deny to pass.
 70+# If false, works like "Order Deny,Allow" in Apache; allow by default,
 71+# deny items in deny that aren't in allow.
 72+
 73+$wgOpenIDConsumerDenyByDefault = false;
 74+
 75+# Which partners to allow; regexps here. See above.
 76+
 77+$wgOpenIDConsumerAllow = array();
 78+
 79+# Which partners to deny; regexps here. See above.
 80+
 81+$wgOpenIDConsumerDeny = array();
 82+
 83+# Where to store transitory data. Only supported type is 'file'.
 84+
 85+$wgOpenIDConsumerStoreType = 'file';
 86+
 87+# If the store type is set to 'file', this is is the name of a
 88+# directory to store the data in.
 89+
 90+$wgOpenIDConsumerStorePath = "/tmp/$wgDBname/openidconsumer/";
 91+
 92+# Expiration time for the OpenID cookie. Lets the user re-authenticate
 93+# automatically if their session is expired. Only really useful if
 94+# it's much greater than $wgCookieExpiration. Default: about one year.
 95+
 96+$wgOpenIDCookieExpiration = 365 * 24 * 60 * 60;
 97+
 98+# END CONFIGURATION VARIABLES
 99+
 100+$wgExtensionFunctions[] = 'setupOpenID';
 101+
 102+$wgExtensionCredits['other'][] = array('name' => 'OpenID',
 103+ 'version' => MEDIAWIKI_OPENID_VERSION,
 104+ 'author' => 'Evan Prodromou',
 105+ 'url' => 'http://www.mediawiki.org/wiki/Extension:OpenID',
 106+ 'description' => 'lets users login to the wiki with an ' .
 107+ ' [http://openid.net/ OpenID] ' .
 108+ 'and login to other OpenID-aware Web sites ' .
 109+ 'with their wiki user account');
 110+
 111+function OpenIDGetServerPath() {
 112+ $rel = 'Auth/OpenID/Server.php';
 113+
 114+ foreach (explode(PATH_SEPARATOR, get_include_path()) as $pe) {
 115+ $full = $pe . DIRECTORY_SEPARATOR . $rel;
 116+ if (file_exists($full)) {
 117+ return $full;
 118+ }
 119+ }
 120+ return $rel;
 121+}
 122+
 123+# Gets stored in the session, needs to be reified before our setup
 124+$wgAutoloadClasses['Auth_OpenID_CheckIDRequest'] = OpenIDGetServerPath();
 125+
 126+function setupOpenID() {
 127+ global $wgHooks, $wgAutoloadClasses, $wgSpecialPages, $wgExtensionCredits;
 128+
 129+ # Autoload for special pages
 130+
 131+ foreach (array('Login', 'Finish', 'Convert', 'Server', 'XRDS') as $sub) {
 132+ $wgAutoloadClasses['SpecialOpenID' . $sub] = dirname(__FILE__) . '/SpecialOpenID' . $sub . '.body.php';
 133+ $wgSpecialPages['OpenID'.$sub] = array('SpecialOpenID'.$sub);
 134+ }
 135+
 136+ # Autoload common parent with utility methods
 137+
 138+ $wgAutoloadClasses['SpecialOpenID'] = dirname(__FILE__) . '/SpecialOpenID.body.php';
 139+
 140+ $wgHooks['PersonalUrls'][] = 'OpenIDPersonalUrls';
 141+ $wgHooks['UserToggles'][] = 'OpenIDUserToggles';
 142+ $wgHooks['ArticleViewHeader'][] = 'OpenIDArticleViewHeader';
 143+ # Load the i18n messages
 144+ $wgHooks['LoadAllMessages'][] = 'SpecialOpenID::loadMessages';
 145+ # Add any aliases for the special page.
 146+ $wgHooks['LanguageGetSpecialPageAliases'][] = 'SpecialOpenID::LocalizedPageName';
 147+ # Typo in versions of MW earlier than 1.11.x (?)
 148+ $wgHooks['LangugeGetSpecialPageAliases'][] = 'SpecialOpenID::LocalizedPageName'; # Add any aliases for the special page.
 149+}
 150+
 151+# Hook is called whenever an article is being viewed
 152+
 153+function OpenIDArticleViewHeader(&$article, &$outputDone, &$pcache ) {
 154+ global $wgOut;
 155+
 156+ $nt = $article->getTitle();
 157+
 158+ // If the page being viewed is a user page,
 159+ // generate the openid.server META tag and output
 160+ // the X-XRDS-Location. See the OpenIDXRDS
 161+ // special page for the XRDS output / generation
 162+ // logic.
 163+
 164+ if ($nt &&
 165+ ($nt->getNamespace() == NS_USER) &&
 166+ strpos($nt->getText(), '/') === false)
 167+ {
 168+ $user = User::newFromName($nt->getText());
 169+ if ($user && $user->getID() != 0) {
 170+ $openid = SpecialOpenID::getUserUrl($user);
 171+ if (isset($openid) && strlen($openid) != 0) {
 172+ global $wgOpenIDShowUrlOnUserPage;
 173+
 174+ if ($wgOpenIDShowUrlOnUserPage == 'always' ||
 175+ ($wgOpenIDShowUrlOnUserPage == 'user' && !$user->getOption('hideopenid')))
 176+ {
 177+ global $wgOpenIDLoginLogoUrl;
 178+
 179+ $url = SpecialOpenID::OpenIDToUrl($openid);
 180+ $disp = htmlspecialchars($openid);
 181+ $wgOut->setSubtitle("<span class='subpages'>" .
 182+ "<img src='$wgOpenIDLoginLogoUrl' alt='OpenID' />" .
 183+ "<a href='$url'>$disp</a>" .
 184+ "</span>");
 185+ }
 186+ } else {
 187+ $st = Title::makeTitleSafe(NS_SPECIAL, 'OpenIDServer');
 188+ $wgOut->addLink(array('rel' => 'openid.server',
 189+ 'href' => $st->getFullURL()));
 190+ $wgOut->addLink(array('rel' => 'openid2.provider',
 191+ 'href' => $st->getFullURL()));
 192+ $rt = Title::makeTitle(NS_SPECIAL, 'OpenIDXRDS/'.$user->getName());
 193+ $wgOut->addMeta('http:X-XRDS-Location', $rt->getFullURL());
 194+ header('X-XRDS-Location', $rt->getFullURL());
 195+ }
 196+ }
 197+ }
 198+
 199+ return TRUE;
 200+}
 201+
 202+function OpenIDPersonalUrls(&$personal_urls, &$title) {
 203+ global $wgHideOpenIDLoginLink, $wgUser, $wgLang, $wgOut;
 204+
 205+ if (!$wgHideOpenIDLoginLink && $wgUser->getID() == 0) {
 206+ $wgOut->addHeadItem('openidloginstyle', OpenIDLoginStyle());
 207+ $sk = $wgUser->getSkin();
 208+ $returnto = ($title->getPrefixedUrl() == $wgLang->specialPage( 'Userlogout' )) ?
 209+ '' : ('returnto=' . $title->getPrefixedURL());
 210+
 211+ $personal_urls['openidlogin'] = array(
 212+ 'text' => wfMsg('openidlogin'),
 213+ 'href' => $sk->makeSpecialUrl( 'OpenIDLogin', $returnto ),
 214+ 'active' => $title->isSpecial( 'OpenIDLogin' )
 215+ );
 216+ }
 217+
 218+ return true;
 219+}
 220+
 221+function OpenIDUserToggles(&$extraToggles) {
 222+ global $wgOpenIDShowUrlOnUserPage;
 223+
 224+ if ($wgOpenIDShowUrlOnUserPage == 'user') {
 225+ $extraToggles[] = 'hideopenid';
 226+ }
 227+
 228+ return true;
 229+}
 230+
 231+function OpenIDLoginStyle() {
 232+ global $wgOpenIDLoginLogoUrl;
 233+ return <<<EOS
 234+<style type='text/css'>
 235+li#pt-openidlogin {
 236+ background: url($wgOpenIDLoginLogoUrl) top left no-repeat;
 237+ padding-left: 20px;
 238+ text-transform: none;
 239+}
 240+</style>
 241+EOS;
 242+}
\ No newline at end of file
Property changes on: tags/extensions/OpenID/REL_0_8_0/OpenID/OpenID.setup.php
___________________________________________________________________
Added: svn:eol-style
1243 + native
Index: tags/extensions/OpenID/REL_0_8_0/OpenID/openid_table.sql
@@ -0,0 +1,8 @@
 2+CREATE TABLE /*$wgDBprefix*/user_openid (
 3+ uoi_openid varchar(255) NOT NULL,
 4+ uoi_user int(5) unsigned NOT NULL,
 5+
 6+ PRIMARY KEY uoi_openid (uoi_openid),
 7+ UNIQUE INDEX uoi_user (uoi_user)
 8+) TYPE=InnoDB;
 9+
Property changes on: tags/extensions/OpenID/REL_0_8_0/OpenID/openid_table.sql
___________________________________________________________________
Added: svn:eol-style
110 + native
Index: tags/extensions/OpenID/REL_0_8_0/OpenID/OpenID.php
@@ -0,0 +1,3 @@
 2+<?php
 3+# backwards compatibility; new sites should require OpenID.setup.php
 4+require_once(dirname(__FILE__) . '/OpenID.setup.php');
Index: tags/extensions/OpenID/REL_0_8_0/OpenID/TODO
@@ -0,0 +1,63 @@
 2++ Add a Storage class for $wgMemc
 3++ Prevent running OpenIDLogin, OpenIDFinish when you're already logged in
 4++ Add a server mode
 5++ Prevent serving OpenID accounts
 6++ Prevent trusting the same server
 7++ Limit domains whose OpenIDs you trust
 8++ For domains, Order Allow,Deny or Deny,Allow
 9++ Function to let skins tell if user is OpenID user
 10++ Function to let skins know user's OpenID URL
 11++ Interwiki login
 12+- drop-down for username prefixes (Yahoo, etc.)
 13++ server mode login
 14++ server mode trust form
 15++ server mode share fields form
 16++ change a "regular" account to an OpenID account
 17+- keep user from setting password in OpenID account
 18+- keep user from mailing new password in OpenID account
 19+- participate in account-creation throttle
 20+- audit to prevent circumventing blocks/bans
 21+- OpenID extension to share MW settings
 22+- share timezone when/if available
 23++ split big file into 2-3 smaller modules
 24++ add openid.server to user pages
 25++ hide openid.server for non-user pages
 26+- optimize trust storage
 27++ change trust data separators from , and | to FS and RS
 28+- User preferences tab for OpenID
 29+- Manage allow-to-trust settings in User preferences
 30+- Optionally redirect User: page to OpenID URL
 31+- If user logs in with OpenID, add a cookie, and auto-login next time
 32+ with check_immediate
 33++ Have some $trust_root values that we _always_ allow to trust
 34++ README
 35++ form to choose user ID if not sreg.nickname or sreg.fullname
 36++ try to guess user name from URL with /
 37+ like http://getopenid.com/evanprodromou or
 38+ http://profile.typekey.com/EvanProdromou/
 39++ Allow specifying a FileStore
 40+- Allow specifying a MySQLStore
 41+- Allow specifying a memcached store
 42++ Fall back to FileStore if Memc is bogus
 43++ Unit test for MemcStore
 44++ for just-a-hostname OpenIDs, if the first element is not
 45+ www, use the first element as a proposed ID
 46+- configurable regexps for finding a user ID from an OpenID.
 47++ Make the OpenID login page more prominent. A personal toolbox link for each page?
 48++ Update Sreg support for new API in version 2.0 libraries
 49+- Provider-driven identifier selection (eh? do we care?)
 50++ fix problem with getting $request->identity after login on server
 51++ fix problem with return_to parsing on login
 52++ fix problem with return_to parsing on convert
 53+- optionally allow user to specify an existing username/password after
 54+ logging in with OpenID the first time (like ma.gnolia.com)
 55+- deal with difference between canonical ID and "display ID" with XRIs
 56++ add more explanatory text to the default login instructions (copy Wikitravel text)
 57+- add a public-domain Help:OpenID.wiki
 58+- add more than one OpenID to a user account
 59+- manage site trust for users who've logged into other sites (after login)
 60+- link to Special:OpenIDConvert if user is already logged in
 61+- support RP discovery
 62+- AJAX login, rather than klutzy JS form
 63+- link to OpenID login on Special:Userlogin
 64+- Dynamic HTML swap in of OpenID login form on Special:Userlogin
Property changes on: tags/extensions/OpenID/REL_0_8_0/OpenID/TODO
___________________________________________________________________
Added: svn:eol-style
165 + native
Index: tags/extensions/OpenID/REL_0_8_0/OpenID/SpecialOpenIDServer.body.php
@@ -0,0 +1,738 @@
 2+<?php
 3+/**
 4+ * Server.php -- Server side of OpenID site
 5+ * Copyright 2006,2007 Internet Brands (http://www.internetbrands.com/)
 6+ * Copyright 2007,2008 Evan Prodromou <evan@prodromou.name>
 7+ *
 8+ * This program is free software; you can redistribute it and/or modify
 9+ * it under the terms of the GNU General Public License as published by
 10+ * the Free Software Foundation; either version 2 of the License, or
 11+ * (at your option) any later version.
 12+ *
 13+ * This program is distributed in the hope that it will be useful,
 14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 16+ * GNU General Public License for more details.
 17+ *
 18+ * You should have received a copy of the GNU General Public License
 19+ * along with this program; if not, write to the Free Software
 20+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 21+ *
 22+ * @author Evan Prodromou <evan@prodromou.name>
 23+ * @addtogroup Extensions
 24+ */
 25+
 26+if (!defined('MEDIAWIKI'))
 27+ exit(1);
 28+
 29+require_once("Auth/OpenID/Server.php");
 30+require_once("Auth/OpenID/Consumer.php");
 31+
 32+# Special page for the server side of OpenID
 33+# It has three major flavors:
 34+# * no parameter is for external requests to validate a user.
 35+# * 'Login' is we got a validation request but the
 36+# user wasn't logged in. We show them a form (see OpenIDServerLoginForm)
 37+# and they post the results, which go to OpenIDServerLogin
 38+# * 'Trust' is when the user has logged in, but they haven't
 39+# specified whether it's OK to let the requesting site trust them.
 40+# If they haven't, we show them a form (see OpenIDServerTrustForm)
 41+# and let them post results which go to OpenIDServerTrust.
 42+#
 43+# OpenID has its own modes; we only handle two of them ('check_setup' and
 44+# 'check_immediate') and let the OpenID libraries handle the rest.
 45+#
 46+# Output may be just a redirect, or a form if we need info.
 47+
 48+class SpecialOpenIDServer extends SpecialOpenID {
 49+
 50+ function SpecialOpenIDServer() {
 51+ SpecialPage::SpecialPage("OpenIDServer", '', false);
 52+ self::loadMessages();
 53+ }
 54+
 55+ function execute($par) {
 56+
 57+ global $wgOut;
 58+
 59+ $this->setHeaders();
 60+
 61+ $server =& $this->getServer();
 62+
 63+ switch ($par) {
 64+ case 'Login':
 65+ list($request, $sreg) = $this->FetchValues();
 66+ $result = $this->Login($request);
 67+ if ($result) {
 68+ if (is_string($result)) {
 69+ $this->LoginForm($request, $result);
 70+ return;
 71+ } else {
 72+ $this->Response($server, $result);
 73+ return;
 74+ }
 75+ }
 76+ break;
 77+ case 'Trust':
 78+ list($request, $sreg) = $this->FetchValues();
 79+ $result = $this->Trust($request, $sreg);
 80+ if ($result) {
 81+ if (is_string($result)) {
 82+ $this->TrustForm($request, $sreg, $result);
 83+ return;
 84+ } else {
 85+ $this->Response($server, $result);
 86+ return;
 87+ }
 88+ }
 89+ break;
 90+ default:
 91+ if (strlen($par)) {
 92+ $wgOut->showErrorPage('openiderror', 'openiderrortext');
 93+ return;
 94+ } else {
 95+ $method = $_SERVER['REQUEST_METHOD'];
 96+ $query = null;
 97+ if ($method == 'GET') {
 98+ $query = $_GET;
 99+ } else {
 100+ $query = $_POST;
 101+ }
 102+
 103+ $request = $server->decodeRequest();
 104+ $sreg = $this->SregFromQuery($query);
 105+ $response = NULL;
 106+ break;
 107+ }
 108+ }
 109+
 110+ if (!isset($request)) {
 111+ $wgOut->showErrorPage('openiderror', 'openiderrortext');
 112+ return;
 113+ }
 114+
 115+ global $wgUser;
 116+
 117+ switch ($request->mode) {
 118+ case "checkid_setup":
 119+ $response = $this->Check($server, $request, $sreg, false);
 120+ break;
 121+ case "checkid_immediate":
 122+ $response = $this->Check($server, $request, $sreg, true);
 123+ break;
 124+ default:
 125+ # For all the other parts, just let the libs do it
 126+ $response =& $server->handleRequest($request);
 127+ }
 128+
 129+ # OpenIDServerCheck returns NULL if some output (like a form)
 130+ # has been done
 131+
 132+ if (isset($response)) {
 133+ # We're done; clear values
 134+ $this->ClearValues();
 135+ $this->Response($server, $response);
 136+ }
 137+ }
 138+
 139+ # Returns the full URL of the special page; we need to pass it around
 140+ # for some requests
 141+
 142+ function Url() {
 143+ $nt = Title::makeTitleSafe(NS_SPECIAL, 'OpenIDServer');
 144+ if (isset($nt)) {
 145+ return $nt->getFullURL();
 146+ } else {
 147+ return NULL;
 148+ }
 149+ }
 150+
 151+ # Returns an Auth_OpenID_Server from the libraries. Utility.
 152+
 153+ function getServer() {
 154+ global $wgOpenIDServerStorePath,
 155+ $wgOpenIDServerStoreType;
 156+
 157+ $store = $this->getOpenIDStore($wgOpenIDServerStoreType,
 158+ 'server',
 159+ array('path' => $wgOpenIDServerStorePath));
 160+
 161+ return new Auth_OpenID_Server($store, $this->serverUrl());
 162+ }
 163+
 164+ # Checks a validation request. $imm means don't run any UI.
 165+ # Fairly meticulous and step-by step, and uses assertions
 166+ # to point out assumptions at each step.
 167+ #
 168+ # XXX: this should probably be broken up into multiple functions for
 169+ # clarity.
 170+
 171+ function Check($server, $request, $sreg, $imm = true) {
 172+
 173+ global $wgUser, $wgOut;
 174+
 175+ assert(isset($wgUser) && isset($wgOut));
 176+ assert(isset($server));
 177+ assert(isset($request));
 178+ assert(isset($sreg));
 179+ assert(isset($imm) && is_bool($imm));
 180+
 181+ # Is the passed identity URL a user page?
 182+
 183+ $url = $request->identity;
 184+
 185+ assert(isset($url) && strlen($url) > 0);
 186+
 187+ $name = $this->UrlToUserName($url);
 188+
 189+ if (!isset($name) || strlen($name) == 0) {
 190+ wfDebug("OpenID: '$url' not a user page.\n");
 191+ return $request->answer(false, $this->serverUrl());
 192+ }
 193+
 194+ assert(isset($name) && strlen($name) > 0);
 195+
 196+ # Is there a logged in user?
 197+
 198+ if ($wgUser->getId() == 0) {
 199+ wfDebug("OpenID: User not logged in.\n");
 200+ if ($imm) {
 201+ return $request->answer(false, $this->serverUrl());
 202+ } else {
 203+ # Bank these for later
 204+ $this->SaveValues($request, $sreg);
 205+ $this->LoginForm($request);
 206+ return NULL;
 207+ }
 208+ }
 209+
 210+ assert($wgUser->getId() != 0);
 211+
 212+ # Is the user page for the logged-in user?
 213+
 214+ $user = User::newFromName($name);
 215+
 216+ if (!isset($user) ||
 217+ $user->getId() != $wgUser->getId()) {
 218+ wfDebug("OpenID: User from url not logged in user.\n");
 219+ return $request->answer(false, $this->serverUrl());
 220+ }
 221+
 222+ assert(isset($user) && $user->getId() == $wgUser->getId() && $user->getId() != 0);
 223+
 224+ # Is the user an OpenID user?
 225+
 226+ $openid = $this->getUserUrl($user);
 227+
 228+ if (isset($openid) && strlen($openid) > 0) {
 229+ wfDebug("OpenID: Not one of our users; logs in with OpenID.\n");
 230+ return $request->answer(false, $this->serverUrl());
 231+ }
 232+
 233+ assert(is_array($sreg));
 234+
 235+ # Does the request require sreg fields that the user has not specified?
 236+
 237+ if (array_key_exists('required', $sreg)) {
 238+ $notFound = false;
 239+ foreach ($sreg['required'] as $reqfield) {
 240+ if (is_null($this->GetUserField($user, $reqfield))) {
 241+ $notFound = true;
 242+ break;
 243+ }
 244+ }
 245+ if ($notFound) {
 246+ wfDebug("OpenID: Consumer demands info we don't have.\n");
 247+ return $request->answer(false, $this->serverUrl());
 248+ }
 249+ }
 250+
 251+ # Trust check
 252+
 253+ $trust_root = $request->trust_root;
 254+
 255+ assert(isset($trust_root) && is_string($trust_root) && strlen($trust_root) > 0);
 256+
 257+ $trust = $this->GetUserTrust($user, $trust_root);
 258+
 259+ # Is there a trust record?
 260+
 261+ if (is_null($trust)) {
 262+ wfDebug("OpenID: No trust record.\n");
 263+ if ($imm) {
 264+ return $request->answer(false, $this->serverUrl());
 265+ } else {
 266+ # Bank these for later
 267+ $this->SaveValues($request, $sreg);
 268+ $this->TrustForm($request, $sreg);
 269+ return NULL;
 270+ }
 271+ }
 272+
 273+ assert(!is_null($trust));
 274+
 275+ # Is the trust record _not_ to allow trust?
 276+ # NB: exactly equal
 277+
 278+ if ($trust === false) {
 279+ wfDebug("OpenID: User specified not to allow trust.\n");
 280+ return $request->answer(false, $this->serverUrl());
 281+ }
 282+
 283+ assert(isset($trust) && is_array($trust));
 284+
 285+ # Does the request require sreg fields that the user has
 286+ # not allowed us to pass, or has not specified?
 287+
 288+ if (array_key_exists('required', $sreg)) {
 289+ $notFound = false;
 290+ foreach ($sreg['required'] as $reqfield) {
 291+ if (!in_array($reqfield, $trust) ||
 292+ is_null($this->GetUserField($user, $reqfield))) {
 293+ $notFound = true;
 294+ break;
 295+ }
 296+ }
 297+ if ($notFound) {
 298+ wfDebug("OpenID: Consumer demands info user doesn't want shared.\n");
 299+ return $request->answer(false, $this->serverUrl());
 300+ }
 301+ }
 302+
 303+ # assert(all required sreg fields are in $trust)
 304+
 305+ # XXX: run a hook here to check
 306+
 307+ # SUCCESS
 308+
 309+ $response_fields = array_intersect(array_unique(array_merge($sreg['required'], $sreg['optional'])),
 310+ $trust);
 311+
 312+ $response = $request->answer(true);
 313+
 314+ assert(isset($response));
 315+
 316+ foreach ($response_fields as $field) {
 317+ $value = $this->GetUserField($user, $field);
 318+ if (!is_null($value)) {
 319+ $response->addField('sreg', $field, $value);
 320+ }
 321+ }
 322+
 323+ return $response;
 324+ }
 325+
 326+ # Get the user's configured trust value for a particular trust root.
 327+ # Returns one of three values:
 328+ # * NULL -> no stored trust preferences
 329+ # * false -> stored trust preference is not to trust
 330+ # * array -> possibly empty array of allowed profile fields; trust is OK
 331+
 332+ function GetUserTrust($user, $trust_root) {
 333+ static $allFields = array('nickname', 'fullname', 'email', 'language');
 334+ global $wgOpenIDServerForceAllowTrust;
 335+
 336+ foreach ($wgOpenIDServerForceAllowTrust as $force) {
 337+ if (preg_match($force, $trust_root)) {
 338+ return $allFields;
 339+ }
 340+ }
 341+
 342+ $trust_array = $this->GetUserTrustArray($user);
 343+
 344+ if (array_key_exists($trust_root, $trust_array)) {
 345+ return $trust_array[$trust_root];
 346+ } else {
 347+ return null; # Unspecified trust
 348+ }
 349+ }
 350+
 351+ function SetUserTrust(&$user, $trust_root, $value) {
 352+
 353+ $trust_array = $this->GetUserTrustArray($user);
 354+
 355+ if (is_null($value)) {
 356+ if (array_key_exists($trust_root, $trust_array)) {
 357+ unset($trust_array[$trust_root]);
 358+ }
 359+ } else {
 360+ $trust_array[$trust_root] = $value;
 361+ }
 362+
 363+ $this->SetUserTrustArray($user, $trust_array);
 364+ }
 365+
 366+ function GetUserTrustArray($user) {
 367+ $trust_array = array();
 368+ $trust_str = $user->getOption('openid_trust');
 369+ if (strlen($trust_str) > 0) {
 370+ $trust_records = explode("\x1E", $trust_str);
 371+ foreach ($trust_records as $record) {
 372+ $fields = explode("\x1F", $record);
 373+ $trust_root = array_shift($fields);
 374+ if (count($fields) == 1 && strcmp($fields[0], 'no') == 0) {
 375+ $trust_array[$trust_root] = false;
 376+ } else {
 377+ $fields = array_map('trim', $fields);
 378+ $fields = array_filter($fields, array($this, 'ValidField'));
 379+ $trust_array[$trust_root] = $fields;
 380+ }
 381+ }
 382+ }
 383+ return $trust_array;
 384+ }
 385+
 386+ function SetUserTrustArray(&$user, $arr) {
 387+ $trust_records = array();
 388+ foreach ($arr as $root => $value) {
 389+ if ($value === false) {
 390+ $record = implode("\x1F", array($root, 'no'));
 391+ } else if (is_array($value)) {
 392+ if (count($value) == 0) {
 393+ $record = $root;
 394+ } else {
 395+ $value = array_map('trim', $value);
 396+ $value = array_filter($value, array($this, 'ValidField'));
 397+ $record = implode("\x1F", array_merge(array($root), $value));
 398+ }
 399+ } else {
 400+ continue;
 401+ }
 402+ $trust_records[] = $record;
 403+ }
 404+ $trust_str = implode("\x1E", $trust_records);
 405+ $user->setOption('openid_trust', $trust_str);
 406+ }
 407+
 408+ function ValidField($name) {
 409+ # XXX: eventually add timezone
 410+ static $fields = array('nickname', 'email', 'fullname', 'language');
 411+ return in_array($name, $fields);
 412+ }
 413+
 414+ function SregFromQuery($query) {
 415+ $sreg = array('required' => array(), 'optional' => array(),
 416+ 'policy_url' => NULL);
 417+ if (array_key_exists('openid.sreg.required', $query)) {
 418+ $sreg['required'] = explode(',', $query['openid.sreg.required']);
 419+ }
 420+ if (array_key_exists('openid.sreg.optional', $query)) {
 421+ $sreg['optional'] = explode(',', $query['openid.sreg.optional']);
 422+ }
 423+ if (array_key_exists('openid.sreg.policy_url', $query)) {
 424+ $sreg['policy_url'] = $query['openid.sreg.policy_url'];
 425+ }
 426+ return $sreg;
 427+ }
 428+
 429+ function SetUserField(&$user, $field, $value) {
 430+ switch ($field) {
 431+ case 'fullname':
 432+ $user->setRealName($value);
 433+ return true;
 434+ break;
 435+ case 'email':
 436+ # FIXME: deal with validation
 437+ $user->setEmail($value);
 438+ return true;
 439+ break;
 440+ case 'language':
 441+ $user->setOption('language', $value);
 442+ return true;
 443+ break;
 444+ default:
 445+ return false;
 446+ }
 447+ }
 448+
 449+ function GetUserField($user, $field) {
 450+ switch ($field) {
 451+ case 'nickname':
 452+ return $user->getName();
 453+ break;
 454+ case 'fullname':
 455+ return $user->getRealName();
 456+ break;
 457+ case 'email':
 458+ return $user->getEmail();
 459+ break;
 460+ case 'language':
 461+ return $user->getOption('language');
 462+ break;
 463+ default:
 464+ return NULL;
 465+ }
 466+ }
 467+
 468+ function Response(&$server, &$response) {
 469+ global $wgOut;
 470+
 471+ assert(!is_null($server));
 472+ assert(!is_null($response));
 473+
 474+ $wgOut->disable();
 475+
 476+ $wr =& $server->encodeResponse($response);
 477+
 478+ assert(!is_null($wr));
 479+
 480+ header("Status: " . $wr->code);
 481+
 482+ foreach ($wr->headers as $k => $v) {
 483+ header("$k: $v");
 484+ }
 485+
 486+ print $wr->body;
 487+
 488+ return;
 489+ }
 490+
 491+ function LoginForm($request, $msg = null) {
 492+
 493+ global $wgOut, $wgUser;
 494+
 495+ $url = $request->identity;
 496+ $name = $this->UrlToUserName($url);
 497+ $trust_root = $request->trust_root;
 498+
 499+ $instructions = wfMsg('openidserverlogininstructions', $url, $name, $trust_root);
 500+
 501+ $username = wfMsg('yourname');
 502+ $password = wfMsg('yourpassword');
 503+ $ok = wfMsg('ok');
 504+ $cancel = wfMsg('cancel');
 505+
 506+ if (isset($msg)) {
 507+ $wgOut->addHTML("<p class='error'>{$msg}</p>");
 508+ }
 509+
 510+ $sk = $wgUser->getSkin();
 511+
 512+ $wgOut->addHTML("<p>{$instructions}</p>" .
 513+ '<form action="' . $sk->makeSpecialUrl('OpenIDServer/Login') . '" method="POST">' .
 514+ '<table>' .
 515+ "<tr><td><label for='username'>{$username}</label></td>" .
 516+ ' <td><span id="username">' . htmlspecialchars($name) . '</span></td></tr>' .
 517+ "<tr><td><label for='password'>{$password}</label></td>" .
 518+ ' <td><input type="password" name="wpPassword" size="32" value="" /></td></tr>' .
 519+ "<tr><td colspan='2'><input type='submit' name='wpOK' value='{$ok}' /> <input type='submit' name='wpCancel' value='{$cancel}' /></td></tr>" .
 520+ '</table>' .
 521+ '</form>');
 522+ }
 523+
 524+ function SaveValues($request, $sreg) {
 525+ global $wgSessionStarted, $wgUser;
 526+
 527+ if (!$wgSessionStarted) {
 528+ $wgUser->SetupSession();
 529+ }
 530+
 531+ $_SESSION['openid_server_request'] = $request;
 532+ $_SESSION['openid_server_sreg'] = $sreg;
 533+
 534+ return true;
 535+ }
 536+
 537+ function FetchValues() {
 538+ return array($_SESSION['openid_server_request'], $_SESSION['openid_server_sreg']);
 539+ }
 540+
 541+ function ClearValues() {
 542+ unset($_SESSION['openid_server_request']);
 543+ unset($_SESSION['openid_server_sreg']);
 544+ return true;
 545+ }
 546+
 547+ function Login($request) {
 548+
 549+ global $wgRequest, $wgUser;
 550+
 551+ assert(isset($request));
 552+
 553+ assert(isset($wgRequest));
 554+
 555+ if ($wgRequest->getCheck('wpCancel')) {
 556+ return $request->answer(false);
 557+ }
 558+
 559+ $password = $wgRequest->getText('wpPassword');
 560+
 561+ if (!isset($password) || strlen($password) == 0) {
 562+ return wfMsg('wrongpasswordempty');
 563+ }
 564+
 565+ assert (isset($password) && strlen($password) > 0);
 566+
 567+ $url = $request->identity;
 568+
 569+ assert(isset($url) && is_string($url) && strlen($url) > 0);
 570+
 571+ $name = $this->UrlToUserName($url);
 572+
 573+ assert(isset($name) && is_string($name) && strlen($name) > 0);
 574+
 575+ $user = User::newFromName($name);
 576+
 577+ assert(isset($user));
 578+
 579+ if (!$user->checkPassword($password)) {
 580+ return wfMsg('wrongpassword');
 581+ } else {
 582+ $id = $user->getId();
 583+ $wgUser = $user;
 584+ $wgUser->SetupSession();
 585+ $wgUser->SetCookies();
 586+ wfRunHooks('UserLoginComplete', array(&$wgUser));
 587+ return false;
 588+ }
 589+ }
 590+
 591+ function TrustForm($request, $sreg, $msg = NULL) {
 592+
 593+ global $wgOut, $wgUser;
 594+
 595+ $url = $request->identity;
 596+ $name = $this->UrlToUserName($url);
 597+ $trust_root = $request->trust_root;
 598+
 599+ $instructions = wfMsg('openidtrustinstructions', $trust_root);
 600+ $allow = wfMsg('openidallowtrust', $trust_root);
 601+
 602+ if (is_null($sreg['policy_url'])) {
 603+ $policy = wfMsg('openidnopolicy');
 604+ } else {
 605+ $policy = wfMsg('openidpolicy', $sreg['policy_url']);
 606+ }
 607+
 608+ if (isset($msg)) {
 609+ $wgOut->addHTML("<p class='error'>{$msg}</p>");
 610+ }
 611+
 612+ $ok = wfMsg('ok');
 613+ $cancel = wfMsg('cancel');
 614+
 615+ $sk = $wgUser->getSkin();
 616+
 617+ $wgOut->addHTML("<p>{$instructions}</p>" .
 618+ '<form action="' . $sk->makeSpecialUrl('OpenIDServer/Trust') . '" method="POST">' .
 619+ '<input name="wpAllowTrust" type="checkbox" value="on" checked="checked" id="wpAllowTrust">' .
 620+ '<label for="wpAllowTrust">' . $allow . '</label><br />');
 621+
 622+ $fields = array_filter(array_unique(array_merge($sreg['optional'], $sreg['required'])),
 623+ array($this, 'ValidField'));
 624+
 625+ if (count($fields) > 0) {
 626+ $wgOut->addHTML('<table>');
 627+ foreach ($fields as $field) {
 628+ $wgOut->addHTML("<tr>");
 629+ $wgOut->addHTML("<th><label for='wpAllow{$field}'>");
 630+ $wgOut->addHTML(wfMsg("openid$field"));
 631+ $wgOut->addHTML("</label></th>");
 632+ $value = $this->GetUserField($wgUser, $field);
 633+ $wgOut->addHTML("</td>");
 634+ $wgOut->addHTML("<td> " . ((is_null($value)) ? '' : $value) . "</td>");
 635+ $wgOut->addHTML("<td>" . ((in_array($field, $sreg['required'])) ? wfMsg('openidrequired') : wfMsg('openidoptional')) . "</td>");
 636+ $wgOut->addHTML("<td><input name='wpAllow{$field}' id='wpAllow{$field}' type='checkbox'");
 637+ if (!is_null($value)) {
 638+ $wgOut->addHTML(" value='on' checked='checked' />");
 639+ } else {
 640+ $wgOut->addHTML(" disabled='disabled' />");
 641+ }
 642+ $wgOut->addHTML("</tr>");
 643+ }
 644+ $wgOut->addHTML('</table>');
 645+ }
 646+ $wgOut->addHTML("<input type='submit' name='wpOK' value='{$ok}' /> <input type='submit' name='wpCancel' value='{$cancel}' /></form>");
 647+ return NULL;
 648+ }
 649+
 650+ function Trust($request, $sreg) {
 651+ global $wgRequest, $wgUser;
 652+
 653+ assert(isset($request));
 654+ assert(isset($sreg));
 655+ assert(isset($wgRequest));
 656+
 657+ if ($wgRequest->getCheck('wpCancel')) {
 658+ return $request->answer(false);
 659+ }
 660+
 661+ $trust_root = $request->trust_root;
 662+
 663+ assert(isset($trust_root) && strlen($trust_root) > 0);
 664+
 665+ # If they don't want us to allow trust, save that.
 666+
 667+ if (!$wgRequest->getCheck('wpAllowTrust')) {
 668+
 669+ $this->SetUserTrust($wgUser, $trust_root, false);
 670+ # Set'em and sav'em
 671+ $wgUser->saveSettings();
 672+ } else {
 673+
 674+ $fields = array_filter(array_unique(array_merge($sreg['optional'], $sreg['required'])),
 675+ array($this, 'ValidField'));
 676+
 677+ $allow = array();
 678+
 679+ foreach ($fields as $field) {
 680+ if ($wgRequest->getCheck('wpAllow' . $field)) {
 681+ $allow[] = $field;
 682+ }
 683+ }
 684+
 685+ $this->SetUserTrust($wgUser, $trust_root, $allow);
 686+ # Set'em and sav'em
 687+ $wgUser->saveSettings();
 688+ }
 689+
 690+ }
 691+
 692+ # Converts an URL to a user name, if possible
 693+
 694+ function UrlToUserName($url) {
 695+
 696+ global $wgArticlePath, $wgServer;
 697+
 698+ # URL must be a string
 699+
 700+ if (!isset($url) || !is_string($url) || strlen($url) == 0) {
 701+ return null;
 702+ }
 703+
 704+ # it must start with our server, case doesn't matter
 705+
 706+ if (strpos(strtolower($url), strtolower($wgServer)) !== 0) {
 707+ return null;
 708+ }
 709+
 710+ $parts = parse_url($url);
 711+
 712+ $relative = $parts['path'];
 713+ if (!is_null($parts['query']) && strlen($parts['query']) > 0) {
 714+ $relative .= '?' . $parts['query'];
 715+ }
 716+
 717+ # Use regexps to extract user name
 718+
 719+ $pattern = str_replace('$1', '(.*)', $wgArticlePath);
 720+ $pattern = str_replace('?', '\?', $pattern);
 721+ # Can't have a pound-sign in the relative, since that's for fragments
 722+ if (!preg_match("#$pattern#", $relative, $matches)) {
 723+ return null;
 724+ } else {
 725+ $titletext = urldecode($matches[1]);
 726+ $nt = Title::newFromText($titletext);
 727+ if (is_null($nt) || $nt->getNamespace() != NS_USER) {
 728+ return null;
 729+ } else {
 730+ return $nt->getText();
 731+ }
 732+ }
 733+ }
 734+
 735+ function serverUrl() {
 736+ return $this->fullURL('OpenIDServer');
 737+ }
 738+}
 739+
Property changes on: tags/extensions/OpenID/REL_0_8_0/OpenID/SpecialOpenIDServer.body.php
___________________________________________________________________
Added: svn:eol-style
1740 + native
Index: tags/extensions/OpenID/REL_0_8_0/OpenID/optionToTable.php
@@ -0,0 +1,50 @@
 2+<?php
 3+/**
 4+ * optionToTable.php -- Convert old user_options-based
 5+ * Copyright 2006,2007 Internet Brands (http://www.internetbrands.com/)
 6+ * By Evan Prodromou <evan@wikitravel.org>
 7+ *
 8+ * This program is free software; you can redistribute it and/or modify
 9+ * it under the terms of the GNU General Public License as published by
 10+ * the Free Software Foundation; either version 2 of the License, or
 11+ * (at your option) any later version.
 12+ *
 13+ * This program is distributed in the hope that it will be useful,
 14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 16+ * GNU General Public License for more details.
 17+ *
 18+ * You should have received a copy of the GNU General Public License
 19+ * along with this program; if not, write to the Free Software
 20+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 21+ *
 22+ * @author Evan Prodromou <evan@wikitravel.org>
 23+ * @addtogroup Extensions
 24+ */
 25+
 26+require_once('commandLine.inc');
 27+ini_set( "include_path", "/usr/share/php:" . ini_get("include_path"));
 28+
 29+require_once("$IP/extensions/OpenID/Consumer.php");
 30+
 31+global $wgSharedDB, $wgDBprefix;
 32+$tableName = "${wgDBprefix}user_openid";
 33+if (isset($wgSharedDB)) {
 34+ $tableName = "`$wgSharedDB`.$tableName";
 35+}
 36+
 37+$dbr =& wfGetDB( DB_SLAVE );
 38+
 39+$res = $dbr->select(array('user'),
 40+ array('user_name'),
 41+ array('user_options LIKE "%openid_url%"'),
 42+ 'optionToTable',
 43+ array('ORDER BY' => 'user_name'));
 44+
 45+while ($res && $row = $dbr->fetchObject($res)) {
 46+ $user = User::newFromName($row->user_name);
 47+ print( $user->getName() . ": " . $user->getOption('openid_url') . "\n");
 48+ OpenIDSetUserUrl($user, $user->getOption('openid_url'));
 49+}
 50+$dbr->freeResult($res);
 51+
Property changes on: tags/extensions/OpenID/REL_0_8_0/OpenID/optionToTable.php
___________________________________________________________________
Added: svn:eol-style
152 + native
Index: tags/extensions/OpenID/REL_0_8_0/OpenID/SpecialOpenIDLogin.body.php
@@ -0,0 +1,176 @@
 2+<?php
 3+/**
 4+ * SpecialOpenIDLogin.body.php -- Consumer side of OpenID site
 5+ * Copyright 2006,2007 Internet Brands (http://www.internetbrands.com/)
 6+ * Copyright 2007,2008 Evan Prodromou <evan@prodromou.name>
 7+ *
 8+ * This program is free software; you can redistribute it and/or modify
 9+ * it under the terms of the GNU General Public License as published by
 10+ * the Free Software Foundation; either version 2 of the License, or
 11+ * (at your option) any later version.
 12+ *
 13+ * This program is distributed in the hope that it will be useful,
 14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 16+ * GNU General Public License for more details.
 17+ *
 18+ * You should have received a copy of the GNU General Public License
 19+ * along with this program; if not, write to the Free Software
 20+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 21+ *
 22+ * @author Evan Prodromou <evan@prodromou.name>
 23+ * @addtogroup Extensions
 24+ */
 25+
 26+if (!defined('MEDIAWIKI'))
 27+ exit(1);
 28+
 29+require_once("Auth/OpenID/Consumer.php");
 30+
 31+class SpecialOpenIDLogin extends SpecialOpenID {
 32+
 33+ function SpecialOpenIDLogin() {
 34+ SpecialPage::SpecialPage("OpenIDLogin");
 35+ self::loadMessages();
 36+ }
 37+
 38+ function execute($par) {
 39+ global $wgRequest, $wgUser, $wgOut;
 40+
 41+ $this->setHeaders();
 42+
 43+ if ($wgUser->getID() != 0) {
 44+ $this->alreadyLoggedIn();
 45+ return;
 46+ }
 47+
 48+ if ($wgRequest->getText('returnto')) {
 49+ $this->setReturnTo($wgRequest->getText('returnto'));
 50+ }
 51+
 52+ $openid_url = $wgRequest->getText('openid_url');
 53+
 54+ if (isset($openid_url) && strlen($openid_url) > 0) {
 55+ $this->login($openid_url);
 56+ } else {
 57+ $this->loginForm();
 58+ }
 59+ }
 60+
 61+ function loginForm() {
 62+ global $wgOut, $wgUser, $wgOpenIDLoginLogoUrl;
 63+ $sk = $wgUser->getSkin();
 64+ $instructions = wfMsgExt('openidlogininstructions', array('parse'));
 65+ $label = wfMsg('openidloginlabel');
 66+ $ok = wfMsg('login');
 67+ $wgOut->addHTML('<form action="' . $sk->makeSpecialUrl('OpenIDLogin') . '" method="POST">' .
 68+ '<label for="openid_url">' . $label . '</label> ' .
 69+ '<input type="text" name="openid_url" id="openid_url" size=30 ' .
 70+ ' style="background: url(' . $wgOpenIDLoginLogoUrl . ') ' .
 71+ ' no-repeat; background-color: #fff; background-position: 0 50%; ' .
 72+ ' color: #000; padding-left: 18px;" value="" />' .
 73+ '<input type="submit" value="' . $ok . '" />' .
 74+ '</form>' .
 75+ $instructions
 76+ );
 77+ }
 78+
 79+ function toUserName($openid) {
 80+ if (Services_Yadis_identifierScheme($openid) == 'XRI') {
 81+ wfDebug("OpenID: Handling an XRI: $openid\n");
 82+ return $this->toUserNameXri($openid);
 83+ } else {
 84+ wfDebug("OpenID: Handling an URL: $openid\n");
 85+ return $this->toUserNameUrl($openid);
 86+ }
 87+ }
 88+
 89+ function alreadyLoggedIn() {
 90+
 91+ global $wgUser, $wgOut;
 92+
 93+ $wgOut->setPageTitle( wfMsg( 'openiderror' ) );
 94+ $wgOut->setRobotpolicy( 'noindex,nofollow' );
 95+ $wgOut->setArticleRelated( false );
 96+ $wgOut->addWikiText( wfMsg( 'openidalreadyloggedin', $wgUser->getName() ) );
 97+ $wgOut->returnToMain(false, $this->returnTo() );
 98+ }
 99+
 100+ function setUserUrl($user, $url) {
 101+ $other = $this->getUserUrl($user);
 102+ if (isset($other)) {
 103+ $this->updateUserUrl($user, $url);
 104+ } else {
 105+ $this->insertUserUrl($user, $url);
 106+ }
 107+ }
 108+
 109+ function insertUserUrl($user, $url) {
 110+ global $wgSharedDB, $wgDBname;
 111+ $dbw =& wfGetDB( DB_MASTER );
 112+
 113+ if (isset($wgSharedDB)) {
 114+ # It would be nicer to get the existing dbname
 115+ # and save it, but it's not possible
 116+ $dbw->selectDB($wgSharedDB);
 117+ }
 118+
 119+ $dbw->insert('user_openid', array('uoi_user' => $user->getId(),
 120+ 'uoi_openid' => $url));
 121+
 122+ if (isset($wgSharedDB)) {
 123+ $dbw->selectDB($wgDBname);
 124+ }
 125+ }
 126+
 127+ function updateUserUrl($user, $url) {
 128+ global $wgSharedDB, $wgDBname;
 129+ $dbw =& wfGetDB( DB_MASTER );
 130+
 131+ if (isset($wgSharedDB)) {
 132+ # It would be nicer to get the existing dbname
 133+ # and save it, but it's not possible
 134+ $dbw->selectDB($wgSharedDB);
 135+ }
 136+
 137+ $dbw->set('user_openid', 'uoi_openid', $url,
 138+ 'uoi_user = ' . $user->getID());
 139+
 140+ if (isset($wgSharedDB)) {
 141+ $dbw->selectDB($wgDBname);
 142+ }
 143+ }
 144+
 145+ function saveValues($response, $sreg) {
 146+ global $wgSessionStarted, $wgUser;
 147+
 148+ if (!$wgSessionStarted) {
 149+ $wgUser->SetupSession();
 150+ }
 151+
 152+ $_SESSION['openid_consumer_response'] = $response;
 153+ $_SESSION['openid_consumer_sreg'] = $sreg;
 154+
 155+ return true;
 156+ }
 157+
 158+ function clearValues() {
 159+ unset($_SESSION['openid_consumer_response']);
 160+ unset($_SESSION['openid_consumer_sreg']);
 161+ return true;
 162+ }
 163+
 164+ function fetchValues() {
 165+ return array($_SESSION['openid_consumer_response'], $_SESSION['openid_consumer_sreg']);
 166+ }
 167+
 168+ function returnTo() {
 169+ return $_SESSION['openid_consumer_returnto'];
 170+ }
 171+
 172+ function setReturnTo($returnto) {
 173+ $_SESSION['openid_consumer_returnto'] = $returnto;
 174+ }
 175+}
 176+
 177+?>
Property changes on: tags/extensions/OpenID/REL_0_8_0/OpenID/SpecialOpenIDLogin.body.php
___________________________________________________________________
Added: svn:eol-style
1178 + native
Index: tags/extensions/OpenID/REL_0_8_0/OpenID/README
@@ -0,0 +1,334 @@
 2+MediaWiki OpenID extension
 3+
 4+version 0.8.0
 5+11 Mar 2008
 6+
 7+This is the README file for the OpenID extension for MediaWiki
 8+software. The extension is only useful if you've got a MediaWiki
 9+installation; it can only be installed by the administrator of the site.
 10+
 11+The extension lets users log in with an OpenID
 12+(http://www.openid.net/) instead of a username and password. An OpenID
 13+is a special URL that people can use to log in to a Web site. The
 14+extension also lets users who have an account on the wiki log in to
 15+other OpenID-aware Web sites with their wiki user page as their OpenID.
 16+
 17+Typical uses:
 18+
 19+* Single-signon between multiple affiliated wikis and other sites. We
 20+ have almost 20 wikis that work together for Wikitravel, and users
 21+ can login to different Wikitravel wikis with their home wiki
 22+ account.
 23+* Single-signon across the Internet. Many, many sites now support
 24+ OpenID, including "big names" like Yahoo!, Google, and AOL. Allowing
 25+ users to login with OpenID means one less step for them to
 26+ contribute to your wiki.
 27+* Distributed reputation. Logging into a new wiki with the same
 28+ username as you have on another wiki doesn't prove that they're the
 29+ same person. Logging in with your OpenID from the old wiki does.
 30+ Using OpenID can help build a distributed reputation across the
 31+ wiki world.
 32+
 33+The software supports OpenID 2.0 and '''requires''' the
 34+openidenabled.com 2.x libraries. Users of previous versions should see
 35+[[#Upgrade]] for more information.
 36+
 37+This extension has been in use for years on several large wikis
 38+without known security problems. However, no software is completely
 39+bug-free or secure, and there's no guarantee that this software will
 40+work as advertised. See [[#Bugs]] section below for info on how to
 41+report problems.
 42+
 43+== License ==
 44+
 45+Copyright 2006,2007 Internet Brands (http://www.internetbrands.com/)
 46+Copyright 2008 Evan Prodromou (http://vinismo.com/en/User:Evan)
 47+
 48+This program is free software; you can redistribute it and/or modify
 49+it under the terms of the GNU General Public License as published by
 50+the Free Software Foundation; either version 2 of the License, or
 51+(at your option) any later version.
 52+
 53+This program is distributed in the hope that it will be useful,
 54+but WITHOUT ANY WARRANTY; without even the implied warranty of
 55+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 56+GNU General Public License for more details.
 57+
 58+You should have received a copy of the GNU General Public License
 59+along with this program; if not, write to the Free Software
 60+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 61+
 62+== Author ==
 63+
 64+Evan Prodromou <evan@vinismo.com>
 65+
 66+Patches for YADIS support and FileStore storage by Jonathan Daugherty
 67+<cygnus@janrain.com>.
 68+
 69+== Pre-requisites ==
 70+
 71+This software has been tested in production with MediaWiki 1.11.x. It
 72+may or may not work with earlier or later versions, but I'm interested
 73+in making later versions work, and I'd be happy to make minor changes
 74+to make older, unsupported versions work too.
 75+
 76+The software depends on the OpenIDEnabled.com PHP library for OpenID,
 77+which in turn depends on the OpenIDEnabled.com PHP library for YADIS.
 78+At the time of this writing, info on installing these libraries was
 79+available here:
 80+
 81+ http://www.openidenabled.com/php-openid/
 82+
 83+The last version tested with is 2.0.1; '''versions below 2.0 will not
 84+work'''. If you must use version 1.x of the openidenabled.com library,
 85+you can use the unsupported version 0.7.0 or below of this extension.
 86+
 87+There are also some required PHP extensions; see the OpenIDEnabled
 88+documentation for details. This software has been tested with the gmp
 89+and curl PHP extensions installed, and it's recommended that you
 90+install them, too.
 91+
 92+Note that some versions of MediaWiki overwrite the PHP library path in
 93+LocalSettings.php. You may need to add the path to your PHP library
 94+directory to the $path variable, like "/usr/share/php" or
 95+"/usr/local/share/php".
 96+
 97+== Installation ==
 98+
 99+To install, copy all the files in the archive you downloaded to the
 100+OpenID subdirectory of the extensions subdirectory of your MediaWiki
 101+installation. Note that the software depends on having its code all in
 102+the "OpenID" sub-directory; naming it "OpenID-Test" or "newextension1"
 103+or whatever won't work.
 104+
 105+You must create a table in your MediaWiki database to hold the OpenID
 106+URL mappings. The openid_table.sql script in this directory should do
 107+the trick. Typically you do this using the mysql command-line client,
 108+like so:
 109+
 110+ mysql -h yourdbhost -u youradminuser -p yourwikidb < openid_table.sql
 111+
 112+Version 0.3 and below of this extension used a different database
 113+structure that was pretty inefficient. If you installed this extension
 114+before, you should copy the optionToTable.php script to your MediaWiki
 115+"maintenance" directory and run it from the command line. This will
 116+copy the OpenID mappings from the user table to the new table (but it
 117+doesn't erase the old data... just in case).
 118+
 119+In your MediaWiki LocalSettings.php, add the following line some place
 120+towards the bottom of the file:
 121+
 122+ require_once("$IP/extensions/OpenID/OpenID.setup.php");
 123+
 124+Theoretically it should work out of the box, but you'll almost
 125+definitely want to set the trust root and access controls (see
 126+Configuration below).
 127+
 128+== Upgrade ==
 129+
 130+'''This is an incompatible upgrade to the previous version of the
 131+MediaWiki OpenID library.''' In particular, the interfaces of the
 132+openidenabled.com libraries have changed from 1.x to 2.x, and no
 133+effort has been made to retain backwards compatibility with the 1.x
 134+versions of the library.
 135+
 136+To upgrade, you'll need to do at least the following:
 137+
 138+* Install the 2.x version of the openidenabled.com PHP OpenID library.
 139+* Check that your consumer and server stores are correct. I got tired
 140+ of maintaining the MemcStore that nobody seemed to want, so if you
 141+ used that, you need to use the filestore now. See below for how to
 142+ configure it.
 143+* Change your require_once line in LocalSettings.php to use the
 144+ .setup.php file.
 145+* 'openidlogininstructions' is now wikitext, not HTML. If you've
 146+ customized it, you may need to re-customize it. Also, it's now
 147+ shown '''below''' the login box, so if you say ''the box below'',
 148+ you may want to change that to ''the box above''.
 149+* The extension has been converted to use a clumsy and perverse
 150+ OOP-like structure, with one class per special page. Most function
 151+ names have been changed to methods of these classes. If you used
 152+ them, look around for their replacements.
 153+* The extension has been converted to use the autoloading features
 154+ of MediaWiki, which means that you need to require() the files
 155+ directly if you really want to use their code. Or you might get
 156+ lucky and have autoloading work for you.
 157+
 158+If you find other incompatibilities that I haven't mentioned here,
 159+please let me know.
 160+
 161+== Logging in using OpenID ==
 162+
 163+To log in to the wiki using an OpenID, go to the Special:OpenIDLogin
 164+page on the wiki. Add the OpenID identity URL to the login box, and
 165+click "Verify".
 166+
 167+This ''should'' take you to the OpenID server for your identity, where
 168+you can either log in (if you're not already) or approve allowing the
 169+wiki to use your OpenID for logging in. If the OpenID server supports
 170+the Simple Registration Extension ('sreg'), it may also ask you
 171+whether to share personal information like your preferred nickname,
 172+real name, email address, etc. Choose as you wish.
 173+
 174+Once you're logged in to your OpenID server, and you've finished
 175+approving the login, you should return to the wiki from whence you
 176+came automatically.
 177+
 178+Every user who logs in with an OpenID identity for the first time will
 179+be assigned a "fake" username in the local wiki. (This just makes
 180+things work better.)
 181+
 182+If you've allowed your nickname to be passed to the wiki, and it's not
 183+already taken, and it's a legal MediaWiki user name, then it should
 184+use that for your login automatically.
 185+
 186+If not, the extension will try to make up some good candidate
 187+usernames for you and present you with a choice. If you don't like any
 188+of them, you can make up your own.
 189+
 190+After you're logged in, you can edit, read, write, and do all the
 191+other things that MediaWiki users do. Since you've got a "real"
 192+account, you'll also have a home page and a message page and such. It
 193+should also be possible to assign extra permissions ('sysop',
 194+'bureaucrat') to the account. You can log out as normal.
 195+
 196+To log back in, use the OpenIDLogin page again. Don't try to login
 197+using the regular login page, since it won't work.
 198+
 199+You can log in with an Interwiki abbreviation of an URL right now, but
 200+that's experimental and may disappear in later versions. Don't fall in
 201+love with this convenient, useful feature. You may get hurt.
 202+
 203+== Using a MediaWiki account as an OpenID ==
 204+
 205+To log in to other sites with your MediaWiki account, your OpenID
 206+identity URL is the full URL of your MediaWiki user page. So, for
 207+example, the author's identity URL is:
 208+
 209+ http://wikitravel.org/en/User:Evan
 210+
 211+When you use this OpenID with another site, logging in should take you
 212+to the wiki site. You may need to enter your password if you're not
 213+already logged in.
 214+
 215+You'll then be asked if you want to let the other site log you in, and
 216+if you want the MediaWiki wiki to share your personal information
 217+(nickname, email, full name, language) with the other site. Choose
 218+what feels comfortable to you. For some sites, you may not be asked;
 219+see Configuration below.
 220+
 221+Once you've finished deciding, the other site will finish the login.
 222+
 223+You can't log in through OpenID on the same server. You can't use the
 224+user page for a fake account created for an OpenID login as an OpenID
 225+itself.
 226+
 227+== Configuration ==
 228+
 229+The administrator can configure these variables in the
 230+LocalSettings.php file. Please read carefully.
 231+
 232+* $wgTrustRoot -- This is an URL that identifies your site to OpenID
 233+ servers. Typically, it's the "root" url of the site, like
 234+ "http://en.wikipedia.org/" or "http://wikitravel.org/it/". If this is
 235+ not set, the software will make a half-hearted guess, but it's not
 236+ very good and you should probably just set it.
 237+
 238+* $wgOpenIDConsumerDenyByDefault -- The administrator can decide which
 239+ OpenIDs are allowed to login to their server. If this flag is
 240+ true, only those OpenIDs that match one of the $wgOpenIDConsumerAllow
 241+ and not one of the $wgOpenIDConsumerDeny patterns will be allowed to
 242+ log in. If it is false, all OpenIDs are allowed to log in, unless
 243+ they are matched by an $wgOpenIDConsumerDeny pattern and not an
 244+ $wgOpenIDConsumerAllow. Typically you'll set this to true for
 245+ testing and then false for general use.
 246+
 247+* $wgOpenIDConsumerAllow -- an array of regular expressions that match
 248+ OpenIDs you want to allow to log in. For example,
 249+ "@^(http://)?wikitravel.org/@" will allow OpenIDs from the Wikitravel
 250+ domain.
 251+
 252+* $wgOpenIDConsumerDeny -- an array of regular expressions that match
 253+ OpenIDs you want to deny access to. This is mostly useful for
 254+ servers that are known to be bad. Example: "#^(http://)?example.com/#".
 255+
 256+* $wgOpenIDServerForceAllowTrust -- an array of regular expressions
 257+ that match trust roots that you want to skip trust checks for when
 258+ the user logs in from those sites. A typical example would be a
 259+ closely federated cluster of sites (like Wikimedia, Wikia, or
 260+ Wikitravel) where the personal data is available to the trusting
 261+ server ''anyways''. Be very careful using this across organizational
 262+ boundaries.
 263+
 264+* $wgOpenIDConsumerStoreType and $wgOpenIDServerStoreType -- strings
 265+ denoting the type of storage to be used to store OpenID assocation
 266+ data when acting as an OpenID relying party (consumer) and server,
 267+ respectively. Only valid value is "file"; "memc" no long valid.
 268+
 269+* $wgOpenIDConsumerStorePath and $wgOpenIDServerStorePath -- strings
 270+ specifying the paths where OpenID assocation data should be stored
 271+ when acting as a relying party (consumer) or server, respectively.
 272+ Each of these need only be set if the store type settings (above)
 273+ are set to "file", respectively. These strings, if both are set,
 274+ MUST NOT be equal. If the store type is "file", the default here is
 275+ "/tmp/$wgDBname/openidconsumer/" and "/tmp/$wgDBname/openidserver/"
 276+ respectively. The path will be automatically created if it doesn't
 277+ exist at runtime.
 278+
 279+* $wgHideOpenIDLoginLink -- boolean that says whether or not to hide
 280+ the OpenID login link in the personal URLs. Typically you'd use this
 281+ if you've already got some other method for showing the OpenID login
 282+ link, like in your skin. Note that it will *not* prevent login if
 283+ the user navigates to Special:OpenIDLogin directly; it's simply
 284+ cosmetic. This is mostly a backwards-compatibility option.
 285+
 286+* $wgOpenIDLoginLogoUrl -- Url of the OpenID login logo. Defaults to
 287+ 'http://www.openid.net/login-bg.gif', but you may want to move it to
 288+ a local URL, or an URL on a CDN, if that kind of thing floats your
 289+ boat.
 290+
 291+* $wgOpenIDShowUrlOnUserPage -- whether to show the OpenID identity URL
 292+ on a user's home page. Possible values are 'always', 'never', or 'user'
 293+ (lets the user decide). Default is 'user'.
 294+
 295+== Skins ==
 296+
 297+If you are customizing a skin, and you want to show the OpenID
 298+identity for a user (say, on their user page), use the function
 299+OpenIDGetUserUrl($user). It takes a User object (not a name or an id!)
 300+and returns the user's OpenID identity if it exists, or null if it
 301+doesn't.
 302+
 303+== Translation ==
 304+
 305+The user interface strings for this extension are configurable through
 306+the same Special:Allmessages page as MediaWiki itself. They all start
 307+with "openid", and they're no more or less cryptic than MediaWiki's.
 308+You can look at OpenID.i18n.php for some details.
 309+
 310+== OpenID services ==
 311+
 312+These are some of the OpenID services I tested this extension with;
 313+all have free signup for identities if you want to test, too.
 314+
 315+* http://www.myopenid.com/ -- uses Simple Registration Extension
 316+* http://getopenid.com/
 317+* http://www.typekey.com/
 318+* http://www.claimid.com/
 319+* http://pip.verisignlabs.com/
 320+* http://certifi.ca/
 321+
 322+== Bugs ==
 323+
 324+Bugs or feature requests can be sent to the author at
 325+evan@vinismo.com. The TODO file in this distribution has stuff I
 326+think needs to be todone; + marks show things I've already done, and -
 327+shows things that are yet to be done.
 328+
 329+The big changes for the future:
 330+
 331+* Configure some stuff through Special:Preferences or a dedicated
 332+ control panel
 333+* Auto-login if you've logged in before with an OpenID, and are logged
 334+ into that account now
 335+
Property changes on: tags/extensions/OpenID/REL_0_8_0/OpenID/README
___________________________________________________________________
Added: svn:eol-style
1336 + native
Property changes on: tags/extensions/OpenID/REL_0_8_0/OpenID
___________________________________________________________________
Added: svn:ignore
2337 + _darcs
testopenid.sh

Status & tagging log