Here's some relatively simple code I've been working on.
I haven't had time to do anything with it recently, so it's not
complete and probably has some bugs, but at one point I had tested all
the google apps api functions for use by the google apps admin. There
are also some functions for individual user account contact list
editing. The batch function is, of course, not tested yet since that
feature isn't supported yet. It includes XML parsing into an array
and a function to reconstruct the XML from the same array, but I only
tested that function a little.
<?php
#Basic Google Apps API functions
#For more details, see:
http://code.google.com/apis/apps/gdata_provisioning_api_v2.0_reference.html
global $domain,$googleURL,$googleAcctsURL,$ch,$SID,$LSID,$header,
$gaXMLData,$adminUser,$adminPW;
#Google Apps base URL
$googleURL = "
https://www.google.com";
$googleAcctsURL='
https://www.google.com/accounts/ClientLogin';
$domain='
domain.com';
#the admin account to use in performing updates
#only admin accounts can use the Google Apps APIs
#this should be the email address of the admin account
$adminUser='
user...@domain.com';
$adminPW='password';
#gaInit(); #get the auth token for the admin username and password
#these are for google apps api functions
#$results=gaAddUser('username','password','firstName','lastName');
#$results=gaUpdateUser('username',$vals); #can't update username,
can only update quota if supported by your googleapps domain account
#$user=gaGetUser('username');
#$users=gaGetAllUsers();
#$results=gaDeleteUser('username');
#$results=gaAddAlias('username','alias');
#$alias=gaGetAliasForUser('username');
#$aliases=gaGetAllAliases();
#$results=gaDeleteAlias('alias');
#$results=gaAddList('listname');
#$list=gaGetList('listname');
#$lists=gaGetListsForRecipient('emailAddress');
#$lists=gaGetAllLists();
#$results=gaDeleteList('listname');
#each function automatically parses the XML and returns a
mulitdimensional array.
#use var_dump to see the structure
#I think it retains the xml data so after individual array values are
edited,
#the xml recomposed from the array by gaMakeXML
#there are some experimental functions near the end for individual
user contact list editing
#use similar to like this
#$authToken=gaGetAuthToken($email,$password,'cp');
#$gData=gaGetUserContacts($email,$authToken);
#if (isset($gData['feed'][0]['entry'])) {
# $userContacts=$gData['feed'][0]['entry'];
#}
function gaInit() {
#initialize and get the auth token
global $ch,$adminUser,$adminPW,$header;
#init curl
$ch = curl_init();
#some operations can take a while, especially if google's servers are
busy
curl_setopt($ch, CURLOPT_TIMEOUT, 150);
#get the auth token used in all subsequent transactions
$gaAuthToken=gaGetAuthToken($adminUser,$adminPW,'apps');
#set common headers
$header[]="Content-type: application/atom+xml";
$header[]="Authorization: GoogleLogin auth=$gaAuthToken";
}
function gaCleanup() {
global $ch;
curl_close($ch);
}
function gaGetAuthToken($username,$password,$service) {
global $googleURL,$ch;
$ch = curl_init();
#get the auth token
#an auth token is required for all admin actions
#the token is good for 24 hours, but it's more secure to obtain a
new one every time the script is run
$postData['accountType']='HOSTED_OR_GOOGLE';
$postData['Email']=$username;
$postData['Passwd']=$password;
$postData['service']=$service;
$page = '/accounts/ClientLogin';
curl_setopt($ch, CURLOPT_URL, $googleURL.$page);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
$response = curl_exec($ch);
if (strpos($response,'LSID')) {
$vals=split("\n",$response);
$SID=split("=",$vals[0]); $SID=$SID[1];
$LSID=split("=",$vals[1]); $LSID=$LSID[1];
$gaAuthToken=split("=",$vals[2]); $gaAuthToken=$gaAuthToken[1];
}
else {
return $response;
}
if(curl_errno($ch)) {return curl_error($ch);}
else {return $gaAuthToken;}
#according to google, SID and LSID aren't used right now
}
function gaAddUser ($username,$password,$firstName,$lastName) {
global $googleURL,$ch,$header,$domain;
$page = "/a/feeds/$domain/user/2.0";
#the xml to create the account
$post_string = '<?xml version="1.0" encoding="UTF-8"?>
<atom:entry xmlns:atom="
http://www.w3.org/2005/Atom"
xmlns:apps="
http://schemas.google.com/apps/2006">
<atom:category scheme="
http://schemas.google.com/g/2005#kind"
term="
http://schemas.google.com/apps/2006#user"/>
<apps:login userName="'.$username.'"
password="'.$password.'" suspended="false"/>
<apps:quota limit="0"/>
<apps:name familyName="'.$lastName.'" givenName="'.$firstName.'"/>
</atom:entry>';
curl_setopt($ch, CURLOPT_URL, $googleURL.$page);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_string);
$response=curl_exec($ch);
if(curl_errno($ch)) {return curl_error($ch);}
else {return $response;}
}
function gaUpdateUser ($username,$vals) {
global $googleURL,$ch,$header,$domain;
$ch=curl_init();
#$vals is an array that can contain the following:
#username, firstName, lastName, password, suspended, quota
#only non-null values are updated
#when changing the username, $username should be the old username and
$vals['username'] the new
$xml = '<?xml version="1.0" encoding="UTF-8"?>
<atom:entry xmlns:atom="
http://www.w3.org/2005/Atom"
xmlns:apps="
http://schemas.google.com/apps/2006">
<atom:category scheme="
http://schemas.google.com/g/2005#kind"
term="
http://schemas.google.com/apps/2006#user"/>';
if (isset($vals['username']) || isset($vals['password']) ||
isset($vals['suspended'])) {
$xml.='<apps:login';
if (isset($vals['username'])) {$xml.=" userName=\"$vals[username]
\"";}
if (isset($vals['password'])) {$xml.=" password=\"$vals[password]
\"";}
if (isset($vals['suspended'])) {$xml.=" suspended=\"$vals[suspended]
\"";}
$xml.="/>";
}
if (isset($vals['quota'])) {$xml.="<apps:quota limit=\"$vals[quota]\"/
>";}
if (isset($vals['firstName']) || isset($vals['lastName'])) {
$xml.="<apps:name";
if (isset($vals['lastName'])) {$xml.=" familyName=\"$vals[lastName]
\"";}
if (isset($vals['firstName'])) {$xml.=" givenName=\"$vals[firstName]
\"";}
$xml.="/>";
}
$xml.='</atom:entry>';
$fh=fopen('/tmp/tmp_gapp.xml','w');
fwrite($fh,$xml);
fclose($fh);
$fh=fopen('/tmp/tmp_gapp.xml','r');
$page = "/a/feeds/$domain/user/2.0/$username";
curl_setopt($ch, CURLOPT_URL, $googleURL.$page);
curl_setopt($ch, CURLOPT_PUT, true);
curl_setopt($ch, CURLOPT_INFILE, $fh);
curl_setopt($ch, CURLOPT_INFILESIZE, strlen($xml));
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
$response=curl_exec($ch);
fclose ($fh);
if(curl_errno($ch)) {return curl_error($ch);}
else {return $response;}
}
function gaDeleteUser ($username) {
global $googleURL,$ch,$header,$domain;
$page = "/a/feeds/$domain/user/2.0/$username";
#print "send to $googleURL$page\n\n";
curl_setopt($ch, CURLOPT_URL, $googleURL.$page);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
# I couldn't get the php curl method to work
# $response=curl_exec($ch);
# if(curl_errno($ch)) {return curl_error($ch);}
# else {return $response;}
shell_exec("curl --silent --request DELETE --header \"$header[0]\n\r
$header[1]\" \"$googleURL$page\"");
}
function gaGetUser ($username) {
global $googleURL,$ch,$header,$gaXMLData,$domain;
$page = "/a/feeds/$domain/user/2.0/$username";
curl_setopt($ch, CURLOPT_URL, $googleURL.$page);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPGET, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
$response=curl_exec($ch);
if(curl_errno($ch)) {return curl_error($ch);}
else {
gaParseXMLResponse($response);
#$gaXMLData is populated as a global var, but we also return it for
intuitive use
return $gaXMLData;
}
}
function gaGetAllUsers () {
global $googleURL,$ch,$header,$gaXMLData,$domain;
$page = "/a/feeds/$domain/user/2.0";
curl_setopt($ch, CURLOPT_URL, $googleURL.$page);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPGET, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
$response=curl_exec($ch);
if(curl_errno($ch)) {return curl_error($ch);}
else {
gaParseXMLResponse($response);
#$gaXMLData is populated as a global var, but we also return it for
intuitive use
return $gaXMLData;
}
}
function gaAddAlias ($username,$alias) {
global $googleURL,$ch,$header,$domain;
$page = "/a/feeds/$domain/nickname/2.0";
#the xml to create the alias
$xml="<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<atom:entry xmlns:atom=\"
http://www.w3.org/2005/Atom\"
xmlns:apps=\"
http://schemas.google.com/apps/2006\">
<atom:category scheme=\"
http://schemas.google.com/g/2005#kind\"
term=\"
http://schemas.google.com/apps/2006#nickname\"/>
<apps:nickname name=\"$alias\"/>
<apps:login userName=\"$username\"/>
</atom:entry>";
curl_setopt($ch, CURLOPT_URL, $googleURL.$page);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
$response=curl_exec($ch);
if(curl_errno($ch)) {return curl_error($ch);}
else {return $response;}
}
function gaGetAlias ($alias) {
global $googleURL,$ch,$header,$gaXMLData,$domain;
$page = "/a/feeds/$domain/nickname/2.0/$alias";
curl_setopt($ch, CURLOPT_URL, $googleURL.$page);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPGET, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
$response=curl_exec($ch);
if(curl_errno($ch)) {return curl_error($ch);}
else {
gaParseXMLResponse($response);
#$gaXMLData is populated as a global var, but we also return it for
intuitive use
return $gaXMLData;
}
}
function gaGetAliasForUser ($username) {
global $googleURL,$ch,$header,$gaXMLData,$domain;
$page = "/a/feeds/$domain/nickname/2.0?username=$username";
curl_setopt($ch, CURLOPT_URL, $googleURL.$page);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPGET, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
$response=curl_exec($ch);
if(curl_errno($ch)) {return curl_error($ch);}
else {
gaParseXMLResponse($response);
#$gaXMLData is populated as a global var, but we also return it for
intuitive use
return $gaXMLData;
}
}
function gaGetAllAliases () {
global $googleURL,$ch,$header,$gaXMLData,$domain;
$page = "/a/feeds/$domain/nickname/2.0";
curl_setopt($ch, CURLOPT_URL, $googleURL.$page);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPGET, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
$response=curl_exec($ch);
if(curl_errno($ch)) {return curl_error($ch);}
else {
gaParseXMLResponse($response);
#$gaXMLData is populated as a global var, but we also return it for
intuitive use
return $gaXMLData;
}
}
function gaDeleteAlias ($alias) {
global $googleURL,$ch,$header,$domain;
$page = "/a/feeds/$domain/nickname/2.0/$alias";
#print "send to $googleURL$page\n\n";
curl_setopt($ch, CURLOPT_URL, $googleURL.$page);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
shell_exec("curl --silent --request DELETE --header \"$header[0]\n\r
$header[1]\" \"$googleURL$page\"");
}
function gaAddList ($listname) {
global $googleURL,$ch,$header,$domain;
$page = "/a/feeds/$domain/emailList/2.0";
#the xml to create the list
$xml="<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<atom:entry xmlns:atom=\"
http://www.w3.org/2005/Atom\"
xmlns:apps=\"
http://schemas.google.com/apps/2006\">
<atom:category scheme=\"
http://schemas.google.com/g/2005#kind\"
term=\"
http://schemas.google.com/apps/2006#emailList\"/>
<apps:emailList name=\"$listname\"/>
</atom:entry>";
curl_setopt($ch, CURLOPT_URL, $googleURL.$page);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
$response=curl_exec($ch);
if(curl_errno($ch)) {return curl_error($ch);}
else {return $response;}
}
function gaGetList ($listname) {
global $googleURL,$ch,$header,$gaXMLData,$domain;
$page = "/a/feeds/$domain/emailList/2.0/$listname";
curl_setopt($ch, CURLOPT_URL, $googleURL.$page);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPGET, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
$response=curl_exec($ch);
if(curl_errno($ch)) {return curl_error($ch);}
else {
gaParseXMLResponse($response);
#$gaXMLData is populated as a global var, but we also return it for
intuitive use
return $gaXMLData;
}
}
function gaGetListsForRecipient ($recipient) {
global $googleURL,$ch,$header,$gaXMLData,$domain;
$page = "/a/feeds/$domain/emailList/2.0?recipient=$recipient";
curl_setopt($ch, CURLOPT_URL, $googleURL.$page);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPGET, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
$response=curl_exec($ch);
if(curl_errno($ch)) {return curl_error($ch);}
else {
gaParseXMLResponse($response);
#$gaXMLData is populated as a global var, but we also return it for
intuitive use
return $gaXMLData;
}
}
function gaGetAllLists () {
global $googleURL,$ch,$header,$gaXMLData,$domain;
$page = "/a/feeds/$domain/emailList/2.0";
curl_setopt($ch, CURLOPT_URL, $googleURL.$page);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPGET, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
$response=curl_exec($ch);
if(curl_errno($ch)) {return curl_error($ch);}
else {
gaParseXMLResponse($response);
#$gaXMLData is populated as a global var, but we also return it for
intuitive use
return $gaXMLData;
}
}
function gaDeleteList ($listname) {
global $googleURL,$ch,$header,$domain;
$page = "/a/feeds/$domain/emailList/2.0/$listname";
# curl_setopt($ch, CURLOPT_URL, $googleURL.$page);
# curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
# curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
shell_exec("curl --silent --request DELETE --header \"$header[0]\n\r
$header[1]\" \"$googleURL$page\"");
}
### FUNCTIONS FOR MANAGING CONTACT LISTS IN INDIVIDUAL ACCOUNTS ###
function gaAuthUser ($username,$passwd) {
#authenticate individual user
# I think this function is redundant - don't remember what I was
doing with it
global $googleAcctsURL,$ch,$domain;
$postData['accountType']='HOSTED';
$postData['Email']=$username;
$postData['Passwd']=$passwd;
$pastData['service']='cp';
$postData['source']=$domain;
curl_setopt($ch, CURLOPT_URL, $googleAcctsURL);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
$response=curl_exec($ch);
if(curl_errno($ch)) {return curl_error($ch);}
else {
return $response;
# gaParseXMLResponse($response);
#$gaXMLData is populated as a global var, but we also return it for
intuitive use
# return $gaXMLData;
}
}
function gaGetUserContacts ($username,$authToken) {
global $fh;
global $googleURL,$ch,$header,$gaXMLData;
$head[]="Content-type: application/atom+xml";
$head[]="Authorization: GoogleLogin auth=$authToken";
$username=urlencode($username);
$url="
http://www.google.com/m8/feeds/contacts/$username/base?max-
results=10000";
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPGET, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $head);
$response=curl_exec($ch);
if(curl_errno($ch)) {return curl_error($ch);}
else {
# return $response;
fwrite($fh, $response);
gaParseXMLResponse($response);
#$gaXMLData is populated as a global var, but we also return
it for intuitive use
return $gaXMLData;
}
}
function gaAddUserContacts ($username,$authToken,$contacts) {
global $googleURL,$ch,$header,$gaXMLData;
$head[]="Content-type: application/atom+xml";
$head[]="Authorization: GoogleLogin auth=$authToken";
$username=urlencode($username);
$url="
http://www.google.com/m8/feeds/contacts/$username/base";
#define defaults (in some cases, a default empty string will result
in the property not being set at all)
$defaults=array('title'=>'','notes'=>'',
'emails'=>array('type'=>'other','address'=>''),
'phones'=>array('type'=>'other','number'=>'','primary'=>0),
'ims'=>array('type'=>'other','protocol'=>'JABBER','address'=>''),
'addresses'=>array('type'=>'other','primary'=>0,'address'=>''));
#make sure all required fields are set properly
foreach ($contacts as $contact) {
$xml='';
if (isset($contact['gaVals'])) {$contact=$contact['gaVals'];}
if (!isset($contact)) {$contact=array();}
foreach ($defaults as $field=>$default) {
if (!isset($contact[$field])) {$contact[$field]=$default;}
#make sure subfields are set properly
if (is_array($default)) {
if (!isset($contact[$field][0]))
{$contact[$field]=array($contact[$field]);}
for ($cnt=0; $cnt<count($contact[$field]);$cnt++) {
foreach ($defaults[$field] as $subfield=>$subdefault) {
if (!isset($contact[$field][$cnt][$subfield])) {
$contact[$field][$cnt][$subfield]=$subdefault;
}
}
}
}
}
$xml.="<atom:entry xmlns:atom='
http://www.w3.org/2005/Atom'
xmlns:gd='
http://schemas.google.com/g/2005'>
<atom:category scheme='
http://schemas.google.com/g/2005#kind'
term='
http://schemas.google.com/contact/2008#contact' />
<atom:title type='text'>$contact[title]</atom:title>
<atom:content type='text'>$contact[notes]</atom:content>\n";
foreach ($contact['emails'] as $index=>$email) {
if ($email['address']) {
$xml.=" <gd:email rel='
http://schemas.google.com/g/2005#
$email[type]' address='$email[address]' />\n";
}
}
foreach ($contact['phones'] as $index=>$phone) {
if ($phone['number']) {
$primary='';
if ($phone['primary']) {$primary="primary='true'";}
$xml.=" <gd:phoneNumber rel='
http://schemas.google.com/g/2005#
$phone[type]' $primary>$phone[number]</gd:phoneNumber>\n";
}
}
foreach ($contact['ims'] as $index=>$im) {
if ($im['address']) {
$xml.="<gd:im address='$im[address]' protocol='http://
schemas.google.com/g/2005#$im[protocol]'
rel='
http://schemas.google.com/g/2005#$im[type]' />\n";
}
}
foreach ($contact['addresses'] as $index=>$address) {
if ($address['address']) {
$primary='';
if ($address['primary']) {$primary="primary='true'";}
$xml.="<gd:postalAddress rel='
http://schemas.google.com/g/2005#
$address[type]' $primary>$address[address]</gd:postalAddress>\n";
}
}
$xml.="</atom:entry>\n";
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $head);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
print '.';
# print "sending $xml to $url\n\n";
$response=curl_exec($ch);
}
if(curl_errno($ch)) {return curl_error($ch);}
else {return $response;}
}
function gaEditUserContacts ($username,$authToken,$contacts,$gData) {
$ch=curl_init();
#empty entry tags to create common xml header
$gData['feed'][0]['entry']=array();
$xmlhead=substr(gaMakeXML($gData['feed'][0]),0,-9);
$xmlhead="<feed xmlns='
http://www.w3.org/2005/Atom'
xmlns:openSearch='
http://a9.com/-/spec/opensearchrss/1.0/'
xmlns:gContact='
http://schemas.google.com/contact/2008'
xmlns:gd='
http://schemas.google.com/g/2005' >";
foreach ($contacts as $email=>$contact) {
$xml=$xmlhead;
#get the edit url for this contact
foreach ($contact['link'] as $index=>$link) {
if ($link['|_ATTRS_|']['rel']=='edit') {
$url=$link['|_ATTRS_|']['href'];
break;
}
}
#recompose the (edited) XML for this contact
$xml.=gaMakeXML($contact,1);
$xml.="\n</feed>";
$head=array();
$head[]="Content-type: application/atom+xml";
$head[]="Authorization: GoogleLogin auth=$authToken";
#create a temp file for PUT to read from
$fileName="edit-$username-$email";
$fh=fopen($fileName,'w');
fwrite($fh,$xml);
fclose($fh);
$fh=fopen($fileName,'r');
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_PUT, true);
curl_setopt($ch, CURLOPT_INFILE, $fh);
curl_setopt($ch, CURLOPT_INFILESIZE, strlen($xml));
curl_setopt($ch, CURLOPT_HTTPHEADER, $head);
print "edit user at $url\n$xml\n";
$response=curl_exec($ch);
unlink($fileName);
}
return $response;
}
function gaBatchRequest ($username,$authToken,$contacts) {
#batch requests can include inserts, deletes, and updates all in one
feed
#however, the contacts API doesn't support this yet
$xml='<feed feed xmlns="
http://www.w3.org/2005/Atom"
xmlns:openSearch="
http://a9.com/-/spec/opensearchrss/1.0/"
xmlns:gContact="
http://schemas.google.com/contact/2008"
xmlns:gd="
http://schemas.google.com/g/2005"
<id>
http://www.google.com/base/feeds/items</id>
<link rel="
http://schemas.google.com/g/2005#feed"
type="application/atom+xml"
href="
http://www.google.com/base/feeds/items"/>
<link rel="
http://schemas.google.com/g/2005#post"
type="application/atom+xml"
href="
http://www.google.com/base/feeds/items"/>
<link rel="
http://schemas.google.com/g/2005#batch"
type="application/atom+xml"
href="
http://www.google.com/base/feeds/items/batch"/>';
foreach ($contacts as $email=>$contact) {
$xml.=gaMakeXML($contact,1);
}
$xml.='</feed>';
$head=array();
$head[]="Content-type: application/atom+xml";
$head[]="Authorization: GoogleLogin auth=$authToken";
#create a temp file for PUT to read from
$fileName="edit-$username-$email";
$fh=fopen($fileName,'w');
fwrite($fh,$xml);
fclose($fh);
$fh=fopen($fileName,'r');
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_PUT, true);
curl_setopt($ch, CURLOPT_INFILE, $fh);
curl_setopt($ch, CURLOPT_INFILESIZE, strlen($xml));
curl_setopt($ch, CURLOPT_HTTPHEADER, $head);
print "edit user at $url\n$xml\n";
$response=curl_exec($ch);
unlink($fileName);
return $response;
}
#### XML parsing #####
function gaMakeXML ($data,$depth=0) {
#recompose xml from an array formatted as descripted in
gaParseXMLResponse
print "Make XML from\n";
var_dump($data);
$spc=str_repeat(' ',$depth*2);
$xml=$spc.'<'.$data['|_NAME_|'];
foreach ($data['|_ATTRS_|'] as $attr=>$aVal) {
$xml.=" $attr='$aVal' ";
}
$xml.=">\n";
$depth++;
$spc=str_repeat(' ',$depth*2);
foreach ($data['|_CONTENTS_|'] as $index=>$content) {
$xml.="$spc$content\n";
}
foreach ($data as $key=>$val) {
if (substr($key,0,2) !='|_') {
#$key is a child tag of $data
foreach ($val as $index=>$tag) {
$xml.=gaMakeXML($tag,$depth);
}
}
}
$depth--;
$spc=str_repeat(' ',$depth*2);
$xml.=$spc.'</'.$data['|_NAME_|'].">\n";
return $xml;
}
function gaParseXMLResponse ($xml) {
#gaParseXMLResponse parses a properly formatted XML string into a php
array structure
#the xml string is parsed into the global assoc array $gaXMLData
#$gaXMLData['children'] holds the top-level tags
#each tag has the following elements:
#name = the tag name
#attrs = array of attributes
#children = assoc array of tags (if any)
#If sibling tags have the same name they are placed in a numeric
array under the element of their shared name
#for example: $gaXMLData['children']['tag'][0]=the first tag of that
name
#If a child has a unique name it's simply under $gaXMLData['children']
['tag']=the tag by that name
#used to let the gaParseXMLContents function know the tag name
global $gaXMLData,$currParent,$depth;
$return=1;
# Create an XML parser
$xml_parser = xml_parser_create();
# Set the functions to handle opening and closing tags
xml_set_element_handler($xml_parser, "gaParseXMLStart",
"gaParseXMLEnd");
# Set the function to handle blocks of character data
xml_set_character_data_handler($xml_parser, "gaParseXMLContents");
# turn off uppercasing of tag names
xml_parser_set_option($xml_parser,XML_OPTION_CASE_FOLDING,0);
#initialize vars
$gaXMLData=array();
$gaXMLData['|_NAME_|']='data';
$gaXMLData['|_ATTRS_|']=array();
$gaXMLData['|_CONTENTS_|']=array();
$GLOBALS['currParent']=&$gaXMLData;
$depth=0;
# print "start parsing ";
#parse the xml
xml_parse($xml_parser, $xml)
#Handle errors in parsing
or $return =sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser));
xml_parser_free($xml_parser);
return $return;
}
function gaParseXMLStart ($parser,$tagName,$attrs) {
#$xmlTags is used to store the path to the current tag
#the length of $xmlTags tells us the depth of the current tag in the
xml data structure
#$gaXMLData is the data parsed into an array
#each element is an array with the elements:
#|_NAME_|: the tag name
#|_ATTRS_|: attributes of the tag - array where key = attr name and
value = attr value
#|_CONTENTS_|: an array of text inside the tag - one line per element
#[child-tag-names]: each unique chilg-tag-name results in a new child
array
#even if a child-tag-name is unique, it's values are still placed
under element 0 of that array for consistency
#for example, gaXMLData['tagname'][0]['|_ATTRS_|'] refers to the
attributes of the first tag of type 'tagname'
#this function uses references, but PHP refs are only aliases to the
same data, never pointers to other variables
#the only way to change where a global var points to is using the
GLOBALS array
global $xmlTags,$gaXMLData,$currParent,$currTag,$depth,$prevParents;
# print "start $tagName, cp=$currParent[name]\n";
#reference the current parent if this is a first child
$tagDepth=count($xmlTags);
if ($tagDepth>$depth) {
#tag is a first child
#set the value of the current tag under the current parent
gaParseXMLAddChild();
#push a ref to the current parent
$prevParents[]=&$currParent;
#set the current parent to the tag just added
$GLOBALS['currParent']=&$currParent[$currTag['|_NAME_|']]
[count($currParent[$currTag['|_NAME_|']])-1];
$depth++;
}
#set the current tag to a new array
$GLOBALS['currTag']=array();
$xmlTags[]=$tagName;
$currTag['|_NAME_|']=$tagName;
$currTag['|_ATTRS_|']=$attrs;
$currTag['|_CONTENTS_|']=array();
}
function gaParseXMLContents ($parser,$content) {
global $xmlTags,$gaXMLData,$currParent,$currTag,$depth;
if (count($xmlTags)>$depth) {
#add content to the child tag
$currTag['|_CONTENTS_|'][]=$content;
}
else {
#add content to the parent tag since we're not inside a child tag at
the moment
$currParent['|_CONTENTS_|'][]=$content;
}
}
function gaParseXMLEnd ($parser,$tagName) {
global $xmlTags,$gaXMLData,$currParent,$currTag,$depth,$prevParents;
# print "end $currTag[name] $tagName under $currParent[name]\n";
array_pop($xmlTags);
if (count($xmlTags)<$depth) {
#tag is the current parent and was already added to its parent
$depth--;
if ($depth) {
#set the current parante to it's parent
#foreach ($prevParents as $p) {print "pp=$p[name], ";} print "\n";
#foreach ($currParent as $key=>$val) {print "$key=$val, ";} print
"\n";
# print "<-change cp from $currParent[name] to ".
$prevParents[count($prevParents)-1]['name']."\n";
$GLOBALS['currParent']=&$prevParents[count($prevParents)-1];
#print "new cp = $currParent[name]\n";
#pop the last previous parent since it si now the current parent
array_pop($prevParents);
#foreach ($prevParents as $p) {print "pp=$p[name], ";} print "\n";
}
}
else {
#tag is a child and needs to be added to parent
gaParseXMLAddChild();
}
}
function gaParseXMLAddChild () {
global $currParent,$currTag;
#print "add $currTag[name] to $currParent[name]\n";
if (!isset($currParent[$currTag['|_NAME_|']])) {
#for consistency, it is assumed that all tags could have sibling
tags with the same tag name
#even if a tag name is unique among siblings, it's data is still
under [tagname][0][...]
$currParent[$currTag['|_NAME_|']]=array();
}
array_push($GLOBALS['currParent'][$currTag['|_NAME_|']],$currTag);
return 1;
}
?>