From abd38899f6f1fcf7c8f16eb83356c8ce1070cf71 Mon Sep 17 00:00:00 2001 From: dakkar Date: Thu, 17 Mar 2011 00:01:25 +0000 Subject: import from http://tobyelliott.wordpress.com/2009/09/11/weave-minimal-server/ --- index.php | 503 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 503 insertions(+) create mode 100644 index.php (limited to 'index.php') diff --git a/index.php b/index.php new file mode 100644 index 0000000..d8da439 --- /dev/null +++ b/index.php @@ -0,0 +1,503 @@ + '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; + } +} +?> -- cgit v1.2.3