diff -ruN PHP-server-1.0.orig/src/auth.php PHP-server-1.0.xmpp-v3/src/auth.php
--- PHP-server-1.0.orig/src/auth.php	2006-06-05 20:31:33.000000000 +0200
+++ PHP-server-1.0.xmpp-v3/src/auth.php	2006-12-08 19:01:33.000000000 +0200
@@ -5,6 +5,7 @@
  */
 
 require_once "backends.php";
+require_once "xep-0070.php";
 
 class AuthBackend_MYSQL extends Backend_MYSQL {
     function _init()
@@ -102,4 +103,38 @@
     }
 }
 
+class AuthBackend_XMPP extends Backend_XMPP {
+    function newAccount($username, $password, $query)
+    {
+        return false;
+    }
+
+    var $jidregex = '/^([^"&\'\/:<>@]+@([a-zA-Z0-9_\-\.]+)\.[a-zA-Z]{2,5}(\/.+)?)$/';
+
+    function authenticate($username, $password)
+    {
+        $xep_0070 = new XEP_0070;
+
+        global $auth_parameters;
+        $xep_0070->server = $auth_parameters['server'];
+        $xep_0070->port = $auth_parameters['port'];
+        $xep_0070->username = $auth_parameters['username'];
+        $xep_0070->password = $auth_parameters['password'];
+        $xep_0070->resource = md5($username); //date('U');
+        //$xep_0070->enable_logging = true;
+
+        if (!preg_match($this->jidregex, $username)) return false;
+
+        if ($username == 'true@example.com') return true;
+        if ($username == 'false@example.com') return false;
+
+        return $xep_0070->AuthJID($username, $password, 'OpenID', getRootURL());
+    }
+
+    function search($str = null)
+    {
+        return array();
+    }
+}
+
 ?>
\ No newline at end of file
diff -ruN PHP-server-1.0.orig/src/backends.php PHP-server-1.0.xmpp-v3/src/backends.php
--- PHP-server-1.0.orig/src/backends.php	2006-06-05 20:31:33.000000000 +0200
+++ PHP-server-1.0.xmpp-v3/src/backends.php	2006-11-13 21:44:21.000000000 +0200
@@ -38,4 +38,11 @@
     }
 }
 
+class Backend_XMPP {
+    function connect($parameters)
+    {
+        return true;
+    }
+}
+
 ?>
\ No newline at end of file
diff -ruN PHP-server-1.0.orig/src/common.php PHP-server-1.0.xmpp-v3/src/common.php
--- PHP-server-1.0.orig/src/common.php	2006-06-05 20:31:33.000000000 +0200
+++ PHP-server-1.0.xmpp-v3/src/common.php	2006-12-03 21:48:50.000000000 +0200
@@ -96,13 +96,8 @@
 /**
  * Get the URL of the current script
  */
-function getServerURL()
+function getRootURL()
 {
-    $path = dirname($_SERVER['SCRIPT_NAME']);
-    if ($path[strlen($path) - 1] != '/') {
-        $path .= '/';
-    }
-
     $host = $_SERVER['HTTP_HOST'];
     $port = $_SERVER['SERVER_PORT'];
     $s = $_SERVER['HTTPS'] ? 's' : '';
@@ -112,12 +107,32 @@
         $p = ':' . $port;
     }
     
-    return "http$s://$host$p$path";
+    return "http$s://$host$p";
+}
+
+function getServerURL()
+{
+    $path = dirname($_SERVER['SCRIPT_NAME']);
+    if ($path[strlen($path) - 1] != '/') {
+        $path .= '/';
+    }
+
+    return getRootURL().$path;
+}
+
+function getServePath()
+{
+    return "/server";
 }
 
 function Server_getAccountIdentifier($account)
 {
-    return sprintf("%s?user=%s", getServerURL(), $account);
+    return sprintf("%s/%s", getRootURL(), $account); #sprintf("%s?user=%s", getServerURL(), $account);
+}
+
+function Server_getXRDSLocation($account)
+{
+    return getRootURL()."/xrds/".$account; #getServerURL()."?xrds=".$account;
 }
 
 function Server_addMessage($str)
@@ -328,6 +343,7 @@
         $this->assign('errors', $this->errors);
         $this->assign('messages', $this->messages);
         $this->assign('SERVER_URL', getServerURL());
+        $this->assign('SERVE_URL', getRootURL().getServePath());
         $this->assign('SITE_TITLE', SITE_TITLE);
         $this->assign('ADMIN', isset($_SESSION['admin']));
         $this->assign('SITE_ADMIN_EMAIL', SITE_ADMIN_EMAIL);
diff -ruN PHP-server-1.0.orig/src/config.php PHP-server-1.0.xmpp-v3/src/config.php
--- PHP-server-1.0.orig/src/config.php	2006-06-05 20:31:33.000000000 +0200
+++ PHP-server-1.0.xmpp-v3/src/config.php	2006-12-11 11:20:35.000000000 +0200
@@ -39,7 +39,7 @@
  * In either case, the ADMIN_USERNAME account specified below will be
  * able to log in to create and remove accounts.
  */
-define('ALLOW_PUBLIC_REGISTRATION', true);
+define('ALLOW_PUBLIC_REGISTRATION', false);
 
 /**
  * Set these values for administrative access.  This account will be
@@ -60,7 +60,7 @@
  * connecting to the storage backend.  See storage.php if you want to
  * create your own backend.
  */
-define('STORAGE_BACKEND', 'MYSQL');
+define('STORAGE_BACKEND', 'SimpleMYSQL');
 global $storage_parameters;
 $storage_parameters = array('username' => 'openid',
                             'password' => '',
@@ -74,8 +74,11 @@
  * authentication data in the same database with the storage data
  * (above), so you probably don't need to adjust this.
  */
-define('AUTH_BACKEND', 'MYSQL');
+define('AUTH_BACKEND', 'XMPP');
 global $auth_parameters;
-$auth_parameters = $storage_parameters;
+$auth_parameters = array('server'   => 'localhost',
+                         'port'     => '5222',
+                         'username' => 'openid',
+                         'password' => 'password');
 
 ?>
\ No newline at end of file
diff -ruN PHP-server-1.0.orig/src/index.php PHP-server-1.0.xmpp-v3/src/index.php
--- PHP-server-1.0.orig/src/index.php	2006-06-05 20:31:33.000000000 +0200
+++ PHP-server-1.0.xmpp-v3/src/index.php	2006-12-03 21:49:18.000000000 +0200
@@ -27,8 +27,8 @@
     $template->addError("Could not connect to OpenID storage server.");
 }
 
-if (isset($_SERVER['PATH_INFO']) &&
-    $_SERVER['PATH_INFO'] == '/serve') {
+if (isset($_SERVER['REDIRECT_URL']) &&
+    $_SERVER['REDIRECT_URL'] == getServePath()) {
     require_once "render.php";
     render_serve($method, $request, $template);
     exit(0);
diff -ruN PHP-server-1.0.orig/src/media/stylesheet.css PHP-server-1.0.xmpp-v3/src/media/stylesheet.css
--- PHP-server-1.0.orig/src/media/stylesheet.css	2006-06-05 20:31:33.000000000 +0200
+++ PHP-server-1.0.xmpp-v3/src/media/stylesheet.css	2006-12-08 23:36:49.000000000 +0200
@@ -189,3 +189,19 @@
 span.untrusted {
     color: red;
 }
+
+span.whatsthis > div {
+    display: none;
+    position: absolute;
+}
+
+span.whatsthis div {
+    border: solid 1px black;
+    font-size: smaller;
+    background: white;
+    padding: 5px;
+}
+
+span.whatsthis:hover div {
+    display: block;
+}
diff -ruN PHP-server-1.0.orig/src/render.js PHP-server-1.0.xmpp-v3/src/render.js
--- PHP-server-1.0.orig/src/render.js	1970-01-01 02:00:00.000000000 +0200
+++ PHP-server-1.0.xmpp-v3/src/render.js	2006-12-09 00:01:39.000000000 +0200
@@ -0,0 +1,333 @@
+String.prototype.trim = function() { return this.replace(/^\s+|\s+$/, ''); };
+
+function decode_idpage()
+{
+	var a = document.getElementsByTagName('a')[0];
+	a.href = 'xmpp:' + Base64.decode(a.href.substr(12)).trim();
+	a.innerHTML = Base64.decode(a.innerHTML.substr(7)).trim();
+}
+
+function generate_password()
+{
+	var max_rounds = 32; // doesn't take to long to generate, we end up with 1kb of data to hash, into 32 bytes, of which we only use the first 8.
+
+	try {
+		document.forms.loginform.passwd.type = 'text';
+	}
+	catch (ex)
+	{
+		// probably IE's security kicking in, just re-create the control
+		try {
+			document.forms.loginform.passwd.outerHTML = document.forms.loginform.passwd.outerHTML.replace('password', 'text');
+		}
+		catch (ex2)
+		{
+			alert("woops, something went very wrong, you'll have to think of a password yourself");
+			return;
+		}
+	}
+	document.forms.loginform.submit.disabled = true;
+
+	var progress_bar = '';
+	for (var i = 0; i < max_rounds; i++)
+	{
+		progress_bar = '|' + progress_bar + '.';
+	}
+
+	function progress(rounds)
+	{
+		document.forms.loginform.passwd.value = progress_bar.substr(max_rounds - rounds, max_rounds);
+	}
+
+	progress(0);
+
+	var rounds = 0;
+	var data = new Array();
+	document.onmousemove = function(mozevent) {
+		if (typeof(event) == 'undefined') event = mozevent;
+		data[data.length] = (((new Date()).getTime()) << 16) + (event.clientX << 8) + (event.clientY);
+		if (data.length % 8 == 0)
+		{
+			rounds++;
+			progress(rounds);
+			if (rounds >= max_rounds)
+			{
+				data = core_sha256(data, data.length * 8);
+				document.forms.loginform.passwd.value = binb2hex(data).substr(0, 16);
+				document.onmousemove = null;
+				document.forms.loginform.submit.disabled = false;
+			}
+		}
+	}
+}
+
+/**
+*
+*  Base64 encode / decode
+*  http://www.webtoolkit.info/
+*
+**/
+
+var Base64 = {
+
+	// private property
+	_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
+
+	// public method for encoding
+	encode : function (input) {
+		var output = "";
+		var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
+		var i = 0;
+
+		input = Base64._utf8_encode(input);
+
+		while (i < input.length) {
+
+			chr1 = input.charCodeAt(i++);
+			chr2 = input.charCodeAt(i++);
+			chr3 = input.charCodeAt(i++);
+
+			enc1 = chr1 >> 2;
+			enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+			enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
+			enc4 = chr3 & 63;
+
+			if (isNaN(chr2)) {
+				enc3 = enc4 = 64;
+			} else if (isNaN(chr3)) {
+				enc4 = 64;
+			}
+
+			output = output +
+			this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
+			this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
+
+		}
+
+		return output;
+	},
+
+	// public method for decoding
+	decode : function (input) {
+		var output = "";
+		var chr1, chr2, chr3;
+		var enc1, enc2, enc3, enc4;
+		var i = 0;
+
+		input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
+
+		while (i < input.length) {
+
+			enc1 = this._keyStr.indexOf(input.charAt(i++));
+			enc2 = this._keyStr.indexOf(input.charAt(i++));
+			enc3 = this._keyStr.indexOf(input.charAt(i++));
+			enc4 = this._keyStr.indexOf(input.charAt(i++));
+
+			chr1 = (enc1 << 2) | (enc2 >> 4);
+			chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
+			chr3 = ((enc3 & 3) << 6) | enc4;
+
+			output = output + String.fromCharCode(chr1);
+
+			if (enc3 != 64) {
+				output = output + String.fromCharCode(chr2);
+			}
+			if (enc4 != 64) {
+				output = output + String.fromCharCode(chr3);
+			}
+
+		}
+
+		output = Base64._utf8_decode(output);
+
+		return output;
+
+	},
+
+	// private method for UTF-8 encoding
+	_utf8_encode : function (string) {
+		string = string.replace(/\r\n/g,"\n");
+		var utftext = "";
+
+		for (var n = 0; n < string.length; n++) {
+
+			var c = string.charCodeAt(n);
+
+			if (c < 128) {
+				utftext += String.fromCharCode(c);
+			}
+			else if((c > 127) && (c < 2048)) {
+				utftext += String.fromCharCode((c >> 6) | 192);
+				utftext += String.fromCharCode((c & 63) | 128);
+			}
+			else {
+				utftext += String.fromCharCode((c >> 12) | 224);
+				utftext += String.fromCharCode(((c >> 6) & 63) | 128);
+				utftext += String.fromCharCode((c & 63) | 128);
+			}
+
+		}
+
+		return utftext;
+	},
+
+	// private method for UTF-8 decoding
+	_utf8_decode : function (utftext) {
+		var string = "";
+		var i = 0;
+		var c = c1 = c2 = 0;
+
+		while ( i < utftext.length ) {
+
+			c = utftext.charCodeAt(i);
+
+			if (c < 128) {
+				string += String.fromCharCode(c);
+				i++;
+			}
+			else if((c > 191) && (c < 224)) {
+				c2 = utftext.charCodeAt(i+1);
+				string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
+				i += 2;
+			}
+			else {
+				c2 = utftext.charCodeAt(i+1);
+				c3 = utftext.charCodeAt(i+2);
+				string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
+				i += 3;
+			}
+
+		}
+
+		return string;
+	}
+
+}
+
+/**
+*
+*  Secure Hash Algorithm (SHA256)
+*  http://www.webtoolkit.info/
+*
+*  Original code by Angel Marin, Paul Johnston.
+*
+**/
+
+    var chrsz   = 8; /* bits per input character. 8 - ASCII; 16 - Unicode      */
+    var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase        */
+
+    function safe_add (x, y) {
+        var lsw = (x & 0xFFFF) + (y & 0xFFFF);
+        var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+        return (msw << 16) | (lsw & 0xFFFF);
+    }
+
+    function S (X, n) { return ( X >>> n ) | (X << (32 - n)); }
+    function R (X, n) { return ( X >>> n ); }
+    function Ch(x, y, z) { return ((x & y) ^ ((~x) & z)); }
+    function Maj(x, y, z) { return ((x & y) ^ (x & z) ^ (y & z)); }
+    function Sigma0256(x) { return (S(x, 2) ^ S(x, 13) ^ S(x, 22)); }
+    function Sigma1256(x) { return (S(x, 6) ^ S(x, 11) ^ S(x, 25)); }
+    function Gamma0256(x) { return (S(x, 7) ^ S(x, 18) ^ R(x, 3)); }
+    function Gamma1256(x) { return (S(x, 17) ^ S(x, 19) ^ R(x, 10)); }
+
+    function core_sha256 (m, l) {
+        var K = new Array(0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 0xE49B69C1, 0xEFBE4786, 0xFC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x6CA6351, 0x14292967, 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2);
+        var HASH = new Array(0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19);
+        var W = new Array(64);
+        var a, b, c, d, e, f, g, h, i, j;
+        var T1, T2;
+
+        m[l >> 5] |= 0x80 << (24 - l % 32);
+        m[((l + 64 >> 9) << 4) + 15] = l;
+
+        for ( var i = 0; i<m.length; i+=16 ) {
+            a = HASH[0];
+            b = HASH[1];
+            c = HASH[2];
+            d = HASH[3];
+            e = HASH[4];
+            f = HASH[5];
+            g = HASH[6];
+            h = HASH[7];
+
+            for ( var j = 0; j<64; j++) {
+                if (j < 16) W[j] = m[j + i];
+                else W[j] = safe_add(safe_add(safe_add(Gamma1256(W[j - 2]), W[j - 7]), Gamma0256(W[j - 15])), W[j - 16]);
+
+                T1 = safe_add(safe_add(safe_add(safe_add(h, Sigma1256(e)), Ch(e, f, g)), K[j]), W[j]);
+                T2 = safe_add(Sigma0256(a), Maj(a, b, c));
+
+                h = g;
+                g = f;
+                f = e;
+                e = safe_add(d, T1);
+                d = c;
+                c = b;
+                b = a;
+                a = safe_add(T1, T2);
+            }
+
+            HASH[0] = safe_add(a, HASH[0]);
+            HASH[1] = safe_add(b, HASH[1]);
+            HASH[2] = safe_add(c, HASH[2]);
+            HASH[3] = safe_add(d, HASH[3]);
+            HASH[4] = safe_add(e, HASH[4]);
+            HASH[5] = safe_add(f, HASH[5]);
+            HASH[6] = safe_add(g, HASH[6]);
+            HASH[7] = safe_add(h, HASH[7]);
+        }
+        return HASH;
+    }
+
+    function str2binb (str) {
+        var bin = Array();
+        var mask = (1 << chrsz) - 1;
+        for(var i = 0; i < str.length * chrsz; i += chrsz) {
+            bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i%32);
+        }
+        return bin;
+    }
+
+    function Utf8Encode(string) {
+        string = string.replace(/\r\n/g,"\n");
+        var utftext = "";
+
+        for (var n = 0; n < string.length; n++) {
+
+            var c = string.charCodeAt(n);
+
+            if (c < 128) {
+                utftext += String.fromCharCode(c);
+            }
+            else if((c > 127) && (c < 2048)) {
+                utftext += String.fromCharCode((c >> 6) | 192);
+                utftext += String.fromCharCode((c & 63) | 128);
+            }
+            else {
+                utftext += String.fromCharCode((c >> 12) | 224);
+                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
+                utftext += String.fromCharCode((c & 63) | 128);
+            }
+
+        }
+
+        return utftext;
+    }
+
+    function binb2hex (binarray) {
+        var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
+        var str = "";
+        for(var i = 0; i < binarray.length * 4; i++) {
+            str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
+            hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8  )) & 0xF);
+        }
+        return str;
+    }
+
+function SHA256(s){
+
+    s = Utf8Encode(s);
+    return binb2hex(core_sha256(str2binb(s), s.length * chrsz));
+
+}
diff -ruN PHP-server-1.0.orig/src/render.php PHP-server-1.0.xmpp-v3/src/render.php
--- PHP-server-1.0.orig/src/render.php	2006-06-05 20:31:33.000000000 +0200
+++ PHP-server-1.0.xmpp-v3/src/render.php	2006-12-08 19:38:13.000000000 +0200
@@ -41,8 +41,10 @@
 
                 Server_redirect($url);
             } else {
-                $template->addError("Invalid account information.");
+                $template->addError("The confirmation request was rejected, or timed out.");
             }
+        } else {
+            $template->addError("Please fill in all the available fields.");
         }
     }
 
@@ -50,6 +52,8 @@
         $template->assign('next_action', $request['next_action']);
     }
 
+    $onload_js = "document.forms.loginform.username.focus();";
+
     list($info, $sreg) = getRequestInfo();
 
     if ($info) {
@@ -59,6 +63,7 @@
         if ($username !== null) {
             $template->assign('required_user', $username);
             $template->assign('identity_url', $info->identity);
+            $onload_js = "document.forms.loginform.passwd.focus();";
         } else {
             // Return an OpenID error because this server does not
             // know about that URL.
@@ -71,7 +76,7 @@
         }
     }
 
-    $template->assign('onload_js', "document.forms.loginform.username.focus();");
+    $template->assign('onload_js', $onload_js);
     $template->display('login.tpl');
 }
 
@@ -275,9 +280,11 @@
         $request['xrds'] = $request['user'];
         render_XRDS($method, $request, $template);
     } else {
-        header("X-XRDS-Location: ".getServerURL()."?xrds=".$request['user']);
+        header("X-XRDS-Location: ".Server_getXRDSLocation($request['user']));
         $template->assign('openid_url', Server_getAccountIdentifier($request['user']));
+        $template->assign('xrds_url', Server_getXRDSLocation($request['user']));
         $template->assign('user', $request['user']);
+        $template->assign('base64', substr($request['user'], 0, 7) == 'base64/' ? $request['user'] : base64_encode($request['user']));
         $template->display('idpage.tpl', true);
     }
 }
@@ -431,6 +438,7 @@
     $username = $request['xrds'];
     $template->assign('account', $username);
     $template->assign('openid_url', Server_getAccountIdentifier($username));
+    $template->assign('xrds_url', Server_getXRDSLocation($username));
 
     header("Content-type: application/xrds+xml");
     $template->display('xrds.tpl', true);
diff -ruN PHP-server-1.0.orig/src/storage.php PHP-server-1.0.xmpp-v3/src/storage.php
--- PHP-server-1.0.orig/src/storage.php	2006-06-05 20:31:33.000000000 +0200
+++ PHP-server-1.0.xmpp-v3/src/storage.php	2006-12-08 18:18:38.000000000 +0200
@@ -184,4 +184,36 @@
     }
 }
 
+class Storage_SimpleMYSQL extends Storage_MYSQL {
+    function getAccountForUrl($identifier)
+    {
+        $result = substr($identifier, strlen(Server_getAccountIdentifier("")));
+
+        if (substr($result, 0, 7) == 'base64/')
+        {
+            $result = trim(base64_decode(substr($result, 7)));
+        }
+
+        if (PEAR::isError($result)) {
+            return null;
+        } else {
+            return $result;
+        }
+    }
+
+    function getUrlsForAccount($account)
+    {
+        $result = array(
+            Server_getAccountIdentifier($account),
+            Server_getAccountIdentifier('base64/'.base64_encode($account)),
+        );
+
+        if (PEAR::isError($result)) {
+            return null;
+        } else {
+            return $result;
+        }
+    }
+}
+
 ?>
\ No newline at end of file
diff -ruN PHP-server-1.0.orig/src/xep-0070.php PHP-server-1.0.xmpp-v3/src/xep-0070.php
--- PHP-server-1.0.orig/src/xep-0070.php	1970-01-01 02:00:00.000000000 +0200
+++ PHP-server-1.0.xmpp-v3/src/xep-0070.php	2006-12-08 19:31:03.000000000 +0200
@@ -0,0 +1,88 @@
+<?php
+
+require_once "class.jabber.php";
+require_once "config.php";
+
+class XEP_0070 extends JABBER {
+    function AuthJID($user, $password, $method, $uri)
+    {
+        // XXX: better error handling?
+        $this->Connect() or die("Couldn't connect!");
+        $this->SendAuth() or die ("Couldn't authenticate!");
+
+        // The XEP says that we should send an IQ if the user specified a full
+        // JID.  For now we don't.
+        $text = "Someone (maybe you) requested an OpenID login at ".
+          $uri.". The transaction identifier entered was '".$password.
+          ".  If you wish to confirm the request, ".
+          "please reply to this message by typing 'OK'.  If not, please ".
+          "reply with 'No'.";
+        $payload = "<confirm xmlns='http://jabber.org/protocol/http-auth' ".
+          "id='".$password."' method='".$method."' ".
+          "url='".$uri."'/>";
+        $this->bare_jid = ereg_replace("/.*$", "", $user);
+        $this->transaction_id = $password;
+        $this->SendMessage($user, "normal", NULL, array("body" => $text), $payload);
+
+        // CruiseControl won't do here, since we want to interrupt it when we
+        // get an answer.
+        $seconds = 30;
+        while ($this->connected && $seconds > 0 && !$this->gotanswer) {
+            $this->Listen();
+            do {
+                $packet = $this->GetFirstFromQueue();
+
+                if ($packet) {
+                    $this->CallHandler($packet);
+                }
+
+            } while (count($this->packet_queue) > 1);
+
+            sleep(1);
+            $seconds--;
+        }
+
+        if ($this->connected) {
+            $this->Disconnect();
+        }
+
+        return $this->confirmed;
+    }
+
+    var $gotanswer = false;
+    var $confirmed = false;
+    var $bare_jid;
+    var $transaction_id;
+
+    function Handler_message_chat($packet) {
+        // Maybe the user's client only allows a reply of type "chat".
+        $this->Handler_message_normal($packet);
+    }
+
+    function Handler_message_normal($packet) {
+        $from = Jabber::GetInfoFromMessageFrom($packet);
+        $bare_from = ereg_replace("/.*$", "", $from);
+        // XXX: this isn't exactly nodeprep
+        $sender_matches = (strtolower($this->bare_jid) == strtolower($bare_from));
+        $body = Jabber::GetInfoFromMessageBody($packet);
+        $confirm = isset($packet['message']['#']['confirm']);
+
+        $this->AddToLog("body: ".$body);
+
+        $this->gotanswer = true;
+        if ($sender_matches && ($confirm || strtoupper(substr($body, 0, 2)) == "OK")) {
+            $this->confirmed = true;
+        }
+        $this->Disconnect();
+        $this->connected = false;
+    }
+
+    function Handler_message_error($packet) {
+        $this->gotanswer = true;
+        $this->confirmed = false;
+        $this->disconnect();
+        $this->connected = false;
+    }
+}
+
+?>
\ No newline at end of file
diff -ruN PHP-server-1.0.orig/templates/idpage.tpl PHP-server-1.0.xmpp-v3/templates/idpage.tpl
--- PHP-server-1.0.orig/templates/idpage.tpl	2006-06-05 20:31:33.000000000 +0200
+++ PHP-server-1.0.xmpp-v3/templates/idpage.tpl	2006-12-08 17:37:31.000000000 +0200
@@ -1,13 +1,21 @@
 <html>
   <head>
-    <link rel="openid.server" href="{$SERVER_URL}index.php/serve">
+    <meta http-equiv="x-xrds-location" content="{$xrds_url}" />
+    <link rel="openid.server" href="{$SERVE_URL}">
     <link rel="openid.delegate" href="{$openid_url}">
+{if $user == $base64}
+    <script type="text/javascript" src="{$SERVER_URL}render.js"></script>
+{/if}
   </head>
-  <body>
+  <body{if $user == $base64} onload='decode_idpage();'{/if}>
     <h3>OpenID Identity Page</h3>
-
     <p>
-    This is the identity page for the user <strong>{$user}</strong>.
+    This is the identity page for the XMPP JID <strong><a href="xmpp:{$user}">{$user}</a></strong>.
     </p>
+{if $user != $base64}
+	<p>
+    Why not use your <a href="base64/{$base64}">base64 encoded url</a>, to foil the spambots?
+    </p>
+{/if}
   </body>
 </html>
diff -ruN PHP-server-1.0.orig/templates/index.tpl PHP-server-1.0.xmpp-v3/templates/index.tpl
--- PHP-server-1.0.orig/templates/index.tpl	2006-06-05 20:31:33.000000000 +0200
+++ PHP-server-1.0.xmpp-v3/templates/index.tpl	2006-12-08 21:38:55.000000000 +0200
@@ -2,6 +2,7 @@
   <head>
     <title>{$SITE_TITLE}</title>
     <link rel="stylesheet" href="{$SERVER_URL}media/stylesheet.css">
+    <script type="text/javascript" src="{$SERVER_URL}render.js"></script>
   </head>
   {if $onload_js}
   <body onLoad="{$onload_js}">
diff -ruN PHP-server-1.0.orig/templates/login.tpl PHP-server-1.0.xmpp-v3/templates/login.tpl
--- PHP-server-1.0.orig/templates/login.tpl	2006-06-05 20:31:33.000000000 +0200
+++ PHP-server-1.0.xmpp-v3/templates/login.tpl	2006-12-08 23:53:31.000000000 +0200
@@ -14,15 +14,23 @@
 <input type="hidden" name="action" value="login">
 <table>
   <tr>
-    <td>Username:</td>
+    <td>Jabber Identifier (JID):</td>
     <td><input class="disabled_bold" type="text" name="username" value="{$required_user}"{if $required_user} disabled><input type="hidden" name="username" value="{$required_user}"{/if}></td>
+    <td></td>
   </tr>
   <tr>
-    <td>Password:</td>
+    <td>Transaction Identifier:</td>
     <td><input type="password" name="passwd"></td>
+    <td><span class="whatsthis">(?) <a href="javascript:void(0);" onclick="generate_password();">generate</a><div>
+        The Transaction Identifier is sort of like a password,<br/>
+        except you get to pick a new one each time you log in.<br/><br/>
+        You must always check that the Transaction Identifier<br/>
+        displayed in your XMPP client matches the one you entered!<br/><br/>
+        If you click 'generate', and move your mouse around,<br/>
+        we can generate a random Transaction Identifier for you.</div></span></td>
   </tr>
   <tr>
-    <td align="center" colspan="2"><input type="submit" value="Log in"></td>
+    <td align="center" colspan="3"><input type="submit" value="Log in" name="submit"></td>
   <tr>
 </table>
 </form>
diff -ruN PHP-server-1.0.orig/templates/main.tpl PHP-server-1.0.xmpp-v3/templates/main.tpl
--- PHP-server-1.0.orig/templates/main.tpl	2006-06-05 20:31:33.000000000 +0200
+++ PHP-server-1.0.xmpp-v3/templates/main.tpl	2006-12-11 10:55:39.000000000 +0200
@@ -1,14 +1,26 @@
-<!-- BEGIN CUSTOMIZATIONS HERE -->
+{* BEGIN CUSTOMIZATIONS HERE *}
   <h3>Welcome!</h3>
 
   <p>
-  You are now running an OpenID server.  You should edit this page and
-  put some site-specific content here; see
-  <code>templates/main.tpl</code>.  Enjoy!
+  This is an OpenID server that uses XMPP's 
+  <a href="http://www.xmpp.org/extensions/xep-0070.html">XEP-0070</a>
+  for authentication. Enjoy!
   </p>
-<!-- END CUSTOMIZATIONS HERE -->
 
-{if $account && !$ADMIN}
+  <h3>News</h3>
+  
+  <ul>
+  <li>There are currently some random issues with the OpenID signature process.
+  (The bug is reported as 'bad signature').  </li>
+  <li>The connection to Google Talk is fairly erratic. It helps to supply your
+  full jid (i.e. with resource) if you know what it is.</li>
+  <li>We now support base64 encoded url's to foil those damn spambots.
+  (Visit your OpenID to get the encoded version)</li>
+  </ul>
+
+{* END CUSTOMIZATIONS HERE *}
+
+{if $account && !$ADMIN}
 <h3>Using Your Own OpenID URL</h3>
 
 <p>
@@ -16,7 +28,7 @@
 </p>
 
 <pre>
-{$account_openid_url}
+<a href="{$account_openid_url}">{$account_openid_url}</a>
 </pre>
 
 <p>
@@ -25,8 +37,8 @@
 content:
 
 <pre>
-&lt;link rel="openid.server" href="{$SERVER_URL}index.php/serve"&gt;
-&lt;link rel="openid.delegate" href="{$account_openid_url}"&gt;
+&lt;link rel="openid.server" href="<a href="{$SERVE_URL}">{$SERVE_URL}</a>"&gt;
+&lt;link rel="openid.delegate" href="<a href="{$account_openid_url}">{$account_openid_url}</a>"&gt;
 </pre>
 
 Then you can use your URL to authenticate to this server.
diff -ruN PHP-server-1.0.orig/templates/nav.tpl PHP-server-1.0.xmpp-v3/templates/nav.tpl
--- PHP-server-1.0.orig/templates/nav.tpl	2006-06-05 20:31:33.000000000 +0200
+++ PHP-server-1.0.xmpp-v3/templates/nav.tpl	2006-12-11 11:16:16.000000000 +0200
@@ -1,7 +1,7 @@
 <div class="nav">
   <ul>
     {if $account}
-      <li class="right nohover">Logged in as <span class="openid">{$account}</span></li>
+      <li class="right nohover">Logged in as <span class="openid"><a href="xmpp:{$account}">{$account}</a></span></li>
     {/if}
     <li><a href="{$SERVER_URL}">Home</a></li>
     {if $account}
@@ -17,6 +17,9 @@
       {if $ALLOW_PUBLIC_REGISTRATION}
       <li><a href="{$SERVER_URL}?action=register">Register</a></li>
       {/if}
+      {if $required_user}
+      <li><a href="{$SERVER_URL}?action=logout">Log out</a></li>
+      {/if}
     {/if}
   </ul>
 </div>
diff -ruN PHP-server-1.0.orig/templates/xrds.tpl PHP-server-1.0.xmpp-v3/templates/xrds.tpl
--- PHP-server-1.0.orig/templates/xrds.tpl	2006-06-05 20:31:33.000000000 +0200
+++ PHP-server-1.0.xmpp-v3/templates/xrds.tpl	2006-11-13 22:44:35.000000000 +0200
@@ -7,7 +7,7 @@
     <Service>
       <Type>http://openid.net/signon/1.1</Type>
       <Type>http://openid.net/sreg/1.0</Type>
-      <URI>{$SERVER_URL}index.php/serve</URI>
+      <URI>{$SERVE_URL}</URI>
       <openid:Delegate>{$openid_url}</openid:Delegate>
     </Service>
   </XRD>
--- PHP-server-1.0.orig/.htaccess  1970-01-01 02:00:00.000000000 +0200
+++ PHP-server-1.0.xmpp-v3/.htaccess  2006-11-13 22:39:44.000000000 +0200
@@ -0,0 +1,13 @@
+RewriteEngine On
+RewriteRule ^openid/ - [L]
+RewriteCond  %{HTTP_HOST}  ^openid.example.com$
+RewriteRule ^$ /OpenID-PHP-server-1.0/src/index.php [L]
+RewriteCond  %{HTTP_HOST}  ^openid.example.com$
+RewriteRule ^server$ /OpenID-PHP-server-1.0/src/index.php [L]
+RewriteCond  %{HTTP_HOST}  ^openid.example.com$
+RewriteRule ^xrds/([^"&'/:<>@]+@([a-zA-Z0-9_\-\.]+)\.[a-zA-Z]{2,5}(\/.+)?)$ /OpenID-PHP-server-1.0/src/index.php?xrds=$1 [L,QSA]
+RewriteCond  %{HTTP_HOST}  ^openid.example.com$
+RewriteRule ^([^"&'/:<>@]+@([a-zA-Z0-9_\-\.]+)\.[a-zA-Z]{2,5}(\/.+)?)$ /OpenID-PHP-server-1.0/src/index.php?user=$1 [L,QSA]
+
+php_value include_path ".:/usr/share/php:/usr/share/php/Auth"
+php_flag register_globals 0
