'400 Bad Request', '401' => '401 Unauthorized', '404' => '404 Not Found', '412' => '412 Precondition Failed', '503' => '503 Service Unavailable'); header('HTTP/1.1 ' . $headers{$code},true,$code); if ($code == 401) { header('WWW-Authenticate: Basic realm="Weave"'); } exit(json_encode($message)); } header("Content-type: application/json"); #get the http auth user data $auth_user = array_key_exists('PHP_AUTH_USER', $_SERVER) ? $_SERVER['PHP_AUTH_USER'] : null; $auth_pw = array_key_exists('PHP_AUTH_PW', $_SERVER) ? $_SERVER['PHP_AUTH_PW'] : null; if (is_null($auth_user) || is_null($auth_pw)) { /* CGI/FCGI auth workarounds */ $auth_str = null; if (array_key_exists('Authorization', $_SERVER)) /* Standard fastcgi configuration */ $auth_str = $_SERVER['Authorization']; else if (array_key_exists('AUTHORIZATION', $_SERVER)) /* Alternate fastcgi configuration */ $auth_str = $_SERVER['AUTHORIZATION']; else if (array_key_exists('HTTP_AUTHORIZATION', $_SERVER)) /* IIS/ISAPI and newer (yet to be released) fastcgi */ $auth_str = $_SERVER['HTTP_AUTHORIZATION']; else if (array_key_exists('REDIRECT_HTTP_AUTHORIZATION', $_SERVER)) /* mod_rewrite - per-directory internal redirect */ $auth_str = $_SERVER['REDIRECT_HTTP_AUTHORIZATION']; if (!is_null($auth_str)) { /* Basic base64 auth string */ if (preg_match('/Basic\s+(.*)$/', $auth_str)) { $auth_str = substr($auth_str, 6); $auth_str = base64_decode($auth_str, true); if ($auth_str != FALSE) { $tmp = explode(':', $auth_str); if (count($tmp) == 2) { $auth_user = $tmp[0]; $auth_pw = $tmp[1]; } } } } } $server_time = round(microtime(1), 2); header("X-Weave-Timestamp: " . $server_time); #Basic path extraction and validation. No point in going on if these are missing $path = '/'; if (!empty($_SERVER['PATH_INFO'])) { $path = $_SERVER['PATH_INFO']; } else if (!empty($_SERVER['ORIG_PATH_INFO'])) { $path = $_SERVER['ORIG_PATH_INFO']; } $path = substr($path, 1); #chop the lead slash list($version, $username, $function, $collection, $id) = explode('/', $path.'//'); if ($version != '0.5' && $version != '1.0') report_problem('Function not found', 404); # Lowercase username before checking path $username = strtolower($username); $auth_user = strtolower($auth_user); if (!$username) report_problem(3, 400); if ($auth_user != $username) report_problem(5, 401); #quick check to make sure that any non-storage function calls are just using GET if ($function != 'storage' && $_SERVER['REQUEST_METHOD'] != 'GET') report_problem(1, 400); #only a get has meaning without a collection (GET returns a collection list) if (!$collection && $_SERVER['REQUEST_METHOD'] != 'GET') report_problem(1, 400); #storage requires a collection to have been passed in. Info requires a directive if (!$collection) report_problem(1, 400); #hook up with the db try { $db = new WeaveStorage($username); } catch(Exception $e) { report_problem($e->getMessage(), $e->getCode()); } #Auth the user try { if (!$db->authenticate_user($auth_pw)) report_problem('Authentication failed', '401'); } catch(Exception $e) { report_problem($e->getMessage(), $e->getCode()); } #user passes, onto actually getting the data if ($_SERVER['REQUEST_METHOD'] == 'GET') { if ($function == 'info') { switch ($collection) { case 'quota': exit(json_encode(array($db->get_storage_total()))); case 'collections': exit(json_encode($db->get_collection_list_with_timestamps())); case 'collection_counts': exit(json_encode($db->get_collection_list_with_counts())); default: report_problem(1, 400); } } elseif ($function == 'storage') { if ($id) #retrieve a single record { try { $wbo = $db->retrieve_objects($collection, $id, 1); #get the full contents of one record } catch(Exception $e) { report_problem($e->getMessage(), $e->getCode()); } if (count($wbo) > 0) echo $wbo[0]->json(); else report_problem("record not found", 404); } else #retrieve a batch of records. Sadly, due to potential record sizes, have the storage object stream the output... { $full = array_key_exists('full', $_GET) ? $_GET['full'] : null; $outputter = new WBOJsonOutput($full); if (array_key_exists('HTTP_ACCEPT', $_SERVER) && !preg_match('/\*\/\*/', $_SERVER['HTTP_ACCEPT']) && !preg_match('/application\/json/', $_SERVER['HTTP_ACCEPT'])) { if (preg_match('/application\/whoisi/', $_SERVER['HTTP_ACCEPT'])) { header("Content-type: application/whoisi"); $outputter->set_format('whoisi'); } elseif (preg_match('/application\/newlines/', $_SERVER['HTTP_ACCEPT'])) { header("Content-type: application/newlines"); $outputter->set_format('newlines'); } } try { $ids = $db->retrieve_objects($collection, null, $full, $outputter, array_key_exists('parentid', $_GET) ? $_GET['parentid'] : null, array_key_exists('predecessorid', $_GET) ? $_GET['predecessorid'] : null, array_key_exists('newer', $_GET) ? $_GET['newer'] : null, array_key_exists('older', $_GET) ? $_GET['older'] : null, array_key_exists('sort', $_GET) ? $_GET['sort'] : null, array_key_exists('limit', $_GET) ? $_GET['limit'] : null, array_key_exists('offset', $_GET) ? $_GET['offset'] : null, array_key_exists('ids', $_GET) ? explode(',', $_GET['ids']) : null, array_key_exists('index_above', $_GET) ? $_GET['index_above'] : null, array_key_exists('index_below', $_GET) ? $_GET['index_below'] : null, array_key_exists('depth', $_GET) ? $_GET['depth'] : null ); } catch(Exception $e) { report_problem($e->getMessage(), $e->getCode()); } } } } else if ($_SERVER['REQUEST_METHOD'] == 'PUT') #add a single record to the server { $putdata = fopen("php://input", "r"); $json = ''; while ($data = fread($putdata,2048)) {$json .= $data;}; $wbo = new wbo(); if (!$wbo->extract_json($json)) report_problem(6, 400); if (array_key_exists('HTTP_X_IF_UNMODIFIED_SINCE', $_SERVER) && $db->get_max_timestamp($collection) > round((float)$_SERVER['HTTP_X_IF_UNMODIFIED_SINCE'], 2)) report_problem(4, 412); #use the url if the json object doesn't have an id if (!$wbo->id() && $id) { $wbo->id($id); } $wbo->collection($collection); $wbo->modified($server_time); #current microtime if ($wbo->validate()) { try { #if there's no payload (as opposed to blank), then update the metadata if ($wbo->payload_exists()) $db->store_object($wbo); else $db->update_object($wbo); } catch (Exception $e) { report_problem($e->getMessage(), $e->getCode()); } echo json_encode($wbo->modified()); } else { report_problem(8, 400); } } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { #stupid php being helpful with input data... $putdata = fopen("php://input", "r"); $jsonstring = ''; while ($data = fread($putdata,2048)) {$jsonstring .= $data;} $json = json_decode($jsonstring, true); if ($json === null) report_problem(6, 400); if (array_key_exists('HTTP_X_IF_UNMODIFIED_SINCE', $_SERVER) && $db->get_max_timestamp($collection) > round((float)$_SERVER['HTTP_X_IF_UNMODIFIED_SINCE'], 2)) report_problem(4, 412); $success_ids = array(); $failed_ids = array(); try { $db->begin_transaction(); } catch(Exception $e) { report_problem($e->getMessage(), $e->getCode()); } foreach ($json as $wbo_data) { $wbo = new wbo(); if (!$wbo->extract_json($wbo_data)) { $failed_ids[$wbo->id()] = $wbo->get_error(); continue; } $wbo->collection($collection); $wbo->modified($server_time); if ($wbo->validate()) { try { #if there's no payload (as opposed to blank), then update the metadata if ($wbo->payload_exists()) { $db->store_object($wbo); } else { $db->update_object($wbo); } } catch (Exception $e) { $failed_ids[$wbo->id()] = $e->getMessage(); continue; } $success_ids[] = $wbo->id(); } else { $failed_ids[$wbo->id()] = $wbo->get_error(); } } $db->commit_transaction(); echo json_encode(array('success' => $success_ids, 'failed' => $failed_ids)); } else if ($_SERVER['REQUEST_METHOD'] == 'DELETE') { if (array_key_exists('HTTP_X_IF_UNMODIFIED_SINCE', $_SERVER) && $db->get_max_timestamp($collection) > round((float)$_SERVER['HTTP_X_IF_UNMODIFIED_SINCE'], 2)) report_problem(4, 412); $timestamp = round(microtime(1), 2); if ($id) { try { $db->delete_object($collection, $id); } catch(Exception $e) { report_problem($e->getMessage(), $e->getCode()); } echo json_encode($timestamp); } else { try { $db->delete_objects($collection, null, array_key_exists('parentid', $_GET) ? $_GET['parentid'] : null, array_key_exists('predecessorid', $_GET) ? $_GET['predecessorid'] : null, array_key_exists('newer', $_GET) ? $_GET['newer'] : null, array_key_exists('older', $_GET) ? $_GET['older'] : null, array_key_exists('sort', $_GET) ? $_GET['sort'] : null, array_key_exists('limit', $_GET) ? $_GET['limit'] : null, array_key_exists('offset', $_GET) ? $_GET['offset'] : null, array_key_exists('ids', $_GET) ? explode(',', $_GET['ids']) : null, array_key_exists('index_above', $_GET) ? $_GET['index_above'] : null, array_key_exists('index_below', $_GET) ? $_GET['index_below'] : null ); } catch(Exception $e) { report_problem($e->getMessage(), $e->getCode()); } echo json_encode($timestamp); } } else { #bad protocol. There are protocols left? HEAD, I guess. report_problem(1, 400); } #The datasets we might be dealing with here are too large for sticking it all into an array, so #we need to define a direct-output method for the storage class to use. If we start producing multiples #(unlikely), we can put them in their own class. class WBOJsonOutput { private $_full = null; private $_comma_flag = 0; private $_output_format = 'json'; function __construct ($full = null) { $this->_full = $full; } function set_format($format) { $this->_output_format = $format; } function output($sth) { if (($rowcount = $sth->rowCount()) > 0) { header('X-Weave-Records: ' . $rowcount); } if ($this->_output_format == 'newlines') { return $this->output_newlines($sth); } elseif ($this->_output_format == 'whoisi') { return $this->output_whoisi($sth); } else { return $this->output_json($sth); } } function output_json($sth) { echo '['; while ($result = $sth->fetch(PDO::FETCH_ASSOC)) { if ($this->_comma_flag) { echo ','; } else { $this->_comma_flag = 1; } if ($this->_full) { $wbo = new wbo(); $wbo->populate($result); echo $wbo->json(); } else echo json_encode($result{'id'}); } echo ']'; return 1; } function output_whoisi($sth) { while ($result = $sth->fetch(PDO::FETCH_ASSOC)) { if ($this->_full) { $wbo = new wbo(); $wbo->populate($result); $output = $wbo->json(); } else $output = json_encode($result{'id'}); echo pack('N', mb_strlen($output, '8bit')) . $output; } return 1; } function output_newlines($sth) { while ($result = $sth->fetch(PDO::FETCH_ASSOC)) { if ($this->_full) { $wbo = new wbo(); $wbo->populate($result); echo preg_replace('/\n/', '\u000a', $wbo->json()); } else echo json_encode($result{'id'}); echo "\n"; } return 1; } } ?>