WordPress.org

Make WordPress Core

Ticket #7644: atom.diff

File atom.diff, 57.0 KB (added by cavemonkey50, 7 years ago)

The shared Atom API library. If committing MT before TypePad, this should be included as well.

  • class-atom-api.php

     
     1<?php 
     2 
     3/****************************************************************************\ 
     4                         PHP Atom API Package 
     5                             version 1.0 
     6 
     7         Includes AtomAPI,  AtomFeed,  AtomEntry and AtomRequest 
     8 
     9                     Written by Beau Lebens,  2005 
     10                       beau <at> dentedreality.com <dot> au 
     11              http://www.dentedreality.com.au/phpatomapi/ 
     12 
     13 
     14   More Atom Info; 
     15     AtomEnabled: http://www.atomenabled.org/ 
     16     Atom Wiki: http://www.intertwingly.net/wiki/pie/FrontPage 
     17     API Spec: http://www.atomenabled.org/developers/api/atom-api-spec.php 
     18     
     19   Blog-Specific API Details 
     20     Blogger: http://code.blogspot.com/archives/atom-docs.html 
     21     TypePad: http://sixapart.com/developers/atom/typepad/ 
     22 
     23   TODO 
     24     - Proper date handling (GMT,  local etc) 
     25     - AtomEntry::from_xml() needs work on author info handling - sample? 
     26     - Complete test suite/application? 
     27 
     28        @copyright (c) 2005, Beau Lebens 
     29        @license http://www.opensource.org/licenses/bsd-license.php  BSD 
     30\****************************************************************************/ 
     31 
     32// Error Numbers 
     33define('ATOMAPI_ENDPOINT_INVALID',         1); 
     34define('ATOMAPI_AUTHTYPE_INVALID',         2); 
     35define('ATOMAPI_AUTHENTICATION_FAILED',         3); 
     36define('ATOMAPI_NO_FEEDS',             4); 
     37define('ATOMAPI_NO_XML',             5); 
     38define('ATOMAPI_FEED_INIT_FAILED',         6); 
     39define('ATOMAPI_METHOD_INVALID',         7); 
     40define('ATOMAPI_PAYLOAD_INVALID',         8); 
     41define('ATOMAPI_REQUEST_INVALID',         9); 
     42define('ATOMAPI_CURL_ERROR',             10); 
     43 
     44// Error Strings 
     45$ATOMAPI_ERROR_STRINGS = array(1=>'The specified AtomAPI endpoint (or request URL) is invalid. Please enter a complete URL,  starting with \'http\'.', 
     46                            2=>'The PHPAtomAPI Package currently only supports \'Basic\' and \'WSSE\' authentication models. Please specify one of them.', 
     47                            3=>'Authentication against the AtomAPI endpoint failed. Please check your username/password.', 
     48                            4=>'No feeds were located at the specified AtomAPI endpoint.', 
     49                            5=>'No feeds were initiated because there was no XML available to parse.', 
     50                            6=>'No feeds were initiated because the FeedURI did not respond as expected.', 
     51                            7=>'An invalid method was supplied to an AtomRequest object. Methods available are GET,  PUT,  POST and DELETE', 
     52                            8=>'The payload supplied to an AtomRequest object appears to be something other than a string. Please only supply a string as payload.', 
     53                            9=>'An AtomRequest object was initiated incorrectly,  and is missing either a URI or a Method.', 
     54                            10=>'cURL failed to perform the required request within an AtomRequest.'); 
     55 
     56class AtomAPI { 
     57    var $endpoint; 
     58    var $authtype; 
     59    var $auth; 
     60    var $feeds; 
     61    var $feed_objs; 
     62    var $err_no; 
     63     
     64    /** 
     65    * @return AtomAPI/FALSE 
     66    * @param String $endpoint 
     67    * @param String $username 
     68    * @param String $password 
     69    * @param String $authtype 
     70    * @desc Creates an AtomAPI Object,  verifying authentication details by attempting to load a list of feeds. $authtype should be 'Basic' or 'WSSE' 
     71    */ 
     72    function AtomAPI($endpoint,  $username,  $password,  $authtype) { 
     73        // Abort if the endpoint is missing 
     74        if ($endpoint === false || !strlen($endpoint)) { 
     75            $this->err_no = ATOMAPI_ENDPOINT_INVALID; 
     76            return false; 
     77        } 
     78         
     79        // Set the endpoint and authtype,  then make sure they worked,  or die 
     80        $this->set_endpoint($endpoint); 
     81        if (!$this->get_endpoint()) { 
     82            return false; 
     83        } 
     84        $this->set_authtype($authtype,  $username,  $password); 
     85        if (!$this->get_auth()) { 
     86            return false; 
     87        } 
     88         
     89        // Verify authentication (and load feeds) 
     90        return $this->verify_auth(); 
     91    } 
     92     
     93    /** 
     94    * @return void 
     95    * @param String $uri 
     96    * @desc Set the endpoint for accessing this AtomAPI. Must be a fully-qualified URI starting with 'http' 
     97    */ 
     98    function set_endpoint($uri) { 
     99        if (substr($uri,  0,  4) == 'http') { 
     100            $this->endpoint = $uri; 
     101        } 
     102        else { 
     103            $this->err_no = ATOMAPI_ENDPOINT_INVALID; 
     104        } 
     105    } 
     106     
     107    /** 
     108    * @return String/FALSE 
     109    * @desc Return the endpoint set for this AtomAPI or FALSE if unset 
     110    */ 
     111    function get_endpoint() { 
     112        if (isset($this->endpoint)) { 
     113            return $this->endpoint; 
     114        } 
     115        else { 
     116            return false; 
     117        } 
     118    } 
     119     
     120    /** 
     121    * @return void 
     122    * @param String $type 
     123    * @param String $user 
     124    * @param String $pass 
     125    * @desc Creates the authentication object for this Atom API,  based on the type specified. Currently supports WSSE and Basic as $type 
     126    */ 
     127    function set_authtype($type,  $user,  $pass) { 
     128        $this->authtype = $type; 
     129         
     130        if ($type == 'Basic') { 
     131        //  require_once(dirname(__FILE__) . '/class.basicauth.php'); 
     132            $this->auth = new BasicAuth($user,  $pass); 
     133        } 
     134        else if ($type == 'WSSE') { 
     135        //  require_once(dirname(__FILE__) . '/class.wsse.php'); 
     136            $this->auth = new WSSE($user,  $pass); 
     137        } 
     138        else { 
     139            $this->err_no = ATOMAPI_AUTHTYPE_INVALID; 
     140        } 
     141    } 
     142     
     143    /** 
     144    * @return String 
     145    * @desc Returns the current authentication model (Basic or WSSE) 
     146    */ 
     147    function get_authtype() { 
     148        return $this->authtype; 
     149    } 
     150     
     151    /** 
     152    * @return Boolean 
     153    * @desc Verifies authentication details by attempting to access the endpoint. If successful,  initiates the list of available feeds and returns true,  otherwise returns false. 
     154    */ 
     155    function verify_auth() { 
     156        // Test authentication by accessing the endpoint with the auth object 
     157        $ar = new AtomRequest('GET',  $this->get_endpoint(),  $this->auth,  ''); 
     158        $response = $ar->exec(); 
     159         
     160        // Looks ok,  return the Object 
     161        if ($response == 200) { 
     162            $this->get_feeds($ar->get_response()); 
     163            return true; 
     164        } 
     165        // Not a 200 Ok response - authetication failed,  or endpoint incorrect 
     166        else { 
     167            $this->err_no = ATOMAPI_AUTHENTICATION_FAILED; 
     168            return false; 
     169        } 
     170    } 
     171     
     172    /** 
     173    * @return AuthenticationObject 
     174    * @desc Returns an authentication object (either WSSE or Basic) 
     175    */ 
     176    function get_auth() { 
     177        return $this->auth; 
     178    } 
     179     
     180    /** 
     181    * @return Array/String 
     182    * @desc Returns an array containing the details of the feeds listed at the endpoint. On failure,  returns the HTTPCode as a string instead 
     183    */ 
     184    function get_feeds($xml = false) { 
     185        // Feeds already fetched? Then just return the array again 
     186        if (is_array($this->feeds)) { 
     187            return $this->feeds; 
     188        } 
     189        // First time the feeds are requested,  so get the endpoint and build out the array 
     190        else { 
     191                        // Create an AtomRequest pointing to the AtomAPI endpoint (where we get a list of available feeds) 
     192                        $ar = new AtomRequest('GET',  $this->endpoint,  $this->auth,  ''); 
     193                        $code = $ar->exec(); 
     194                        if ($code) { 
     195                            $feeds_raw = $ar->get_response(); 
     196                                return $feeds_raw; 
     197                        } 
     198                        else { 
     199                            return false; 
     200                        } 
     201        } 
     202    } 
     203     
     204    /** 
     205    * @return AtomFeed 
     206    * @param Int $id 
     207    * @desc Creates (if required) and returns an AtomFeed object,  based on an $id for the feeds array (pulled from endpoint) 
     208    */ 
     209    function get_feed($id) { 
     210        // Build the feeds array if it's not already initiated 
     211        if (!sizeof($this->feeds)) { 
     212            $this->get_feeds(); 
     213        } 
     214         
     215        // And create this AtomFeed if it's not there 
     216        if (!is_object($this->feed_objs[$id])) { 
     217            $this->feed_objs[$id] = new AtomFeed($this->feeds[$id]['service.feed'],  $this->get_auth()); 
     218        } 
     219         
     220        // Return false if there's still no object 
     221        if (!is_object($this->feed_objs[$id])) { 
     222            return false; 
     223        } 
     224        // Return the requested AtomFeed 
     225        else { 
     226            return $this->feed_objs[$id]; 
     227        } 
     228    } 
     229     
     230    /** 
     231    * @return Array/String 
     232    * @param Int $id 
     233    * @desc Creates an AtomFeed object for the details specified in $id,  and gets the entries available. Stores the object internally for later 
     234    */ 
     235    function get_entries($id) { 
     236        // If the AtomFeed where entries are requested from doesn't exist,  create it first 
     237        if (!is_object($this->feed_objs[$id])) { 
     238            $this->feed_objs[$id] = new AtomFeed($this->feeds[$id]['service.feed'],  $this->auth); 
     239        } 
     240         
     241        // Return false if there's still no object 
     242        if (!is_object($this->feed_objs[$id])) { 
     243            return false; 
     244        } 
     245        // Request all entries available from this AtomFeed and return whatever comes back 
     246        else { 
     247            $entries = $this->feed_objs[$id]->get_entries(); 
     248            return $entries; 
     249        } 
     250    } 
     251     
     252    /** 
     253    * @return Int/FALSE 
     254    * @desc Returns the last error registered in this class,  or FALSE if none 
     255    */ 
     256    function error() { 
     257        if (isset($this->err_no) && is_int($this->err_no)) { 
     258            return $this->err_no; 
     259        } 
     260        else { 
     261            return false; 
     262        } 
     263    } 
     264} 
     265 
     266 
     267 
     268class AtomFeed { 
     269    var $endpoint; 
     270    var $auth; 
     271    var $title = array(); 
     272    var $author = array(); 
     273    var $version; 
     274    var $links = array(); 
     275    var $tagline = array(); 
     276    var $id; 
     277    var $generator = array(); 
     278    var $info = array(); 
     279    var $modified; 
     280    var $entries = array(); 
     281    var $err_no; 
     282     
     283    /** 
     284    * @return AtomFeed 
     285    * @param String $endpoint 
     286    * @param AuthObj $auth 
     287    * @param String $xml 
     288    * @desc Creates an AtomFeed object,  based on the details specified. If $xml is provided,  then it is parsed to populate the object,  otherwise the endpoint is accessed and it is populated from the response XML. If all values are false or omitted,  then an empty AtomFeed is created,  and awaits the use of set_*() functions to set it up manually. 
     289    */ 
     290    function AtomFeed($endpoint = false,  $auth = false,  $xml = false) { 
     291        // If they are set,  then store the endpoint and auth object 
     292        if ($endpoint && $auth) { 
     293            $this->set_endpoint($endpoint); 
     294            $this->set_auth($auth); 
     295        } 
     296         
     297        // If the object was created with XML,  populate it with parsed values 
     298        if ($xml !== false) { 
     299            $this->from_xml($xml); 
     300        } 
     301        // No XML? If there's an endpoint and auth object,  then get some XML to populate with 
     302        else if ($endpoint && $auth) { 
     303            $res = $this->init(); 
     304            // >= 300 is not a good http status code to get back 
     305            if ($res >= 300) { 
     306                return false; 
     307            } 
     308        } 
     309        else { 
     310            $this->err_no = ATOMAPI_FEED_INIT_FAILED; 
     311            return false; 
     312        } 
     313    } 
     314     
     315    /** 
     316    * @return void 
     317    * @param AuthObj $auth 
     318    * @desc Sets the authentication object to use for this AtomFeed 
     319    */ 
     320    function set_auth($auth) { 
     321        if (is_object($auth)) { 
     322            $this->auth = $auth; 
     323        } 
     324        else { 
     325            $this->err_no = ATOMAPI_AUTHTYPE_INVALID; 
     326        } 
     327    } 
     328     
     329    /** 
     330    * @return AuthObj/FALSE 
     331    * @desc Returns the currently set authentication object for this AtomFeed,  or FALSE if not set 
     332    */ 
     333    function get_auth() { 
     334        if (is_object($this->auth)) { 
     335            return $this->auth; 
     336        } 
     337        else { 
     338            return false; 
     339        } 
     340    } 
     341     
     342    /** 
     343    * @return void 
     344    * @param String $uri 
     345    * @desc Set the endpoint for accessing this AtomFeed. Must be a fully-qualified URI starting with 'http' 
     346    */ 
     347    function set_endpoint($uri) { 
     348        if (substr($uri,  0,  4) == 'http') { 
     349            $this->endpoint = $uri; 
     350        } 
     351        else { 
     352            $this->err_no = ATOMAPI_ENDPOINT_INVALID; 
     353        } 
     354    } 
     355     
     356    /** 
     357    * @return String/FALSE 
     358    * @desc Returns the endpoint for this AtomFeed if specified,  or FALSE if not 
     359    */ 
     360    function get_endpoint() { 
     361        if (isset($this->endpoint) && strlen($this->endpoint)) { 
     362            return $this->endpoint; 
     363        } 
     364        else { 
     365            return false; 
     366        } 
     367    } 
     368     
     369    /** 
     370    * @return void 
     371    * @param String $v 
     372    * @desc Set the version number for this AtomFeed (goes in the <feed> element) 
     373    */ 
     374    function set_version($v) { 
     375        $this->version = $v; 
     376    } 
     377     
     378    /** 
     379    * @return String 
     380    * @desc Returns the version number specified for this AtomFeed in the <feed> element 
     381    */ 
     382    function get_version() { 
     383        if (isset($this->version)) { 
     384            return $this->version; 
     385        } 
     386        else { 
     387            return false; 
     388        } 
     389    } 
     390     
     391    /** 
     392    * @return void 
     393    * @param String $i 
     394    * @desc Sets the <id> element contents for the AtomFeed. 
     395    */ 
     396    function set_id($i) { 
     397        $this->id = $i; 
     398    } 
     399     
     400    /** 
     401    * @return String 
     402    * @desc Returns the contents of the <id> element for this AtomFeed 
     403    */ 
     404    function get_id() { 
     405        if (isset($this->id)) { 
     406            return $this->id; 
     407        } 
     408        else { 
     409            return false; 
     410        } 
     411    } 
     412     
     413    /** 
     414    * @return void 
     415    * @param Array/String $title 
     416    * @desc Sets the title for this AtomFeed. 
     417    */ 
     418    function set_title($title) { 
     419        if (is_array($title)) { 
     420            foreach ($title as $key=>$val) { 
     421                if (strlen($key) && strlen($val)) { 
     422                    $this->title[$key] = $val; 
     423                } 
     424            } 
     425        } 
     426        else if (is_string($title)) { 
     427            $this->title['title'] = $title; 
     428        } 
     429    } 
     430     
     431    /** 
     432    * @return Array/String/FALSE 
     433    * @param String $elem 
     434    * @desc Returns either a specific element of the title,  or the entire array of data if none is specified. 
     435    */ 
     436    function get_title($elem = false) { 
     437        if ($elem === false) { 
     438            return $this->title; 
     439        } 
     440        else { 
     441            if (in_array($elem,  array_keys($this->title))) { 
     442                return $this->title[$elem]; 
     443            } 
     444            else { 
     445                return false; 
     446            } 
     447        } 
     448    } 
     449     
     450    /** 
     451    * @return void 
     452    * @param Array/String $tagline 
     453    * @desc Sets the details of the tagline for this AtomFeed 
     454    */ 
     455    function set_tagline($tagline) { 
     456        if (is_array($tagline)) { 
     457            foreach ($tagline as $key=>$val) { 
     458                $this->tagline[$key] = $val; 
     459            } 
     460        } 
     461        else if (is_string($tagline)) { 
     462            $this->tagline['tagline'] = $tagline; 
     463        } 
     464    } 
     465     
     466    /** 
     467    * @return Array/String/FALSE 
     468    * @param String $elem 
     469    * @desc Returns either a specific element of the tagline,  or the entire array of data if none is specified. 
     470    */ 
     471    function get_tagline($elem = false) { 
     472        if ($elem === false) { 
     473            return $this->tagline; 
     474        } 
     475        else { 
     476            if (in_array($elem,  array_keys($this->tagline))) { 
     477                return $this->tagline[$elem]; 
     478            } 
     479            else { 
     480                return false; 
     481            } 
     482        } 
     483    } 
     484     
     485    /** 
     486    * @return void 
     487    * @param Array $gen 
     488    * @desc Sets the details of the generator for this AtomFeed. 
     489    */ 
     490    function set_generator($gen) { 
     491        if (is_array($gen)) { 
     492            foreach ($gen as $key=>$val) { 
     493                $this->generator[$key] = $val; 
     494            } 
     495        } 
     496    } 
     497     
     498    /** 
     499    * @return void 
     500    * @param String $date 
     501    * @desc Set the modified date for this feed 
     502    */ 
     503    function set_modified($date) { 
     504        if (preg_match('/(\d{4})-(\d{2})-(\d{2}).(\d{2}):(\d{2}):(\d{2})-(\d{2}):(\d{2})/',  $date,  $parts) || preg_match('/(\d{4})-(\d{2})-(\d{2}).(\d{2}):(\d{2}):(\d{2})Z/',  $date,  $parts)) { 
     505            $this->modified = $date; 
     506        } 
     507    } 
     508     
     509    /** 
     510    * @return String/FALSE 
     511    * @desc Get the modified date for this feed,  or FALSE if it's not set 
     512    */ 
     513    function get_modified() { 
     514        if (isset($this->modified) && strlen($this->modified)) { 
     515            return $this->modified; 
     516        } 
     517    } 
     518     
     519    /** 
     520    * @return void 
     521    * @param Array/String $info 
     522    * @desc Set the info element details. If a string is passed in,  then defaults to the info contents 
     523    */ 
     524    function set_info($info) { 
     525        if (is_array($info)) { 
     526            foreach ($info as $key=>$val) { 
     527                $this->info[$key] = $val; 
     528            } 
     529        } 
     530        else { 
     531            $this->info['info'] = $info; 
     532        } 
     533    } 
     534     
     535    /** 
     536    * @return Array/String/FALSE 
     537    * @desc Returns either the requested individual attribute of the info element,  the entire array,  or FALSE if it's not set. 
     538    */ 
     539    function get_info($elem = false) { 
     540        if ($elem === false) { 
     541            return $this->info; 
     542        } 
     543        else { 
     544            if (in_array($elem,  array_keys($this->info))) { 
     545                return $this->info[$elem]; 
     546            } 
     547            else { 
     548                return false; 
     549            } 
     550        } 
     551    } 
     552     
     553    /** 
     554    * @return Array/String/FALSE 
     555    * @param String $elem 
     556    * @desc Returns either a specific element of the generator,  or the entire array of data if none is specified. 
     557    */ 
     558    function get_generator($elem = false) { 
     559        if ($elem === false) { 
     560            return $this->generator; 
     561        } 
     562        else { 
     563            if (in_array($elem,  array_keys($this->generator))) { 
     564                return $this->generator[$elem]; 
     565            } 
     566            else { 
     567                return false; 
     568            } 
     569        } 
     570    } 
     571     
     572    /** 
     573    * @return void 
     574    * @param String $href 
     575    * @param String $rel 
     576    * @param String $title 
     577    * @param String $type 
     578    * @desc Adds a link to the links array with the details as per the associative array passed in. Requires href,  rel,  title and type 
     579    */ 
     580    function add_link($href,  $rel,  $title = '',  $type) { 
     581        if (strlen($href) && strlen($rel) && strlen($type)) { 
     582            $this->links[] = array('href'=>$href,  'rel'=>$rel,  'title'=>$title,  'type'=>$type); 
     583        } 
     584    } 
     585     
     586    /** 
     587    * @return Array 
     588    * @param String $key 
     589    * @param String $val 
     590    * @desc Filters the <link>s of this AtomFeed for ones where the $key matches the $val and returns an array of them,  otherwise returns all links 
     591    */ 
     592    function get_links($key = false,  $val = false) { 
     593        // If key and val are specified,  then filter the array for entries where the specified key == the value 
     594        if ($key !== false && $val !== false) { 
     595            $out = array(); 
     596            foreach ($this->links as $link) { 
     597                if ($link[$key] == $val) { 
     598                    $out[] = $link; 
     599                } 
     600            } 
     601        } 
     602        else { 
     603            $out = $this->links; 
     604        } 
     605         
     606        return $out; 
     607    } 
     608     
     609    /** 
     610    * @return Array/FALSE 
     611    * @desc Returns an array containing AtomEntries for each entry in this AtomFeed,  or FALSE if none are set 
     612    */ 
     613    function get_entries() { 
     614        if (sizeof($this->entries)) { 
     615            return $this->entries; 
     616        } 
     617        else { 
     618            return false; 
     619        } 
     620    } 
     621     
     622    /** 
     623    * @return void 
     624    * @param AtomEntry $entry 
     625    * @desc Add an AtomEntry object to the array of entries that make up this AtomFeed 
     626    */ 
     627    function add_entry($entry) { 
     628        if (is_object($entry)) { 
     629            $this->entries[] = $entry; 
     630        } 
     631    } 
     632     
     633    /** 
     634    * @return TRUE/Int on error 
     635    * @desc Initiates the AtomFeed object from a URI by loading the data,  then parsing it as XML. Returns an HTTP response code on error,  or TRUE if successful. 
     636    */ 
     637    function init() { 
     638        $ar = new AtomRequest('GET',  $this->get_endpoint(),  $this->get_auth()); 
     639        $code = $ar->exec(); 
     640         
     641        // Successfully retrieved feed,  now process it out 
     642        if ($code == 200) { 
     643            $this->from_xml($ar->get_response()); 
     644            return true; 
     645        } 
     646        // Not a clean result,  return the error 
     647        else { 
     648            $this->err_no = ATOMAPI_FEED_INIT_FAILED; 
     649            return $code; 
     650        } 
     651    } 
     652     
     653    /** 
     654    * @return Array/FALSE 
     655    * @param String $str 
     656    * @desc Parses an XML/HTML string and returns an associative array containing key/value pairs 
     657    */ 
     658    function extract_attribs($str) { 
     659        $out = array(); 
     660        if (preg_match_all('/([^ =]+)="([^"]*)"/si',  $str,  $attribs)) { 
     661            foreach ($attribs[1] as $c=>$key) { 
     662                $out[$key] = $attribs[2][$c]; 
     663            } 
     664        } 
     665        return $out; 
     666    } 
     667     
     668    /** 
     669    * @return void 
     670    * @param String $xml 
     671    * @desc Populates the variables of the AtomFeed,  based on a complete XML representation of it. 
     672    */ 
     673    function from_xml($xml) { 
     674        $orig_xml = $xml; 
     675         
     676        // Strip down the XML to just the part we want to work with 
     677        if (preg_match('/(<feed.*)<entry/sUi',  $xml,  $feed_xml)) { 
     678            $xml = $feed_xml[1]; 
     679        } 
     680         
     681        // ATOM FEED VERSION 
     682        if (preg_match('/<feed[^>]+version="([^"]*)"/is',  $xml,  $ver)) { 
     683            $this->set_version($ver[1]); 
     684        } 
     685         
     686        // TITLE 
     687        if (preg_match_all('/<title([^>]*)>(.*)<\/title>/sUi',  $xml,  $title)) { 
     688            $title_attribs = $this->extract_attribs($title[1][0]); 
     689            $title = array('title'=>$title[2][0]); 
     690            $title = array_merge_recursive($title,  $title_attribs); 
     691            $this->set_title($title); 
     692        } 
     693         
     694        // TAGLINE 
     695        if (preg_match_all('/<tagline([^>]*)>(.*)<\/tagline>/sUi',  $xml,  $tagline)) { 
     696            $tagline_attribs = $this->extract_attribs($tagline[1][0]); 
     697            $tagline = array('tagline'=>$tagline[2][0]); 
     698            $tagline = array_merge_recursive($tagline,  $tagline_attribs); 
     699            $this->set_tagline($tagline); 
     700        } 
     701         
     702        // ID 
     703        if (preg_match('/<id>([^<]*)<\/id>/is',  $xml,  $id)) { 
     704            $this->set_id($id[1]); 
     705        } 
     706         
     707        // INFO 
     708        if (preg_match('/<info([^>]+)>(.*)<\/info>/is',  $xml,  $info)) { 
     709            $info_attribs = $this->extract_attribs($info[1]); 
     710            $info = array('info'=>$info[2]); 
     711            $info = array_merge_recursive($info,  $info_attribs); 
     712            $this->set_info($info); 
     713        } 
     714         
     715        // MODIFIED 
     716        if (preg_match('/<modified>([^<]*)<\/modified>/is',  $xml,  $modified)) { 
     717            $this->set_modified($modified[1]); 
     718        } 
     719         
     720        // GENERATOR 
     721        if (preg_match_all('/<generator([^>]*)>(.*)<\/generator>/sUi',  $xml,  $generator)) { 
     722            $generator_attribs = $this->extract_attribs($generator[1][0]); 
     723            $generator = array('generator'=>$generator[2][0]); 
     724            $generator = array_merge_recursive($generator,  $generator_attribs); 
     725            $this->set_generator($generator); 
     726        } 
     727         
     728        // LINKS 
     729        if (preg_match_all('/<link([^>]+)>/Ui',  $xml,  $links)) { 
     730            foreach ($links[1] as $link) { 
     731                $link = $this->extract_attribs($link); 
     732                $this->add_link($link['href'],  $link['rel'],  $link['title'],  $link['type']); 
     733            } 
     734        } 
     735         
     736        // Handle all of the entries,  creating AtomEntry objects and linking them 
     737        preg_match_all('/(<entry[^>]*>.*<\/entry>)/sUi',  $orig_xml,  $entries_raw); 
     738        if (strlen($entries_raw[0][0])) { 
     739            foreach ($entries_raw[1] as $e=>$entry) { 
     740                $ae = new AtomEntry(); 
     741                $ae->from_xml($entry); 
     742                if ($ae) { 
     743                    $this->add_entry($ae); 
     744                } 
     745            } 
     746        } 
     747    } 
     748     
     749    /** 
     750    * @return String 
     751    * @desc Returns an XML/String representation of the entire AtomFeed,  including header values and all AtomEntries found in $this->entries. 
     752    */ 
     753    function to_xml() { 
     754        $xml = '<?xml version="1.0" encoding="utf-8"?>' . "\n\n"; 
     755         
     756        $xml .= '<feed'; 
     757         
     758        // VERSION 
     759        $ver = $this->get_version(); 
     760        if ($ver) { 
     761            $xml .= ' version="' . $ver . '"'; 
     762        } 
     763         
     764        $xml .= ' xmlns="http://purl.org/atom/ns#">' . "\n"; 
     765         
     766        // LINKS 
     767        $links = $this->get_links(); 
     768        if (sizeof($links)) { 
     769            foreach ($links as $link) { 
     770                $xml .= '<link'; 
     771                foreach ($link as $attrib=>$val) { 
     772                     if (strlen($attrib) && strlen($val)) { 
     773                         $xml .= ' ' . $attrib . '="' . utf8_encode($val) . '"'; 
     774                     } 
     775                } 
     776                $xml .= '/>' . "\n"; 
     777            } 
     778        } 
     779         
     780        // TITLE 
     781        $title = $this->get_title(); 
     782        if (sizeof($title)) { 
     783            $xml .= '<title'; 
     784            foreach ($title as $key=>$val) { 
     785                if ($key != 'title') { 
     786                    $xml .= ' ' . $key . '="' . $val . '"'; 
     787                } 
     788            } 
     789            $xml .= '>' . utf8_encode($title['title']) . '</title>' . "\n"; 
     790        } 
     791         
     792        // TAGLINE 
     793        $tag = $this->get_tagline(); 
     794        if (sizeof($tag)) { 
     795            $xml .= '<tagline'; 
     796            foreach ($tag as $key=>$val) { 
     797                if ($key != 'tagline') { 
     798                    $xml .= ' ' . $key . '="' . $val . '"'; 
     799                } 
     800            } 
     801            $xml .= '>' . utf8_encode($tag['tagline']) . '</tagline>' . "\n"; 
     802        } 
     803         
     804        // ID 
     805        $id = $this->get_id(); 
     806        if ($id) { 
     807            $xml .= '<id>' . $id . '</id>' . "\n"; 
     808        } 
     809         
     810        // MODIFIED 
     811        $mod = $this->get_modified(); 
     812        if ($mod) { 
     813            $xml .= '<modified>' . $mod . '</modified>' . "\n"; 
     814        } 
     815         
     816         
     817        // GENERATOR 
     818        $gen = $this->get_generator(); 
     819        if (is_array($gen) && sizeof($gen)) { 
     820            $xml .= '<generator url="' . $gen['url'] . '" version="' . $gen['version'] . '">' . $gen['generator'] . '</generator>' . "\n"; 
     821        } 
     822         
     823        // INFO 
     824        $info = $this->get_info(); 
     825        if (sizeof($info)) { 
     826            $xml .= '<info'; 
     827            foreach ($info as $key=>$val) { 
     828                if ($key != 'info') { 
     829                    $xml .= ' ' . $key . '="' . $val . '"'; 
     830                } 
     831            } 
     832            $xml .= '>' . utf8_encode($info['info']) . '</info>' . "\n"; 
     833        } 
     834         
     835        // ENTRIES 
     836        $entries = $this->get_entries(); 
     837        if ($entries) { 
     838            foreach ($entries as $entry) { 
     839                $xml .= $entry->to_xml('FEED'); 
     840            } 
     841        } 
     842         
     843        $xml .= '</feed>'; 
     844         
     845        return $xml; 
     846    } 
     847     
     848    /** 
     849    * @return Int/FALSE 
     850    * @desc Returns the last error registered in this class,  or FALSE if none 
     851    */ 
     852    function error() { 
     853        if (isset($this->err_no) && is_int($this->err_no)) { 
     854            return $this->err_no; 
     855        } 
     856        else { 
     857            return false; 
     858        } 
     859    }     
     860} 
     861 
     862 
     863 
     864class AtomEntry { 
     865    var $links = array(); 
     866    var $title = array(); 
     867    var $content = array(); 
     868    var $author = array(); 
     869    var $generator = array(); 
     870    var $issued; 
     871    var $modified; 
     872    var $created; 
     873    var $id; 
     874    var $err_no; 
     875     
     876    /** 
     877    * @return AtomEntry 
     878    * @param Array $title 
     879    * @param Array $content 
     880    * @param Array $author 
     881    * @param String $issued 
     882    * @param String $modified 
     883    * @param String $created 
     884    * @desc Creates an AtomEntry,  with optional initial values. 
     885    */ 
     886    function AtomEntry($title = array(),  $content = array()) { 
     887        $this->set_title($title); 
     888        $this->set_content($content); 
     889         
     890        // Default timezone is that of the current server (for dates) 
     891        $this->tz = date('O'); 
     892    } 
     893     
     894    /** 
     895    * @return void 
     896    * @param Array $title 
     897    * @desc Sets the title details for the Entry,  including some defaults if ommitted. FORMAT: array('title'=>$title); 
     898    */ 
     899    function set_title($title) { 
     900        if (is_array($title)) { 
     901            foreach ($title as $key=>$val) { 
     902                if (strlen($key) && strlen($val)) { 
     903                    $this->title[$key] = $val; 
     904                } 
     905            } 
     906        } 
     907        else if (is_string($title)) { 
     908            $this->title['title'] = $title; 
     909        } 
     910    } 
     911     
     912    /** 
     913    * @return String/Array/FALSE 
     914    * @param String $elem 
     915    * @desc Returns either the requested value,  or all details of the $title 
     916    */ 
     917    function get_title($elem = false) { 
     918        if ($elem === false) { 
     919            return $this->title; 
     920        } 
     921        else { 
     922            if (in_array($elem,  array_keys($this->title))) { 
     923                return $this->title[$elem]; 
     924            } 
     925            else { 
     926                return false; 
     927            } 
     928        } 
     929    } 
     930     
     931    /** 
     932    * @return void 
     933    * @param Array $content 
     934    * @desc Sets the content details for the Entry,  including some defaults if ommitted. FORMAT: array('content'=>$content); Also removes default <DIV> from Blogger entries to avoid PUT problems 
     935    */ 
     936    function set_content($content) { 
     937        if (is_array($content)) { 
     938            foreach ($content as $key=>$val) { 
     939                $this->content[$key] = $val; 
     940            } 
     941        } 
     942        else if (is_string($content)) { 
     943            $this->content['content'] = $content; 
     944        } 
     945    } 
     946     
     947    /** 
     948    * @return Array/String/FALSE 
     949    * @param String $elem 
     950    * @desc Returns the contents of this entry - including attributes of the content element 
     951    */ 
     952    function get_content($elem = false) { 
     953        if ($elem === false) { 
     954            return $this->content; 
     955        } 
     956        else { 
     957            if (in_array($elem,  array_keys($this->content))) { 
     958                return $this->content[$elem]; 
     959            } 
     960            else { 
     961                return false; 
     962            } 
     963        } 
     964    } 
     965     
     966    /** 
     967    * @return void 
     968    * @param Array $author 
     969    * @desc Set all details relating to the author (name,  email,  url) 
     970    */ 
     971    function set_author($author) { 
     972        if (is_array($author)) { 
     973            foreach ($author as $key=>$val) { 
     974                $this->author[$key] = $val; 
     975            } 
     976        } 
     977        else if (is_string($author)) { 
     978            $this->author['name'] = $author; 
     979        } 
     980    } 
     981     
     982    /** 
     983    * @return Array/String/FALSE 
     984    * @param String $elem 
     985    * @desc Returns author details - either item requested,  or all. 
     986    */ 
     987    function get_author($elem = false) { 
     988        if ($elem === false) { 
     989            return $this->author; 
     990        } 
     991        else { 
     992            if (in_array($elem,  array_keys($this->author))) { 
     993                return $this->author[$elem]; 
     994            } 
     995            else { 
     996                return false; 
     997            } 
     998        } 
     999    } 
     1000     
     1001    /** 
     1002    * @return void 
     1003    * @param String $date 
     1004    * @desc Set the <issued> date,  after sanitization 
     1005    */ 
     1006    function set_issued($date) { 
     1007        $this->issued = $this->sanitize_date($date); 
     1008    } 
     1009     
     1010    /** 
     1011    * @return String 
     1012    * @desc Returns the <issued> date for the entry 
     1013    */ 
     1014    function get_issued() { 
     1015        if (isset($this->issued)) { 
     1016            return $this->issued; 
     1017        } 
     1018        else { 
     1019            return false; 
     1020        } 
     1021    } 
     1022     
     1023    /** 
     1024    * @return void 
     1025    * @param String $date 
     1026    * @desc Set the <modified> date,  after sanitization 
     1027    */ 
     1028    function set_modified($date) { 
     1029        $this->modified = $this->sanitize_date($date); 
     1030    } 
     1031     
     1032    /** 
     1033    * @return String/FALSE 
     1034    * @desc Returns the <modified> date for the entry 
     1035    */ 
     1036    function get_modified() { 
     1037        if (isset($this->modified)) { 
     1038            return $this->modified; 
     1039        } 
     1040        else { 
     1041            return false; 
     1042        } 
     1043    } 
     1044     
     1045    /** 
     1046    * @return void 
     1047    * @param String $date 
     1048    * @desc Set the <created> date,  after sanitization 
     1049    */ 
     1050    function set_created($date) { 
     1051        $this->created = $this->sanitize_date($date); 
     1052    } 
     1053     
     1054    /** 
     1055    * @return String/FALSE 
     1056    * @desc Returns the <created> date for the entry 
     1057    */ 
     1058    function get_created() { 
     1059        if (isset($this->created)) { 
     1060            return $this->created; 
     1061        } 
     1062        else { 
     1063            return false; 
     1064        } 
     1065    } 
     1066     
     1067    /** 
     1068    * @return void 
     1069    * @param String $id 
     1070    * @desc Set the unique <id> for the Entry 
     1071    */ 
     1072    function set_id($id) { 
     1073        $this->id = $id; 
     1074    } 
     1075     
     1076    /** 
     1077    * @return String/FALSE 
     1078    * @desc Returns the unique <id> for this Entry 
     1079    */ 
     1080    function get_id() { 
     1081        if (isset($this->id)) { 
     1082            return $this->id; 
     1083        } 
     1084        else { 
     1085            return false; 
     1086        } 
     1087    } 
     1088     
     1089    /** 
     1090    * @return void 
     1091    * @param String $href 
     1092    * @param String $rel 
     1093    * @param String $title 
     1094    * @param String $type 
     1095    * @desc Adds a <link> to the array for this Entry. href,  type,  title and rel are all required 
     1096    */ 
     1097    function add_link($href,  $rel,  $title,  $type) { 
     1098        if (strlen($href) && strlen($rel) && strlen($type)) { 
     1099            $this->links[] = array('href'=>$href,  'rel'=>$rel,  'title'=>$title,  'type'=>$type); 
     1100        } 
     1101    } 
     1102     
     1103    /** 
     1104    * @return Array 
     1105    * @param String $key 
     1106    * @param String $val 
     1107    * @desc Returns the links where $key matches $val if specified,  or all links if both ommitted 
     1108    */ 
     1109    function get_links($key = false,  $val = false) { 
     1110        // If key and val are specified,  then filter the array for entries where the specified key == the value 
     1111        if ($key !== false && $val !== false) { 
     1112            $out = array(); 
     1113            foreach ($this->links as $link) { 
     1114                if ($link[$key] == $val) { 
     1115                    $out[] = $link; 
     1116                } 
     1117            } 
     1118        } 
     1119        else { 
     1120            $out = $this->links; 
     1121        } 
     1122         
     1123        return $out; 
     1124    } 
     1125     
     1126    function sanitize_date($date) { 
     1127        if ($date == '' || $date === false) { 
     1128            return false; 
     1129        } 
     1130        else if (!stristr($date,  'T') || !stristr($date,  'Z')) { 
     1131            if (preg_match('/(\d{4})-(\d{2})-(\d{2}).(\d{2}):(\d{2}):(\d{2})-(\d{2}):(\d{2})/',  $date,  $parts)) { 
     1132                return $date; 
     1133            } 
     1134            else if (preg_match('/(\d{4})-(\d{2})-(\d{2}).(\d{2}):(\d{2}):(\d{2})/',  $date,  $parts)) { 
     1135                return $date; 
     1136            } 
     1137            return false; 
     1138        } 
     1139        else { 
     1140            return $date; 
     1141        } 
     1142    } 
     1143     
     1144    function atom_date($date,  $tz) { 
     1145        if ($date == '' || $date === false) { 
     1146            preg_match('/(\+|-)(\d{2}):(\d{2})/',  $tz,  $tz_parts); 
     1147            return gmdate('Y-m-d\TH:i:s\Z',  mktime(eval("date('H') " . $tz_parts[1] . " " . $tz_parts[2] . ";"),  eval("date('i') " . $tz_parts[1] . " " . $tz_parts[3] . ";"),  date('s'),  date('m'),  date('d'),  date('Y'))); 
     1148        } 
     1149        else { 
     1150            return $date; 
     1151        } 
     1152    } 
     1153     
     1154    /** 
     1155    * @return Array 
     1156    * @param String $str 
     1157    * @desc Parses an XML/HTML string and returns an associative array containing key/value pairs 
     1158    */ 
     1159    function extract_attribs($str) { 
     1160        $out = array(); 
     1161        if (preg_match_all('/([^ =]+)="([^"]*)"/si',  $str,  $attribs)) { 
     1162            foreach ($attribs[1] as $c=>$key) { 
     1163                $out[$key] = $attribs[2][$c]; 
     1164            } 
     1165        } 
     1166                 
     1167        return $out; 
     1168    } 
     1169     
     1170    /** 
     1171    * @return void 
     1172    * @param String $xml 
     1173    * @desc Builds out the details of the object by parsing an XML <entry> 
     1174    */ 
     1175    function from_xml($xml) { 
     1176        // Parse an XML <entry> into its elements and then set them to this Object 
     1177        // LINKS 
     1178        if (preg_match_all('/<link([^>]+)>/Ui',  $xml,  $links)) { 
     1179            foreach ($links[1] as $link) { 
     1180                $link = $this->extract_attribs($link); 
     1181                $this->add_link($link['href'],  $link['rel'],  $link['title'],  $link['type']); 
     1182            } 
     1183        } 
     1184         
     1185        // AUTHOR----------------------------------------------------------------------------------------------* 
     1186        // Needs to handle the other elements properly - but need a sample to work from! 
     1187        if (preg_match('/<author>\s*(<([^>]+)>(.*)<\/\2>)*\s*<\/author>/sUi',  $xml,  $author)) { 
     1188            $this->set_author(array($author[2]=>$author[3])); 
     1189        } 
     1190         
     1191        // DATES 
     1192        if (preg_match('/<issued>([^>]*)<\/issued>/i',  $xml,  $date)) { 
     1193            $this->set_issued($date[1]); 
     1194        } 
     1195         
     1196        if (preg_match('/<modified>([^>]*)<\/modified>/i',  $xml,  $date)) { 
     1197            $this->set_modified($date[1]); 
     1198        } 
     1199         
     1200        if (preg_match('/<created>([^>]*)<\/created>/i',  $xml,  $date)) { 
     1201            $this->set_created($date[1]); 
     1202        } 
     1203         
     1204        // ID 
     1205        if (preg_match('/<id>([^>]*)<\/id>/i',  $xml,  $id)) { 
     1206            $this->set_id($id[1]); 
     1207        } 
     1208         
     1209        // TITLE 
     1210        if (preg_match_all('/<title([^>]*)>(.*)<\/title>/sUi',  $xml,  $title)) { 
     1211            $title_attribs = $this->extract_attribs($title[1][0]); 
     1212            $title = array('title'=>$title[2][0]); 
     1213            $title = array_merge_recursive($title,  $title_attribs); 
     1214            $this->set_title($title); 
     1215        } 
     1216         
     1217        // CONTENT 
     1218        if (preg_match_all('/<content([^>]*)>(.*)<\/content>/sUi',  $xml,  $content)) { 
     1219            $content_attribs = $this->extract_attribs($content[1][0]); 
     1220            $content = array('content'=>$content[2][0]); 
     1221            $content = array_merge_recursive($content,  $content_attribs); 
     1222            $this->set_content($content); 
     1223        } 
     1224    } 
     1225     
     1226    /** 
     1227    * @return String 
     1228    * @param String $purpose 
     1229    * @desc Creates an XML representation of this Entry. $purpose is [PUT|POST|FEED] and defines what you're going to do with this XML. $purpose modifies the elements included according to the Atom spec. 
     1230    */ 
     1231    function to_xml($purpose = 'POST') { 
     1232        $xml = ''; 
     1233         
     1234        // XML prolog if PUT or POST 
     1235        if ($purpose == 'PUT' || $purpose == 'POST') { 
     1236            $xml .= '<?xml version="1.0" encoding="utf-8"?>' . "\n"; 
     1237        } 
     1238 
     1239        // Start XML packet 
     1240        $xml .= '<entry xmlns="http://purl.org/atom/ns#">' . "\n"; 
     1241         
     1242        // GENERATOR 
     1243        if ($purpose == 'PUT' || $purpose == 'POST') { 
     1244            $xml .= '<generator version="0.1" url="http://www.dentedreality.com.au/phpatomapi/">Dented Reality PHP Atom API</generator>' . "\n"; 
     1245        } 
     1246         
     1247        // LINKS 
     1248        $links = $this->get_links(); 
     1249        if (sizeof($links)) { 
     1250            foreach ($links as $link) { 
     1251                $xml .= '<link'; 
     1252                foreach ($link as $attrib=>$val) { 
     1253                     if (strlen($attrib) && strlen($val)) { 
     1254                         $xml .= ' ' . $attrib . '="' . utf8_encode($val) . '"'; 
     1255                     } 
     1256                } 
     1257                $xml .= '/>' . "\n"; 
     1258            } 
     1259        } 
     1260         
     1261        // AUTHOR 
     1262        $author = $this->get_author(); 
     1263        if (sizeof($author)) { 
     1264            $xml .= '<author>'; 
     1265            foreach ($author as $key=>$val) { 
     1266                if (strlen($key) && strlen($val)) { 
     1267                    $xml .= '<' . $key .'>' . utf8_encode($val) . '</' . $key . '>' . "\n"; 
     1268                } 
     1269            } 
     1270            $xml .= '</author>' . "\n"; 
     1271        } 
     1272         
     1273        // DATES (issued,  created,  modified) 
     1274        if (strlen($this->get_issued())) { 
     1275            $xml .= '<issued>' . $this->get_issued() . '</issued>' . "\n"; 
     1276        } 
     1277         
     1278        // Modified and Id,  only if not creating a POST entry 
     1279        if ($purpose != 'POST' && $purpose != 'PUT') { 
     1280            if (strlen($this->get_created())) { 
     1281                $xml .= '<created>' . $this->get_created() . '</created>' . "\n"; 
     1282            } 
     1283            if (strlen($this->get_modified())) { 
     1284                $xml .= '<modified>' . $this->get_modified() . '</modified>' . "\n"; 
     1285            } 
     1286             
     1287            // ID element 
     1288            $xml .= '<id>' . $this->id . '</id>' . "\n"; 
     1289        } 
     1290         
     1291        // TITLE 
     1292        $title = $this->get_title(); 
     1293        if (is_array($title)) { 
     1294            $xml .= '<title'; 
     1295            foreach ($title as $key=>$val) { 
     1296                if ($key != 'title') { 
     1297                    $xml .= ' ' . $key . '="' . $val . '"'; 
     1298                } 
     1299            } 
     1300            $xml .= '>' . utf8_encode($title['title']) . '</title>' . "\n"; 
     1301        } 
     1302         
     1303        // CONTENT 
     1304        $content = $this->get_content(); 
     1305        if (is_array($content)) { 
     1306            $xml .= '<content'; 
     1307            foreach ($content as $key=>$val) { 
     1308                if ($key != 'content') { 
     1309                    $xml .= ' ' . $key . '="' . $val . '"'; 
     1310                } 
     1311            } 
     1312            $xml .= '>' . utf8_encode($content['content']) . '</content>' . "\n"; 
     1313        } 
     1314         
     1315        $xml .= '</entry>' . "\n\n"; 
     1316         
     1317        return $xml; 
     1318    } 
     1319     
     1320    /** 
     1321    * @return Int/FALSE 
     1322    * @desc Returns the last error registered in this class,  or FALSE if none 
     1323    */ 
     1324    function error() { 
     1325        if (isset($this->err_no) && is_int($this->err_no)) { 
     1326            return $this->err_no; 
     1327        } 
     1328        else { 
     1329            return false; 
     1330        } 
     1331    } 
     1332} 
     1333 
     1334 
     1335 
     1336class AtomRequest { 
     1337    var $method; 
     1338    var $uri; 
     1339    var $auth; 
     1340    var $payload; 
     1341    var $response; 
     1342    var $httpcode; 
     1343    var $err_no; 
     1344     
     1345    /** 
     1346    * @return AtomRequest 
     1347    * @param String $method 
     1348    * @param String $uri 
     1349    * @param AuthObject $auth 
     1350    * @param String $payload 
     1351    * @desc Creates an object with functions for performing standard Atom requests. $method should be [GET|PUT|POST|DELETE],  $uri is the endpoint/uri to execute the request against,  $auth is an authentication object (BasicAuth or WSSE) and $payload is the XML of the request,  if required (POST|PUT) 
     1352    */ 
     1353    function AtomRequest($method,  $uri,  $auth,  $payload = false) { 
     1354        $this->set_method($method); 
     1355        $this->set_uri($uri); 
     1356        $this->set_auth($auth); 
     1357        $this->set_payload($payload); 
     1358         
     1359        if ($this->error()) { 
     1360            return false; 
     1361        } 
     1362    } 
     1363     
     1364    /** 
     1365    * @return void 
     1366    * @param String $uri 
     1367    * @desc Set the URI to execute this AtomRequest against. Should be a fully-qualified URL and start with 'http' 
     1368    */ 
     1369    function set_uri($uri) { 
     1370        if (substr($uri,  0,  4) == 'http') { 
     1371            $this->uri = $uri; 
     1372        } 
     1373        else { 
     1374            $this->err_no = ATOMAPI_ENDPOINT_INVALID; 
     1375        } 
     1376    } 
     1377     
     1378    /** 
     1379    * @return String/FALSE on error 
     1380    * @desc Returns the URI stored for this AtomRequest to operate against,  or FALSE if not set yet 
     1381    */ 
     1382    function get_uri() { 
     1383        if (isset($this->uri) && strlen($this->uri)) { 
     1384            return $this->uri; 
     1385        } 
     1386        else { 
     1387            return false; 
     1388        } 
     1389    } 
     1390     
     1391    /** 
     1392    * @return void 
     1393    * @param AuthObj $auth 
     1394    * @desc Set the authentication object to use for this AtomRequest 
     1395    */ 
     1396    function set_auth($auth) { 
     1397        if (is_object($auth)) { 
     1398            $this->auth = $auth; 
     1399        } 
     1400        else { 
     1401            $this->err_no = ATOMAPI_AUTHTYPE_INVALID; 
     1402        } 
     1403    } 
     1404     
     1405    /** 
     1406    * @return void 
     1407    * @param String $method 
     1408    * @desc Sets the method of this AtomRequest. Allowed values are [GET|PUT|POST|DELETE] 
     1409    */ 
     1410    function set_method($method) { 
     1411        if (in_array($method,  array('GET',  'PUT',  'POST',  'DELETE'))) { 
     1412            $this->method = $method; 
     1413        } 
     1414        else { 
     1415            $this->err_no = ATOMAPI_METHOD_INVALID; 
     1416        } 
     1417    } 
     1418     
     1419    /** 
     1420    * @return String/FALSE on error 
     1421    * @desc Returns the current $method set for this AtomRequest. Should be [GET|PUT|POST|DELETE]. Returns FALSE if it is unset. 
     1422    */ 
     1423    function get_method() { 
     1424        if (isset($this->method) && strlen($this->method)) { 
     1425            return $this->method; 
     1426        } 
     1427        else { 
     1428            return false; 
     1429        } 
     1430    } 
     1431     
     1432    /** 
     1433    * @return void 
     1434    * @param String $xml 
     1435    * @desc Set the payload (body) of the AtomRequest. Should be an XML string,  and is only required for POST and PUT operations 
     1436    */ 
     1437    function set_payload($xml) { 
     1438        if (is_string($xml)) { 
     1439            $this->payload = $xml; 
     1440        } 
     1441        else { 
     1442            $this->err_no = ATOMAPI_PAYLOAD_INVALID; 
     1443        } 
     1444    } 
     1445     
     1446    /** 
     1447    * @return String/FALSE on error 
     1448    * @desc Returns the current stored payload,  which should be an XML package,  or FALSE if it is not set yet. 
     1449    */ 
     1450    function get_payload() { 
     1451        if (isset($this->payload)) { 
     1452            return $this->payload; 
     1453        } 
     1454        else { 
     1455            return false; 
     1456        } 
     1457    } 
     1458     
     1459    /** 
     1460    * @return Int/FALSE on error 
     1461    * @desc Executes the AtomRequest on the URI specified in the object. Returns the HTTP response code,  or FALSE on a serious error 
     1462    */ 
     1463    function exec() { 
     1464        // Confirm minimum requirements (method,  uri) 
     1465        if (!$this->get_uri() || !$this->get_method()) { 
     1466            $this->err_no = ATOMAPI_REQUEST_INVALID; 
     1467            return; 
     1468        } 
     1469         
     1470        // Common cURL configuration directives 
     1471        $ch = curl_init(); 
     1472        curl_setopt($ch,  CURLOPT_URL,  $this->get_uri()); 
     1473        curl_setopt($ch,  CURLOPT_USERAGENT,  "Dented Reality PHP Atom API Library v0.1"); 
     1474        curl_setopt($ch,  CURLOPT_SSL_VERIFYPEER,  0); // Don't stress about SSL validity 
     1475        curl_setopt($ch,  CURLOPT_RETURNTRANSFER,  1); // Return the response,  don't output it 
     1476 
     1477                // Rebuild the nonce each request 
     1478                $this->auth->rebuild(); 
     1479         
     1480        // Handle configuration of specific options for certain request types 
     1481        switch ($this->get_method()) { 
     1482            case 'POST' : 
     1483                // Authentication header and content-type (required for Blogger.com) 
     1484                $headers = $this->auth->get_header(true); 
     1485                $headers[] = 'Content-type: application/xml'; 
     1486                curl_setopt($ch,  CURLOPT_HTTPHEADER,  $headers); 
     1487                 
     1488                // Configuring post contents (should be an XML payload) 
     1489                curl_setopt($ch,  CURLOPT_POST,  1); 
     1490                curl_setopt($ch,  CURLOPT_POSTFIELDSIZE,  strlen($this->get_payload())); 
     1491                curl_setopt($ch,  CURLOPT_POSTFIELDS,  $this->get_payload()); 
     1492                break; 
     1493            case 'PUT' : 
     1494                // PUT requires the payload to be written to file,  and passed as a file pointer 
     1495                $put = tmpfile(); 
     1496                fputs($put,  $this->get_payload()); 
     1497                rewind($put); 
     1498                 
     1499                // Authentication headers and content-type (for Blogger.com) 
     1500                $headers = $this->auth->get_header(true); 
     1501                $headers[] = 'Content-type: application/xml'; 
     1502                curl_setopt($ch,  CURLOPT_HTTPHEADER,  $headers);  
     1503                 
     1504                // Performing a PUT operation,  requires file pointer and size of payload 
     1505                curl_setopt($ch,  CURLOPT_PUT,  1); 
     1506                curl_setopt($ch,  CURLOPT_INFILE,  $put); 
     1507                curl_setopt($ch,  CURLOPT_INFILESIZE,  strlen($this->get_payload())); 
     1508                break; 
     1509            case 'DELETE' : 
     1510                // Simple DELETE request (authenticated) 
     1511                curl_setopt($ch,  CURLOPT_CUSTOMREQUEST,  'DELETE'); 
     1512                curl_setopt($ch,  CURLOPT_HTTPHEADER,  $this->auth->get_header(true)); 
     1513                break; 
     1514            case 'GET' : 
     1515            default : 
     1516                // Straight GET,  with authentication headers 
     1517                curl_setopt($ch,  CURLOPT_HTTPHEADER,  $this->auth->get_header(true)); 
     1518        } 
     1519         
     1520        // Execute cURL session and handle results 
     1521        $this->response = curl_exec($ch); 
     1522         
     1523        if (curl_errno($ch)) { 
     1524            $this->err_no = ATOMAPI_CURL_ERROR; 
     1525            curl_close($ch); 
     1526            return; 
     1527        } 
     1528        else { 
     1529            // Set the HTTPcode of the response into this object,  then return it 
     1530            $this->set_httpcode(curl_getinfo($ch,  CURLINFO_HTTP_CODE)); 
     1531            curl_close($ch); 
     1532            return $this->get_httpcode(); 
     1533        } 
     1534    } 
     1535     
     1536    /** 
     1537    * @return String/FALSE on error 
     1538    * @desc Returns the response content from the exec()'d AtomRequest,  or FALSE if not set yet 
     1539    */ 
     1540    function get_response() { 
     1541        if (isset($this->response)) { 
     1542            return $this->response; 
     1543        } 
     1544        else { 
     1545            return false; 
     1546        } 
     1547    } 
     1548     
     1549    /** 
     1550    * @return void 
     1551    * @param Int $code 
     1552    * @desc Internal function,  used to update the http status code returned when the AtomRequest is exec()'d 
     1553    */ 
     1554    function set_httpcode($code) { 
     1555        if (strlen($code) && is_int($code)) { 
     1556            $this->httpcode = $code; 
     1557        } 
     1558        else { 
     1559            $this->httpcode = false; 
     1560        } 
     1561    } 
     1562     
     1563    /** 
     1564    * @return Int/FALSE on error 
     1565    * @desc Returns the last stored http status code for this AtomRequest 
     1566    */ 
     1567    function get_httpcode() { 
     1568        if (isset($this->httpcode) && strlen($this->httpcode)) { 
     1569            return $this->httpcode; 
     1570        } 
     1571        else { 
     1572            return false; 
     1573        } 
     1574    } 
     1575     
     1576    /** 
     1577    * @return Int/FALSE 
     1578    * @desc Returns the last error registered in this class,  or FALSE if none 
     1579    */ 
     1580    function error() { 
     1581        if (isset($this->err_no) && is_int($this->err_no)) { 
     1582            return $this->err_no; 
     1583        } 
     1584        else { 
     1585            return false; 
     1586        } 
     1587    }     
     1588} 
     1589 
     1590 
     1591 
     1592 
     1593class BasicAuth { 
     1594        var $username; 
     1595        var $password; 
     1596         
     1597        /** 
     1598        * @return BasicAuth 
     1599        * @param String $username 
     1600        * @param String $password 
     1601        * @desc Constructor - sets variables only 
     1602        */ 
     1603        function BasicAuth($username, $password) { 
     1604                if (strlen($username)) { 
     1605                        $this->username = $username; 
     1606                } 
     1607                 
     1608                if (strlen($password)) { 
     1609                        $this->password = $password; 
     1610                } 
     1611        } 
     1612         
     1613        /** 
     1614        * @return String or Array 
     1615        * @param Boolean $array TRUE to return an array suitable for cURL, FALSE/NULL for a string  
     1616        * @desc Returns the HTTP headers required for sending Basic Authentication information 
     1617        */ 
     1618        function get_header($array = false) { 
     1619                // Array for use in CURLOPT_HTTPHEADER 
     1620                if ($array) { 
     1621                        return array("Authorization: Basic " . base64_encode($this->username . ':' . $this->password)); 
     1622                } 
     1623                // String for manual request construction 
     1624                else { 
     1625                        return 'Authorization: Basic ' . base64_encode($this->username . ':' . $this->password) . "\r\n"; 
     1626                } 
     1627        } 
     1628         
     1629        /** 
     1630        * @return String/FALSE 
     1631        * @desc Returns username if set, or FALSE 
     1632        */ 
     1633        function get_username() { 
     1634                if (isset($this->username)) { 
     1635                        return $this->username; 
     1636                } 
     1637                else { 
     1638                        return false; 
     1639                } 
     1640        } 
     1641         
     1642        /** 
     1643        * @return String/FALSE 
     1644        * @desc Returns password if set, or FALSE 
     1645        */ 
     1646        function get_password() { 
     1647                if (isset($this->password)) { 
     1648                        return $this->password; 
     1649                } 
     1650                else { 
     1651                        return false; 
     1652                } 
     1653        } 
     1654} 
     1655 
     1656 
     1657 
     1658class WSSE { 
     1659    var $username  = ''; 
     1660    var $password  = ''; 
     1661    var $profile   = 'UsernameToken'; 
     1662    var $timestamp = ''; 
     1663    var $digest    = ''; 
     1664    var $nonce     = ''; 
     1665     
     1666    /** 
     1667    * @return WSSE 
     1668    * @param String $username 
     1669    * @param String $password 
     1670    * @desc Creates a WSSE object which can give back a digest,  header string etc 
     1671     */ 
     1672    function WSSE($username,  $password,  $profile = false) { 
     1673        // If no profile,  use default 
     1674        if ($profile !== false) { 
     1675            $this->profile = $profile; 
     1676        } 
     1677         
     1678        // Set username and password 
     1679        $this->set_username($username); 
     1680        $this->set_password($password); 
     1681         
     1682        // Calculate digest (requires nonce calculation) 
     1683        $this->rebuild(); 
     1684         
     1685        // return compiled object 
     1686        return $this; 
     1687    } 
     1688     
     1689    /** 
     1690    * @return void 
     1691    * @param String $username 
     1692    * @desc Sets the username for this WSSE Object 
     1693     */ 
     1694    function set_username($username) { 
     1695        $this->username = $username; 
     1696    } 
     1697     
     1698    /** 
     1699    * @return void 
     1700    * @param String $password 
     1701    * @desc Sets the password for this WSSE Object 
     1702     */ 
     1703    function set_password($password) { 
     1704        $this->password = $password; 
     1705    } 
     1706     
     1707    /** 
     1708    * @return void 
     1709    * @param String $profile 
     1710    * @desc Sets the profile of the authentication method. Used in the header output. Defaults to UsernameToken. 
     1711     */ 
     1712    function set_profile($profile) { 
     1713        $this->profile = $profile; 
     1714    } 
     1715     
     1716    /** 
     1717    * @return void 
     1718    * @desc Creates a digest of the password,  using the nonce and timestamp 
     1719     */ 
     1720    function set_digest() { 
     1721        $digest = base64_encode(pack("H*",  sha1($this->get_nonce() 
     1722                                . $this->get_timestamp() 
     1723                                . $this->get_password()))); 
     1724        $this->digest = $digest; 
     1725    } 
     1726     
     1727    /** 
     1728    * @return void 
     1729    * @desc Makes an attempt at creating a randomised string for use as a nonce 
     1730     */ 
     1731    function set_nonce() { 
     1732        $mt = substr(microtime(),  2,  8); 
     1733        $md5 = md5($mt); 
     1734        $seed = $mt . rand(rand(100,  2000),  4000) . $md5; 
     1735        $nonce = md5($seed); 
     1736        $this->nonce = $nonce; 
     1737    } 
     1738     
     1739    /** 
     1740    * @return void 
     1741    * @desc Creates a timestamp for the object,  W3DTF Format 
     1742    */ 
     1743    function set_timestamp() { 
     1744        $this->timestamp = gmdate('Y-m-d\TH:i:s\Z'); 
     1745    } 
     1746     
     1747 
     1748    /** 
     1749    * @return String/Array 
     1750    * @param Boolean $array TRUE to return an array,  FALSE to return a string 
     1751    * @desc Returns a WSSE header for use in cURL or socket requests. If $array is TRUE,  then returns an array suitable for cURL CUSTOMHEADER 
     1752    */ 
     1753    function get_header($array = false) { 
     1754        if ($array) { 
     1755            return array('Authorization: WSSE profile="' . $this->get_profile() . '"', 
     1756                          'X-WSSE: ' . $this->get_profile() . ' Username="' . $this->get_username() . '",  ' 
     1757                        . 'PasswordDigest="' . $this->digest . '",  Nonce="' . base64_encode($this->get_nonce()) . '",  ' 
     1758                        . 'Created="' . $this->get_timestamp() . '"'); 
     1759        } 
     1760        else { 
     1761            return 'Authorization: WSSE profile="' . $this->get_profile() . '"' . "\r\n" 
     1762                    . 'X-WSSE: ' . $this->get_profile() . ' Username="' . $this->get_username() . '",  ' 
     1763                    . 'PasswordDigest="' . $this->digest . '",  Nonce="' . base64_encode($this->get_nonce()) . '",  ' 
     1764                    . 'Created="' . $this->get_timestamp() . '"' . "\r\n"; 
     1765        } 
     1766    } 
     1767     
     1768    /** 
     1769    * @return String 
     1770    * @desc Returns plain string containing the username 
     1771     */ 
     1772    function get_username() { 
     1773        if (isset($this->username)) { 
     1774            return $this->username; 
     1775        } 
     1776        else { 
     1777            return false; 
     1778        } 
     1779    } 
     1780     
     1781    /** 
     1782    * @return String 
     1783    * @desc Returns plain string containing the password 
     1784     */ 
     1785    function get_password() { 
     1786        if (isset($this->password)) { 
     1787            return $this->password; 
     1788        } 
     1789        else { 
     1790            return false; 
     1791        } 
     1792    } 
     1793     
     1794    /** 
     1795    * @return String 
     1796    * @desc Retrieve the nonce (or false if not set) 
     1797     */ 
     1798    function get_nonce() { 
     1799        if ($this->nonce != '') { 
     1800            return $this->nonce; 
     1801        } 
     1802        else { 
     1803            return false; 
     1804        } 
     1805    } 
     1806     
     1807    /** 
     1808    * @return String 
     1809    * @desc Retrieve the profile (or false if not set) 
     1810     */ 
     1811    function get_profile() { 
     1812        if ($this->profile != '') { 
     1813            return $this->profile; 
     1814        } 
     1815        else { 
     1816            return false; 
     1817        } 
     1818    } 
     1819     
     1820    /** 
     1821    * @return Timestamp 
     1822    * @desc Get the timestamp being used,  or false if not set 
     1823     */ 
     1824    function get_timestamp() { 
     1825        if ($this->timestamp != '') { 
     1826            return $this->timestamp; 
     1827        } 
     1828        else { 
     1829            return false; 
     1830        } 
     1831    } 
     1832     
     1833    /** 
     1834    * @return String 
     1835    * @desc Retrieve the digest (or false if not set) 
     1836     */ 
     1837    function get_digest() { 
     1838        if ($this->digest != '') { 
     1839            return $this->digest; 
     1840        } 
     1841        else { 
     1842            return false; 
     1843        } 
     1844    } 
     1845     
     1846    /** 
     1847    * @return void 
     1848    * @desc Recreate dynamic components,  effectively does everything 
     1849    *       required to rebuild the digest 
     1850     */ 
     1851    function rebuild() { 
     1852        $this->set_timestamp(); 
     1853        $this->set_nonce(); 
     1854        $this->set_digest(); 
     1855    } 
     1856} 
     1857 
     1858?> 
     1859 No newline at end of file