Index: modules/openid/openid.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/openid/openid.inc,v
retrieving revision 1.27
diff -u -9 -p -r1.27 openid.inc
--- modules/openid/openid.inc	22 Mar 2010 18:48:20 -0000	1.27
+++ modules/openid/openid.inc	5 Apr 2010 22:38:53 -0000
@@ -383,36 +383,44 @@ function _openid_nonce() {
     chr(mt_rand(0, 25) + 65) .
     chr(mt_rand(0, 25) + 65) .
     chr(mt_rand(0, 25) + 65);
 }
 
 /**
  * Pull the href attribute out of an html link element.
  */
 function _openid_link_href($rel, $html) {
-  $rel = preg_quote($rel);
-  preg_match('|<link\s+rel=["\'](.*)' . $rel . '(.*)["\'](.*)/?>|iUs', $html, $matches);
-  if (isset($matches[3])) {
-    preg_match('|href=["\']([^"]+)["\']|iU', $matches[3], $href);
-    return trim($href[1]);
+  @$html_dom = DOMDocument::loadHTML($html);
+  if ($html_dom) {
+    $html_element = simplexml_import_dom($html_dom);
+    foreach ($html_element->head->link as $link) {
+      // The rel attribute contains a space-separated list of case-insensitive
+      // link types.
+      if (preg_match('/(\s|^)' . $rel . '(\s|$)/i', $link['rel'])) {
+        return trim($link['href']);
+      }
+    }
   }
   return FALSE;
 }
 
 /**
- * Pull the http-equiv attribute out of an html meta element
+ * Pull the content attribute out of an X-XRDS-Location meta http-equiv element.
  */
-function _openid_meta_httpequiv($equiv, $html) {
-  preg_match('|<meta\s+http-equiv=["\']' . $equiv . '["\'](.*)/?>|iUs', $html, $matches);
-  if (isset($matches[1])) {
-    preg_match('|content=["\']([^"]+)["\']|iUs', $matches[1], $content);
-    if (isset($content[1])) {
-      return $content[1];
+function _openid_meta_httpequiv($html) {
+  @$html_dom = DOMDocument::loadHTML($html);
+  if ($html_dom) {
+    $html_element = simplexml_import_dom($html_dom);
+    foreach ($html_element->head->meta as $meta) {
+      // The http-equiv attribute is case-insensitive.
+      if (strtolower(trim($meta['http-equiv'])) == 'x-xrds-location') {
+        return trim($meta['content']);
+      }
     }
   }
   return FALSE;
 }
 
 /**
  * Sign certain keys in a message
  * @param $association - object loaded from openid_association or openid_server_association table
  *              - important fields are ->assoc_type and ->mac_key
Index: modules/openid/openid.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/openid/openid.module,v
retrieving revision 1.80
diff -u -9 -p -r1.80 openid.module
--- modules/openid/openid.module	26 Mar 2010 12:12:41 -0000	1.80
+++ modules/openid/openid.module	5 Apr 2010 22:38:54 -0000
@@ -440,19 +440,19 @@ function _openid_xrds_discovery($claimed
         $services = _openid_xrds_parse($result->data);
       }
       else {
         $xrds_url = NULL;
         if (isset($result->headers['X-XRDS-Location'])) {
           $xrds_url = $result->headers['X-XRDS-Location'];
         }
         else {
           // Look for meta http-equiv link in HTML head
-          $xrds_url = _openid_meta_httpequiv('X-XRDS-Location', $result->data);
+          $xrds_url = _openid_meta_httpequiv($result->data);
         }
         if (!empty($xrds_url)) {
           $headers = array('Accept' => 'application/xrds+xml');
           $xrds_result = drupal_http_request($xrds_url, array('headers' => $headers));
           if (!isset($xrds_result->error)) {
             $services = _openid_xrds_parse($xrds_result->data);
           }
         }
       }
Index: modules/openid/tests/openid_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/openid/tests/openid_test.module,v
retrieving revision 1.13
diff -u -9 -p -r1.13 openid_test.module
--- modules/openid/tests/openid_test.module	23 Mar 2010 21:14:49 -0000	1.13
+++ modules/openid/tests/openid_test.module	5 Apr 2010 22:38:54 -0000
@@ -139,19 +139,19 @@ function openid_test_yadis_x_xrds_locati
 }
 
 /**
  * Menu callback; regular HTML page with <meta> element.
  */
 function openid_test_yadis_http_equiv() {
   $element = array(
     '#tag' => 'meta',
     '#attributes' => array(
-      'http-equiv' => 'X-XRDS-Location',
+      'http-equiv' => "X-XRDS-Location\n",
       'content' => url('openid-test/yadis/xrds', array('absolute' => TRUE)),
     ),
   );
   drupal_add_html_head($element, 'openid_test_yadis_http_equiv');
   return t('This page includes a &lt;meta equiv=...&gt; element containing the URL of an XRDS document.');
 }
 
 /**
  * Menu callback; regular HTML page with OpenID 1.0 <link> element.
@@ -159,19 +159,20 @@ function openid_test_yadis_http_equiv() 
 function openid_test_html_openid1() {
   drupal_add_html_head_link(array('rel' => 'openid.server', 'href' => url('openid-test/endpoint', array('absolute' => TRUE))));
   return t('This page includes a &lt;link rel=...&gt; element containing the URL of an OpenID Provider Endpoint.');
 }
 
 /**
  * Menu callback; regular HTML page with OpenID 2.0 <link> element.
  */
 function openid_test_html_openid2() {
-  drupal_add_html_head_link(array('rel' => 'openid2.provider', 'href' => url('openid-test/endpoint', array('absolute' => TRUE))));
+  // Use unusual values in order to test proper parsing of HTML attributes.
+  drupal_add_html_head_link(array('rel' => "foo\nopenid2.PROVIDER\tbar", 'href' => "\n" . url('openid-test/endpoint', array('absolute' => TRUE))));
   return t('This page includes a &lt;link rel=...&gt; element containing the URL of an OpenID Provider Endpoint.');
 }
 
 /**
  * Menu callback; OpenID Provider Endpoint.
  *
  * It accepts "associate" requests directly from the Relying Party, and
  * "checkid_setup" requests made by the user's browser based on HTTP redirects
  * (in OpenID 1) or HTML forms (in OpenID 2) generated by the Relying Party.
