<?

/*

    #######################################################################################

    Kosso : March 01 2007 [ http://kosso.wordpress.com ]

    # Using a Google Account to authorize access to a new Blogger Account (was beta)
    # This uses a Secure and Registered web application written in PHP using the Curl extension
    # http://uk2.php.net/manual/en/ref.curl.php

    Plenty of debug echos are commented out. It is safe to uncomment to see what is happpening.
    There are also urls to docs for further reading as we go along the way

    Also provided are methods to list a users blogs using the old (soon to be defunct) Blogger login

    This took a LONG time to figure out. I hope this code helps someone out there,
    who might be tearing their hair out, like I was! :)

    FIRST: In order to make this a secure process, you MUST register this script and its url with Google
    see : http://code.google.com/apis/accounts/AuthForWebApps.html
    
    
    NB!!: It is very important that your system clock is correct. If not, the secure signature will not work!!!
    
    Other useful resources are the Google Groups surrounding this subject
    
    http://groups.google.com/group/Google-Accounts-API
    
    http://groups.google.com/group/bloggerDev
    
    (Thanks to Ryan Boyd and Pete Hopkins from Google for their assistance there) :)
    
    Cheers!
    Kosso

    #########################################################################################

*/



// #### SETUP

// NB: Note the url to the Blogger service is likely to change soon to www instead of www2

// Blogger API service
$scopeUrl "http://www.blogger.com/feeds";
$blogListUrl "http://www2.blogger.com/feeds/default/blogs";

// AuthSub Request service
$authSubRequestUrl "https://www.google.com/accounts/AuthSubRequest";


// Secure Token Exchange service
$sessionTokenUrl "https://www.google.com/accounts/AuthSubSessionToken";

// Path to the private key file generated during AuthSub registration (where you used with your 'cert' file)
// see : http://code.google.com/apis/accounts/RegistrationForWebApps.html

$pvtKeyFile "/PATH/TO/YOUR/PRIVATE_RSA_KEY.pem";


/////////////////////////////////
// OK : here we go!
/////////////////////////////////

echo "<h3>test authorization to a google blogger (new) account then list available blogs (test only - no token data stored)</h3>";


// Show the link and form to log in (old and new)

if(!$_GET['token'] || !$_POST){
    if(!
$_GET['token']){

        
// display the link to start Authorization
        
showLinkToGoogleLogin();

        echo 
"<h5>or try the old way using an old blogger account:</h5>";

        
// display user/pass for for old blogger accounts
        
showBloggerBasicLogin();

        
/*
        // test form to request the Atom feed give the blogspot url
        echo "<br /><br /><b>test</b> (old version) check your blogger blog version by request the Atom feed: <form action=".$_SERVER['PHP_SELF']." method=POST>
        Blogger Blog Url: http://<input type=text size=16 name=blogurl value=\"\">.blogspot.com
        <input type=Submit value=\"next step..\">
        </form>";
        echo "<br />if this works then it must be the old version";
        */

    
}

}



/////////////////////////////////////

function showLinkToGoogleLogin() {

    
// creates a link to Google to authorise this system to use a users account
    // see : http://code.google.com/apis/accounts/AuthForWebApps.html

    
global $scopeUrl$authSubRequestUrl;

    
$next_url  'http://' $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];

    
// NOTE: secure = 1 and session = 1
    // This says that we will be wanting to use a secure token and we are requesting sessioon=1 as we will be 'exchanging' the
    // returned 'single-use' token for one which never expires

    
$redirect_url $authSubRequestUrl '?secure=1&session=1';
    
$redirect_url .= '&next=';
    
$redirect_url .= urlencode($next_url);
    
$redirect_url .= "&scope=";
    
$redirect_url .= urlencode($scopeUrl);

    
// Provide a link for the user to click which will start the Authorization process, then return them to the next_url
    
echo "First you will need to authorize this script to post to your Blogger blog : <a href=\"".$redirect_url."\">click here to go to this next step</a>";

}



// OK, the first single-use session token has been returned
if(isset($_GET['token'])) {

    
// After 'Grant'ing permission for this script to use this account, Google replies with
    // a single-use token in the query string [ ?token= ]

    // echo "OK here's the (single use) token<br /><pre>".$_GET['token']."</pre>";

    // echo "<br />let's exchange for a session token....<br />";
    
$sessToken exchangeToken(trim($_GET['token']));

    if(empty(
$sessToken)){

        echo 
"error : the token was not exchanged successfully";

    } else {

        
// Now we have a token which does not expire.
        // The reply is in the form :  Token=xxxxxxxxxxxxxxxxxxx

        // echo "<hr>session token exchange reply: ".$sessToken;

        // Lets get the token from this key=value
        
$tok explode("=",$sessToken);
        
$token $tok[1];

        
// ###
        // ### This is where you would store this token for future use from your users
        // ###

        // echo "<hr>OK! Requesting user's blogs<br />";

        // Use this token to list this users blogs
        
getBlogs($token);

    }

}


/////////////////////////////////////////////



function exchangeToken($token){

    
// Performs a signed Curl request to the Session Exchange service
    // exchanges single time use token for one which never expires - for us to store

    
global $sessionTokenUrl;

    
// echo "<h4>attempting token exchange (secure)</h4>";

    // First, we create the Authorization header to add to the Curl request
    // to the session token exhange service at
    // https://www.google.com/accounts/AuthSubSessionToken
    // using getAuthHeader

    // see : http://code.google.com/apis/accounts/AuthForWebApps.html#signingrequests

    
$authSubHeader getAuthHeader("GET",$sessionTokenUrl,$token);

    
// echo "This is the Authorization header we will use: <br /><pre>".$authSubHeader."</pre><br />";

    
$ch curl_init();
    
curl_setopt($chCURLOPT_URL$sessionTokenUrl);
    
// Add our Authorization Header
    
curl_setopt($chCURLOPT_HTTPHEADER, array($authSubHeader));
    
curl_setopt($chCURLOPT_HEADERfalse);
    
curl_setopt($chCURLOPT_RETURNTRANSFERtrue);
    
//curl_setopt($ch, CURLOPT_HEADERFUNCTION, processHeader);

    // echo "<br />executing Curl request to ".$sessionTokenUrl." with this Authorization header";

    
$result curl_exec($ch);
    
// parse the result header
    
$resultArray curl_getinfo($ch);
    
// close the curl handle
    
curl_close($ch);

    if(
$resultArray['http_code'] == "200"){

        
// echo "<br />token exchanged ok!<b></b><br />";
        // echo $result."<hr>";
        
return $result;

    }

}


function 
getBlogs($token) {

    
// Performs a signed Curl request to the Blogger service to list the blogs associated with this Google Account

    
global $blogListUrl;

    
// As before, we create the Authorization header to add to the Curl request
    // but now request the blog list from
    // NB: www2 will change to www soon !!! (edit $blogListUrl above)
    // http://www2.blogger.com/feeds/default/blogs
    // using getAuthHeader

    // see : http://code.google.com/apis/accounts/AuthForWebApps.html#signingrequests


    
$authSubHeader getAuthHeader("GET",$blogListUrl,trim($token));

    
// echo "This is the Authorization header we will use: <br /><pre>".$authSubHeader."</pre><br />";

    
$ch curl_init();
    
curl_setopt($chCURLOPT_URL$blogListUrl);
    
// Add our Authorization Header  and set output to xml (our result will be Atom)
    
curl_setopt($chCURLOPT_HTTPHEADER, array(
        
'Content-Type: application/atom+xml',
        
''.$authSubHeader.''
    
));
    
curl_setopt($chCURLOPT_RETURNTRANSFERtrue);

    
// exucute request
    
$result curl_exec($ch);
    
// parse result headers
    
$resultArray curl_getinfo($ch);
    
// free memory!
    
curl_close($ch);

    if(
$resultArray['http_code'] == "200"){

        echo 
"<br />OK!<br /><b>listing users blogs : </b><br />";

        
// result ok - so now load this into a SimpleXML object to list this users blogs so we can store their details for future posts etc
        
parseBlogList($result);

    }

    
// debug the result
    // echo "<pre>";
    // print_r($resultArray);
    // echo "</pre><hr>";
    // $sResult = htmlentities($result);
    // $sResult = str_replace("&gt;&lt;","&gt;<br />&lt;",$sResult);
    // echo "<pre>";
    // print $sResult;
    // echo "</pre>";

}






//////////////////////////////////////////
// this section handle the generation of the secure signed Authorization header

function signData ($data) {

    global 
$pvtKeyFile;

    
// Read the contents of your private key - generated when you registered your script with Google
    // see: http://code.google.com/apis/accounts/RegistrationForWebApps.html

    
$handle fopen($pvtKeyFile"rb");
    
$priv_key '';
    while (!
feof($handle)) {
        
$priv_key .= fread($handle1024);
    }
    
fclose($handle);

    
// echo 'KEY IS: <br />' . $priv_key . '<br /><br />';

    // Sign the data using openssl
    // see: http://uk2.php.net/manual/en/ref.openssl.php

    
$pkeyid openssl_get_privatekey($priv_key);
    
openssl_sign($data$signature$pkeyid);
    
openssl_free_key($pkeyid);
    
// echo 'SIG IS (before encoding): ' . $signature . '<br /><br />';
    // echo 'SIG IS (after encoding): ' . base64_encode($signature) . '<br /><br />';

    // Base64 encode the signed data and return to the request
    
return base64_encode($signature);

}


function 
getAuthHeader ($method$url$token) {

    
// Generate the securely signed Authorization header to use with Curl

    // Create our data string by concatenating the request method, the url, a unix timestamp and a 'nonce' (64bit unsigned random number)
    
$timeStamp time();
    
$nonce mt_rand(0,mt_getrandmax());
    
$dataToSign $method' '.$url.' '.$timeStamp.' '.$nonce;

    
// Sign the data
    
$sig signData($dataToSign);

    
// Concatenate the info to create the full secure Authorization header
    // see : http://code.google.com/apis/accounts/AuthForWebApps.html#signingrequests
    
$authHeader 'Authorization: AuthSub token="' $token '" ' .
                     
'data="'$dataToSign '" sig="' $sig '" ' .
                    
'sigalg="rsa-sha1"';
     return 
$authHeader;

}


// Use simpleXML to read the results of the Atom reply of the blog list
// see : http://uk2.php.net/manual/en/ref.simplexml.php

function parseBlogList($result){

    
// Loads the blog list result (in Atom format) into a SimpleXML object for us to list them

    
if($blogsXML simplexml_load_string($result)){
        foreach(
$blogsXML->entry as $blog){


            foreach(
$blog->link as $link){

                if(
$link['rel']=="alternate"){

                    
$blogUrl $link['href'];

                }

            }

            
// list blog titles with url links - and id element
            
echo "<li><a href=\"".$blogUrl."\">".$blog->title."</a> [".$blog->id."]";

        }
    } else {
        echo 
"error parsing result";
    }
}






/////////////////////////////////////

// stuff for old accounts which you might find useful


function getBloggerBlogs($username,$password){

    
// Lists blogs using the OLD blogger login

    // GET this url via authenication

    
$host "http://www.blogger.com/feeds/default/blogs";

    
$ch curl_init();

    
curl_setopt($chCURLOPT_URL$host);
    
//curl_setopt($ch, CURLOPT_HEADER, 1);
    
curl_setopt($chCURLOPT_VERBOSE1);
    
curl_setopt($chCURLOPT_RETURNTRANSFER1);
    
curl_setopt($chCURLOPT_USERPWD"$username:$password");
    
curl_setopt($chCURLOPT_HTTP_VERSIONCURL_HTTP_VERSION_1_1);
    
$result curl_exec($ch);
    
$resultArray curl_getinfo($ch);

    
// close curl
    
curl_close($ch);

    echo 
"http code: ".$resultArray['http_code']."<br />";

    if(
$resultArray['http_code'] == "200"){

        echo 
"<br />OK!<br /><b>listing users blogs : </b><br />";

        
// result ok - so now load this into a SimpleXML object to list this users blogs so we can store their details for future posts etc
        
parseBlogList($result);

    } else {

        echo 
"error listing user blogs : ".$host;

    }

    
// ### debug the result
    // echo "<pre>";
    // print_r($resultArray);
    // echo "</pre><hr>";
    // $sResult = htmlentities($result);
    // $sResult = str_replace("&gt;&lt;","&gt;<br />&lt;",$sResult);
    // echo "<pre>";
    // print $sResult;
    // echo "</pre>";


}






function 
showBloggerBasicLogin(){

    
// create the form to login to the old accounts - then list blogs
    
echo "<form action=".$_SERVER['PHP_SELF']." method=POST>
    Username: <input type=text name=user> <br />
    Password: <input type=password name=pass> <br />
    <input type=Submit value=\"list my blogger blogs\">
    </form>"
;

}



if(isset(
$_POST['user']) && isset($_POST['pass'])){

    
// request bloglist given old blogger account
    
getBloggerBlogs($_POST['user'],$_POST['pass']);

}




if(isset(
$_POST['blogurl'])){

    
// request Atom feed given the blogspot url/name

    // check blogger type : BASIC or AuthSub

    
$host "http://".$_POST['blogurl'].".blogspot.com/feeds/posts/default";

    echo 
"<h3>requesting blog feed</h3>";

    
$ch curl_init();

    
curl_setopt($chCURLOPT_URL$host);
    
curl_setopt($chCURLOPT_HEADER1);
    
curl_setopt($chCURLOPT_VERBOSE1);
    
curl_setopt($chCURLOPT_RETURNTRANSFER1);
    
curl_setopt($chCURLOPT_HTTP_VERSIONCURL_HTTP_VERSION_1_1);

    
$result curl_exec($ch);

    
$resultArray curl_getinfo($ch);


    
// debug the output ...etc..

    // headers
    
echo "<pre>";
    
print_r($resultArray);
    echo 
"</pre><hr>";

    
// reply
    
$sResult htmlentities($result);
    
$sResult str_replace("&gt;&lt;","&gt;<br />&lt;",$sResult);
    echo 
"<pre>";
    print 
$sResult;
    echo 
"</pre>";

}







?>