<?
  
// example server-side import/export processor for dataSync extension
  //   http://www.gozer.org/mozilla/extensions/
  //
  //   datasync.server.url -> http://host/path/server.php

  // location to store data files. the directory (and files) must be writable
  // by the server and should be protected from (unauthorized) requests
  
$data "data";

  
// optional, but authentication and/or IP restrictions are recommended
  //   datasync.server.user -> username
  //   datasync.server.pass -> password
  
$user getenv('REMOTE_USER');

  function 
sendResponse($response$type$message$serial null) {
    global 
$action;

    
$xml simplexml_load_string("<dataSync version='0'/>");

    
$xml->addAttribute('response'$response);

    if (
$type)
      
$xml->addAttribute('type'$type);

    if (
$message)
      
$xml->addAttribute('message'$message);

    if (
$serial)
      
$xml->addAttribute('serial'$serial);

    
# likely an error or other message
    
if ($response != $action)
      
$xml->addAttribute('action'$action);

    echo 
$xml->asXML();
  }

  function 
sendError($type$message)
  {
    
sendResponse('error'$type$message);
  }

  function 
getFilePath($base)
  {
    global 
$data$user;

    return 
$data "/" . ($user "$user-" "") . $base ".xml";
  }

  
header("Content-type: application/xml");

  
$xml simplexml_load_string($HTTP_RAW_POST_DATA);

  if (!
$xml) {
    
sendError(null"Failed to parse post payload");
    exit;
  }

  
$type    basename($xml['type']);
  
$serial  $xml['serial'];
  
$hash    $xml['hash'];
  
$action  $xml['action'];
  
$version $xml['version'];

  if (empty(
$action)) {
    
sendError($type"Missing or empty action attribute");
    exit;
  }

  
$file getFilePath($action == 'updates' $action $type);

  if (
$action == 'import' || $action == 'updates') {
    if (
file_exists($file)) {
      
$xml simplexml_load_file($file);

      if (!
$xml) {
        
sendError($type"Failed to parse $action file");
        exit;
      }

      unset(
$xml['response']);
      
$xml->addAttribute('response'$action);

      echo 
$xml->asXML();
    } else if (
$action == 'import')
      
sendError($type"No previous export exits");
    else if (
$action == 'updates')
      
sendResponse($actionnullnull);

    exit;
  }

  
// -- export ----------------------------------------------------------------

  // don't overwrite the updates file
  
if ($type == 'updates') {
    
sendError($type"Reserved type ($type)");
    exit;
  }

  if (
$action != 'export') {
    
sendError($type"Unknown action ($action)");
    exit;
  }

  if (empty(
$serial)) {
    
sendError($type"Missing or empty serial attribute");
    exit;
  }

  if (empty(
$hash)) {
    
sendError($type"Missing or empty hash attribute");
    exit;
  }

  if (!
file_exists($data))
    
mkdir($data0700);

  if (
file_exists($file)) {
    
$old "$file.old";
    if (
file_exists($old))
      
unlink($old);
    
rename($file$old);
  }

  if (!(
$fp fopen($file"w"))) {
    
sendError($type"Failed to open $file");
    exit;
  }

  
$asXML $xml->asXML();

  
$bytes fputs($fp$asXML);

  
fclose($fp);

  
$len strlen($asXML);

  if (
$bytes != $len) {
    
sendError($type"Incomplete write ($bytes of $len bytes)");
    exit;
   }

  
// update versions file

  
$file getFilePath('updates');

  
$xml null;

  if (
file_exists($file))
    
$xml simplexml_load_file($file);

  if (!
$xml)
    
$xml simplexml_load_string("<dataSync version='0'/>");

  unset(
$xml->$type);
  
$xml->addChild($type$serial);

  if (!(
$fp fopen($file"w"))) {
    
sendError($type"Failed to open $file");
    exit;
  }

  
fputs($fp$xml->asXML());

  
fclose($fp);

  
sendResponse($action$type"$bytes bytes written"$serial);
?>