protected function end() { if ($this->profile) { Zotero_DB::profileEnd($this->objectLibraryID, true); } switch ($this->responseCode) { case 200: // Output a Content-Type header for the given format // // Note that this overrides any Content-Type set elsewhere. To force a content // type elsewhere, clear $this->queryParams['format'] when calling header() // manually. // // TODO: Check headers_list so that clearing the format parameter manually isn't // necessary? Performance? if (isset($this->queryParams['format'])) { Zotero_API::outputContentType($this->queryParams['format']); } break; case 301: case 302: case 303: // Handled in $this->redirect() break; case 401: header('WWW-Authenticate: Basic realm="Zotero API"'); header('HTTP/1.1 401 Unauthorized'); break; // PHP completes these automatically // PHP completes these automatically case 201: case 204: case 300: case 304: case 400: case 403: case 404: case 405: case 409: case 412: case 413: case 422: case 500: case 501: case 503: header("HTTP/1.1 " . $this->responseCode); break; case 428: header("HTTP/1.1 428 Precondition Required"); break; case 429: header("HTTP/1.1 429 Too Many Requests"); break; default: throw new Exception("Unsupported response code " . $this->responseCode); } if (isset($this->libraryVersion)) { if ($this->apiVersion >= 2) { header("Last-Modified-Version: " . $this->libraryVersion); } // Send notification if library has changed if ($this->isWriteMethod()) { if ($this->libraryVersion > Zotero_Libraries::getOriginalVersion($this->objectLibraryID)) { Zotero_Notifier::trigger('modify', 'library', $this->objectLibraryID); } } } if ($this->responseXML instanceof SimpleXMLElement) { if (!$this->responseCode) { $updated = (string) $this->responseXML->updated; if ($updated) { $updated = strtotime($updated); $ifModifiedSince = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] : false; $ifModifiedSince = strtotime($ifModifiedSince); if ($ifModifiedSince >= $updated) { header('HTTP/1.1 304 Not Modified'); exit; } $lastModified = substr(date('r', $updated), 0, -5) . "GMT"; header("Last-Modified: {$lastModified}"); } } $xmlstr = $this->responseXML->asXML(); // TEMP: Strip control characters $xmlstr = Zotero_Utilities::cleanString($xmlstr, true); $doc = new DOMDocument('1.0'); $doc->loadXML($xmlstr); $doc->formatOutput = true; echo $doc->saveXML(); } $this->logRequestTime(); self::addHeaders(); echo ob_get_clean(); exit; }
/** * @param object $object Zotero object (Zotero_Item, Zotero_Collection, Zotero_Search, Zotero_Setting) * @param object $json JSON object to check * @param array $requestParams * @param int $requireVersion If 0, don't require; if 1, require if there's * an object key property in the JSON; if 2, * always require */ public static function checkJSONObjectVersion($object, $json, $requestParams, $requireVersion) { $objectType = Zotero_Utilities::getObjectTypeFromObject($object); if (!in_array($objectType, array('item', 'collection', 'search', 'setting'))) { throw new Exception("Invalid object type"); } $oldKeyProp = $objectType . "Key"; $oldVersionProp = $objectType == 'setting' ? 'version' : $objectType . "Version"; $newKeyProp = 'key'; $newVersionProp = 'version'; if ($requestParams['v'] >= 3) { $keyProp = $newKeyProp; $versionProp = $newVersionProp; // Disallow old properties if (isset($json->{$oldKeyProp})) { throw new Exception("'{$oldKeyProp}' property is now '" . $newKeyProp . "'", Z_ERROR_INVALID_INPUT); } else { if (isset($json->{$oldVersionProp}) && $oldVersionProp != $newVersionProp) { throw new Exception("'{$oldVersionProp}' property is now '" . $newVersionProp . "'", Z_ERROR_INVALID_INPUT); } } } else { $keyProp = $oldKeyProp; $versionProp = $oldVersionProp; // Disallow new properties if (isset($json->{$newKeyProp})) { throw new Exception("Invalid property '{$newKeyProp}'", Z_ERROR_INVALID_INPUT); } else { if (isset($json->{$newVersionProp}) && $oldVersionProp != $newVersionProp) { throw new Exception("Invalid property '{$newVersionProp}'", Z_ERROR_INVALID_INPUT); } } } if (isset($json->{$versionProp})) { if ($requestParams['v'] < 2) { throw new Exception("Invalid property '{$versionProp}'", Z_ERROR_INVALID_INPUT); } if (!is_numeric($json->{$versionProp})) { throw new Exception("'{$versionProp}' must be an integer", Z_ERROR_INVALID_INPUT); } if (!isset($json->{$keyProp}) && $objectType != 'setting' && !$object->key) { throw new Exception("'{$versionProp}' is valid only if {$objectType} key is provided", Z_ERROR_INVALID_INPUT); } $originalVersion = Zotero_Libraries::getOriginalVersion($object->libraryID); $updatedVersion = Zotero_Libraries::getUpdatedVersion($object->libraryID); // Make sure the object hasn't been modified since the specified version if ($object->version > $json->{$versionProp}) { // Unless it was modified in this request if ($updatedVersion != $originalVersion && $object->version == $updatedVersion) { return; } throw new HTTPException(ucwords($objectType) . " has been modified since specified version " . "(expected {$json->{$versionProp}}, found {$object->version})", 412); } else { if ($json->{$versionProp} > 0 && !$object->version) { throw new HTTPException(ucwords($objectType) . " doesn't exist (expected version {$json->{$versionProp}}; use 0 instead)", 404); } } } else { if ($requireVersion == 1 && isset($json->{$keyProp})) { if ($objectType == 'setting') { throw new HTTPException("Either If-Unmodified-Since-Version or " . "'{$versionProp}' property must be provided", 428); } else { throw new HTTPException("Either If-Unmodified-Since-Version or " . "'{$versionProp}' property must be provided for " . "'{$keyProp}'-based writes", 428); } } else { if ($requireVersion == 2) { throw new HTTPException("Either If-Unmodified-Since-Version or " . "'{$versionProp}' property must be provided for " . "single-{$objectType} writes", 428); } } } }
public function settings() { if ($this->apiVersion < 2) { $this->e404(); } // Check for general library access if (!$this->permissions->canAccess($this->objectLibraryID)) { $this->e403(); } if ($this->isWriteMethod()) { Zotero_DB::beginTransaction(); // Check for library write access if (!$this->permissions->canWrite($this->objectLibraryID)) { $this->e403("Write access denied"); } // Make sure library hasn't been modified if (!$this->singleObject) { $libraryTimestampChecked = $this->checkLibraryIfUnmodifiedSinceVersion(); } Zotero_Libraries::updateVersionAndTimestamp($this->objectLibraryID); } // Single setting if ($this->singleObject) { $this->allowMethods(array('GET', 'PUT', 'DELETE')); $setting = Zotero_Settings::getByLibraryAndKey($this->objectLibraryID, $this->objectKey); if (!$setting) { if ($this->method == 'PUT') { $setting = new Zotero_Setting(); $setting->libraryID = $this->objectLibraryID; $setting->name = $this->objectKey; } else { $this->e404("Setting not found"); } } if ($this->isWriteMethod()) { if ($this->method == 'PUT') { $json = $this->jsonDecode($this->body); $objectVersionValidated = $this->checkSingleObjectWriteVersion('setting', $setting, $json); } $this->libraryVersion = Zotero_Libraries::getUpdatedVersion($this->objectLibraryID); // Update setting if ($this->method == 'PUT') { $changed = Zotero_Settings::updateFromJSON($setting, $json, $this->queryParams, $this->userID, $objectVersionValidated ? 0 : 2); // If not updated, return the original library version if (!$changed) { $this->libraryVersion = Zotero_Libraries::getOriginalVersion($this->objectLibraryID); Zotero_DB::rollback(); $this->e204(); } } else { if ($this->method == 'DELETE') { Zotero_Settings::delete($this->objectLibraryID, $this->objectKey); } else { throw new Exception("Unexpected method {$this->method}"); } } Zotero_DB::commit(); $this->e204(); } else { $this->libraryVersion = $setting->version; $json = $setting->toJSON(true, $this->queryParams); echo Zotero_Utilities::formatJSON($json); } } else { $this->allowMethods(array('GET', 'POST')); $this->libraryVersion = Zotero_Libraries::getUpdatedVersion($this->objectLibraryID); // Create a setting if ($this->method == 'POST') { $obj = $this->jsonDecode($this->body); $changed = Zotero_Settings::updateMultipleFromJSON($obj, $this->queryParams, $this->objectLibraryID, $this->userID, $this->permissions, $libraryTimestampChecked ? 0 : 1, null); // If not updated, return the original library version if (!$changed) { $this->libraryVersion = Zotero_Libraries::getOriginalVersion($this->objectLibraryID); } Zotero_DB::commit(); $this->e204(); } else { $settings = Zotero_Settings::search($this->objectLibraryID, $this->queryParams); $json = new stdClass(); foreach ($settings as $setting) { $json->{$setting->name} = $setting->toJSON(true, $this->queryParams); } echo Zotero_Utilities::formatJSON($json); } } $this->end(); }