OpenIdCredentials.class.php

Go to the documentation of this file.
00001 <?php
00002 /***************************************************************************
00003  *   Copyright (C) 2007 by Anton E. Lebedevich                             *
00004  *                                                                         *
00005  *   This program is free software; you can redistribute it and/or modify  *
00006  *   it under the terms of the GNU Lesser General Public License as        *
00007  *   published by the Free Software Foundation; either version 3 of the    *
00008  *   License, or (at your option) any later version.                       *
00009  *                                                                         *
00010  ***************************************************************************/
00011 
00015     final class OpenIdCredentials
00016     {
00017         const HEADER_CONT_TYPE = 'application/xrds+xml';
00018         const HEADER_XRDS_LOCATION = 'x-xrds-location';
00019         const HEADER_ACCEPT = 'text/html,application/xhtml+xml,application/xml,application/xrds+xml';
00020         const IDENTIFIER_SELECT = 'http://specs.openid.net/auth/2.0/identifier_select';
00021         
00022         private $claimedId  = null;
00023         private $realId     = null;
00024         private $server     = null;
00025         private $httpClient = null;
00026         private $isIdentifierSelect = false;
00027         
00028         public function __construct(
00029             HttpUrl $claimedId,
00030             HttpClient $httpClient
00031         )
00032         {
00033             $this->claimedId = $claimedId->makeComparable();
00034             
00035             if (!$claimedId->isValid())
00036                 throw new OpenIdException('invalid claimed id');
00037             
00038             $this->httpClient = $httpClient;
00039             
00040             $response = $httpClient->send(
00041                 HttpRequest::create()->
00042                 setHeaderVar('Accept', self::HEADER_ACCEPT)->
00043                 setMethod(HttpMethod::get())->
00044                 setUrl($claimedId)
00045             );
00046             
00047             if ($response->getStatus()->getId() != 200) {
00048                 throw new OpenIdException('can\'t fetch document');
00049             }
00050             
00051             $contentType = $response->getHeader('content-type');
00052             if (mb_stripos($contentType, self::HEADER_CONT_TYPE) !== false) {
00053                 $this->parseXRDS($response->getBody());
00054             } elseif ($response->hasHeader(self::HEADER_XRDS_LOCATION)) {
00055                 $this->loadXRDS($response->getHeader(self::HEADER_XRDS_LOCATION));
00056             } else {
00057                 $this->parseHTML($response->getBody());
00058             }
00059             
00060             if (!$this->server || !$this->server->isValid())
00061                 throw new OpenIdException('bad server');
00062             else
00063                 $this->server->makeComparable();
00064             
00065             if (!$this->realId)
00066                 $this->realId = $claimedId;
00067             elseif (!$this->realId->isValid())
00068                 throw new OpenIdException('bad delegate');
00069             else
00070                 $this->realId->makeComparable();
00071         }
00072         
00076         public static function create(
00077             HttpUrl $claimedId,
00078             HttpClient $httpClient
00079         )
00080         {
00081             return new self($claimedId, $httpClient);
00082         }
00083         
00087         public function getRealId()
00088         {
00089             if ($this->isIdentifierSelect()) {
00090                 return  HttpUrl::create()->parse(self::IDENTIFIER_SELECT);
00091             }
00092             
00093             return $this->realId;
00094         }
00095         
00099         public function getServer()
00100         {
00101             return $this->server;
00102         }
00103         
00104         public function isIdentifierSelect()
00105         {
00106             return $this->isIdentifierSelect;
00107         }
00108         
00112         public function setIdentifierSelect($bool)
00113         {
00114             $this->isIdentifierSelect = (bool) $bool;
00115             
00116             return $this;
00117         }
00118         
00119         protected function parseHTML($content)
00120         {
00121             $tokenizer = HtmlTokenizer::create(
00122                     StringInputStream::create($content)
00123                 )->
00124                 lowercaseTags(true)->
00125                 lowercaseAttributes(true);
00126             
00127             $insideHead = false;
00128             while ($token = $tokenizer->nextToken()) {
00129                 if (!$insideHead) {
00130                     if ($token instanceof SgmlOpenTag
00131                         && $token->getId() == 'head'
00132                     ) {
00133                         $insideHead = true;
00134                         continue;
00135                     }
00136                 }
00137                 
00138                 if ($insideHead) {
00139                     if ($token instanceof SgmlEndTag && $token->getId() == 'head')
00140                         break;
00141                     
00142                     if (
00143                         $token instanceof SgmlOpenTag
00144                         && $token->getId() == 'link'
00145                         && $token->hasAttribute('rel')
00146                         && $token->hasAttribute('href')
00147                     ) {
00148                         if ($token->getAttribute('rel') == 'openid.server')
00149                             $this->server = HttpUrl::create()->parse(
00150                                 $token->getAttribute('href')
00151                             );
00152                         
00153                         if ($token->getAttribute('rel') == 'openid.delegate')
00154                             $this->realId = HttpUrl::create()->parse(
00155                                 $token->getAttribute('href')
00156                             );
00157                     }
00158                     
00159                     if (
00160                         $token instanceof SgmlOpenTag
00161                         && $token->getId() == 'meta'
00162                         && $token->hasAttribute('content')
00163                         && $token->hasAttribute('http-equiv')
00164                         && mb_strtolower($token->getAttribute('http-equiv'))
00165                             == self::HEADER_XRDS_LOCATION
00166                     ) {
00167                         $this->loadXRDS($token->getAttribute('content'));
00168                     
00169                         return $this;
00170                     }
00171                 }
00172             }
00173             
00174             return $this;
00175         }
00176         
00177         protected function parseXRDS($content)
00178         {
00179             if (preg_match('|<URI>(.*?)</URI>|uis', $content, $match)) {
00180                 $this->server = HttpUrl::create()->parse($match[1]);
00181             }
00182             
00183             return $this;
00184         }
00185         
00186         protected function loadXRDS($url)
00187         {
00188             $response = $this->httpClient->send(
00189                 HttpRequest::create()->
00190                 setHeaderVar('Accept', self::HEADER_ACCEPT)->
00191                 setMethod(HttpMethod::get())->
00192                 setUrl(HttpUrl::create()->parse($url))
00193             );
00194             
00195             if ($response->getStatus()->getId() != 200) {
00196                 throw new OpenIdException('can\'t fetch document');
00197             }
00198             
00199             $this->parseXRDS($response->getBody());
00200             
00201             return $this;
00202         }
00203     }
00204 ?>