/** * Handle uploaded data, overwriting existing data */ public function upload() { $this->sessionCheck(); // Another session is either queued or writing — upload data won't be valid, // so client should wait and return to /updated with 'upload' flag Zotero_DB::beginTransaction(); if (Zotero_Sync::userIsReadLocked($this->userID) || Zotero_Sync::userIsWriteLocked($this->userID)) { Zotero_DB::commit(); $locked = $this->responseXML->addChild('locked'); $locked['wait'] = $this->getWaitTime($this->sessionID); $this->end(); } Zotero_DB::commit(); $this->clearWaitTime($this->sessionID); if (empty($_REQUEST['updateKey'])) { $this->error(400, 'INVALID_UPLOAD_DATA', 'Update key not provided'); } if ($_REQUEST['updateKey'] != Zotero_Users::getUpdateKey($this->userID)) { $this->e409("Server data has changed since last retrieval"); } // TODO: change to POST if (empty($_REQUEST['data'])) { $this->error(400, 'MISSING_UPLOAD_DATA', 'Uploaded data not provided'); } $xmldata =& $_REQUEST['data']; try { $doc = new DOMDocument(); $doc->loadXML($xmldata, LIBXML_PARSEHUGE); // For huge uploads, make sure notes aren't bigger than SimpleXML can parse if (strlen($xmldata) > 7000000) { $xpath = new DOMXPath($doc); $results = $xpath->query('/data/items/item/note[string-length(text()) > ' . Zotero_Notes::$MAX_NOTE_LENGTH . ']'); if ($results->length) { $noteElem = $results->item(0); $text = $noteElem->textContent; $libraryID = $noteElem->parentNode->getAttribute('libraryID'); $key = $noteElem->parentNode->getAttribute('key'); // UTF-8 (0xC2 0xA0) isn't trimmed by default $whitespace = chr(0x20) . chr(0x9) . chr(0xa) . chr(0xd) . chr(0x0) . chr(0xb) . chr(0xc2) . chr(0xa0); $excerpt = iconv("UTF-8", "UTF-8//IGNORE", Zotero_Notes::noteToTitle(trim($text), true)); $excerpt = trim($excerpt, $whitespace); // If tag-stripped version is empty, just return raw HTML if ($excerpt == '') { $excerpt = iconv("UTF-8", "UTF-8//IGNORE", preg_replace('/\\s+/', ' ', mb_substr(trim($text), 0, Zotero_Notes::$MAX_TITLE_LENGTH))); $excerpt = html_entity_decode($excerpt); $excerpt = trim($excerpt, $whitespace); } $msg = "=Note '" . $excerpt . "...' too long"; if ($key) { $msg .= " for item '" . $libraryID . "/" . $key . "'"; } throw new Exception($msg, Z_ERROR_NOTE_TOO_LONG); } } } catch (Exception $e) { $this->handleUploadError($e, $xmldata); } function relaxNGErrorHandler($errno, $errstr) { //Z_Core::logError($errstr); } set_error_handler('relaxNGErrorHandler'); set_time_limit(60); if (!$doc->relaxNGValidate(Z_ENV_MODEL_PATH . 'relax-ng/upload.rng')) { $id = substr(md5(uniqid(rand(), true)), 0, 10); $str = date("D M j G:i:s T Y") . "\n"; $str .= "IP address: " . $_SERVER['REMOTE_ADDR'] . "\n"; if (isset($_SERVER['HTTP_X_ZOTERO_VERSION'])) { $str .= "Version: " . $_SERVER['HTTP_X_ZOTERO_VERSION'] . "\n"; } $str .= "Error: RELAX NG validation failed\n\n"; $str .= $xmldata; if (!file_put_contents(Z_CONFIG::$SYNC_ERROR_PATH . $id, $str)) { error_log("Unable to save error report to " . Z_CONFIG::$SYNC_ERROR_PATH . $id); } $this->error(500, 'INVALID_UPLOAD_DATA', "Uploaded data not well-formed (Report ID: {$id})"); } restore_error_handler(); try { $xml = simplexml_import_dom($doc); $queue = true; if (Z_ENV_TESTING_SITE && !empty($_GET['noqueue'])) { $queue = false; } if ($queue) { $affectedLibraries = Zotero_Sync::parseAffectedLibraries($xmldata); // Relations-only uploads don't have affected libraries if (!$affectedLibraries) { $affectedLibraries = array(Zotero_Users::getLibraryIDFromUserID($this->userID)); } Zotero_Sync::queueUpload($this->userID, $this->sessionID, $xmldata, $affectedLibraries); try { Zotero_Processors::notifyProcessors('upload'); Zotero_Processors::notifyProcessors('error'); usleep(750000); } catch (Exception $e) { Z_Core::logError($e); } // Give processor a chance to finish while we're still here $this->uploadstatus(); } else { set_time_limit(210); $timestamp = Zotero_Sync::processUpload($this->userID, $xml); $this->responseXML['timestamp'] = $timestamp; $this->responseXML->addChild('uploaded'); $this->end(); } } catch (Exception $e) { $this->handleUploadError($e, $xmldata); } }
/** * Handle uploaded data, overwriting existing data */ public function upload() { $this->sessionCheck(); // Another session is either queued or writing — upload data won't be valid, // so client should wait and return to /updated with 'upload' flag Zotero_DB::beginTransaction(); if (Zotero_Sync::userIsReadLocked($this->userID) || Zotero_Sync::userIsWriteLocked($this->userID)) { Zotero_DB::commit(); $locked = $this->responseXML->addChild('locked'); $locked['wait'] = $this->getWaitTime($this->sessionID); $this->end(); } Zotero_DB::commit(); $this->clearWaitTime($this->sessionID); if (empty($_REQUEST['updateKey'])) { $this->error(400, 'INVALID_UPLOAD_DATA', 'Update key not provided'); } if ($_REQUEST['updateKey'] != Zotero_Users::getUpdateKey($this->userID)) { $this->e409("Server data has changed since last retrieval"); } // TODO: change to POST if (empty($_REQUEST['data'])) { $this->error(400, 'MISSING_UPLOAD_DATA', 'Uploaded data not provided'); } $xmldata =& $_REQUEST['data']; $doc = new DOMDocument(); $doc->loadXML($xmldata); function relaxNGErrorHandler($errno, $errstr) { //Z_Core::logError($errstr); } set_error_handler('relaxNGErrorHandler'); set_time_limit(60); if (!$doc->relaxNGValidate(Z_ENV_MODEL_PATH . 'relax-ng/upload.rng')) { $id = substr(md5(uniqid(rand(), true)), 0, 10); $str = date("D M j G:i:s T Y") . "\n"; $str .= "IP address: " . $_SERVER['REMOTE_ADDR'] . "\n"; if (isset($_SERVER['HTTP_X_ZOTERO_VERSION'])) { $str .= "Version: " . $_SERVER['HTTP_X_ZOTERO_VERSION'] . "\n"; } $str .= "Error: RELAX NG validation failed\n\n"; $str .= $xmldata; file_put_contents(Z_CONFIG::$SYNC_ERROR_PATH . $id, $str); $this->error(500, 'INVALID_UPLOAD_DATA', "Uploaded data not well-formed (Report ID: {$id})"); } restore_error_handler(); try { $xml = simplexml_import_dom($doc); $queue = true; if (Z_ENV_TESTING_SITE && !empty($_GET['noqueue'])) { $queue = false; } if ($queue) { $affectedLibraries = Zotero_Sync::parseAffectedLibraries($xmldata); // Relations-only uploads don't have affected libraries if (!$affectedLibraries) { $affectedLibraries = array(Zotero_Users::getLibraryIDFromUserID($this->userID)); } Zotero_Sync::queueUpload($this->userID, $this->sessionID, $xmldata, $affectedLibraries); try { Zotero_Processors::notifyProcessors('upload'); Zotero_Processors::notifyProcessors('error'); usleep(750000); } catch (Exception $e) { Z_Core::logError($e); } // Give processor a chance to finish while we're still here $this->uploadstatus(); } else { set_time_limit(210); $timestamp = Zotero_Sync::processUpload($this->userID, $xml); $this->responseXML['timestamp'] = $timestamp; $this->responseXML->addChild('uploaded'); Zotero_Processors::notifyProcessors('index'); $this->end(); } } catch (Exception $e) { $this->handleUploadError($e, $xmldata); } }