private function handleUploadError(Exception $e, $xmldata) { $msg = $e->getMessage(); if ($msg[0] == '=') { $msg = substr($msg, 1); $explicit = true; // TODO: more specific error messages } else { $explicit = false; } switch ($e->getCode()) { case Z_ERROR_TAG_TOO_LONG: case Z_ERROR_COLLECTION_TOO_LONG: break; default: Z_Core::logError($msg); } if (!$explicit && Z_ENV_TESTING_SITE) { switch ($e->getCode()) { case Z_ERROR_COLLECTION_NOT_FOUND: case Z_ERROR_CREATOR_NOT_FOUND: case Z_ERROR_ITEM_NOT_FOUND: case Z_ERROR_TAG_TOO_LONG: case Z_ERROR_LIBRARY_ACCESS_DENIED: case Z_ERROR_TAG_LINKED_ITEM_NOT_FOUND: break; default: throw $e; } $id = 'N/A'; } else { $id = substr(md5(uniqid(rand(), true)), 0, 8); $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 .= $msg; switch ($e->getCode()) { // Don't log uploaded data for some errors case Z_ERROR_TAG_TOO_LONG: case Z_ERROR_FIELD_TOO_LONG: case Z_ERROR_NOTE_TOO_LONG: case Z_ERROR_COLLECTION_TOO_LONG: break; default: $str .= "\n\n" . $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); } } Zotero_DB::rollback(true); switch ($e->getCode()) { case Z_ERROR_LIBRARY_ACCESS_DENIED: preg_match('/[Ll]ibrary ([0-9]+)/', $e->getMessage(), $matches); $libraryID = $matches ? $matches[1] : null; $this->error(400, 'LIBRARY_ACCESS_DENIED', "Cannot make changes to library (Report ID: {$id})", array('libraryID' => $libraryID)); break; case Z_ERROR_ITEM_NOT_FOUND: case Z_ERROR_COLLECTION_NOT_FOUND: case Z_ERROR_CREATOR_NOT_FOUND: error_log($e); $this->error(500, "FULL_SYNC_REQUIRED", "Please perform a full sync in the Sync->Reset pane of the Zotero preferences. (Report ID: {$id})"); break; case Z_ERROR_TAG_TOO_LONG: $message = $e->getMessage(); preg_match("/Tag '(.+)' too long/s", $message, $matches); if ($matches) { $name = $matches[1]; $this->error(400, "TAG_TOO_LONG", "Tag '" . mb_substr($name, 0, 50) . "…' too long", array(), array("tag" => $name)); } break; case Z_ERROR_COLLECTION_TOO_LONG: $message = $e->getMessage(); preg_match("/Collection '(.+)' too long/s", $message, $matches); if ($matches) { $name = $matches[1]; $this->error(400, "COLLECTION_TOO_LONG", "Collection '" . mb_substr($name, 0, 50) . "…' too long", array(), array("collection" => $name)); } break; case Z_ERROR_NOTE_TOO_LONG: preg_match("/Note '(.+)' too long(?: for item '(.+)\\/(.+)')?/s", $msg, $matches); if ($matches) { $name = $matches[1]; $libraryID = false; if (isset($matches[2])) { $libraryID = (int) $matches[2]; $itemKey = $matches[3]; if (Zotero_Libraries::getType($libraryID) == 'group') { $groupID = Zotero_Groups::getGroupIDFromLibraryID($libraryID); $group = Zotero_Groups::get($groupID); $libraryName = $group->name; } else { $libraryName = false; } } else { $itemKey = ''; } $showNoteKey = false; if (isset($_SERVER['HTTP_X_ZOTERO_VERSION'])) { require_once '../model/ToolkitVersionComparator.inc.php'; $showNoteKey = ToolkitVersionComparator::compare($_SERVER['HTTP_X_ZOTERO_VERSION'], "4.0.27") < 0; } if ($showNoteKey) { $this->error(400, "ERROR_PROCESSING_UPLOAD_DATA", "The note '" . mb_substr($name, 0, 50) . "…' in " . ($libraryName === false ? "your library " : "the group '{$libraryName}' ") . "is too long to sync to zotero.org.\n\n" . "Search for the excerpt above or copy and paste " . "'{$itemKey}' into the Zotero search bar. " . "Shorten the note, or delete it and empty the Zotero " . "trash, and then try syncing again."); } else { $this->error(400, "NOTE_TOO_LONG", "The note '" . mb_substr($name, 0, 50) . "…' in " . ($libraryName === false ? "your library " : "the group '{$libraryName}' ") . "is too long to sync to zotero.org.\n\n" . "Shorten the note, or delete it and empty the Zotero " . "trash, and then try syncing again.", [], $libraryID ? ["item" => $libraryID . "/" . $itemKey] : []); } } break; case Z_ERROR_FIELD_TOO_LONG: preg_match("/(.+) field value '(.+)\\.\\.\\.' too long(?: for item '(.+)')?/s", $msg, $matches); if ($matches) { $fieldName = $matches[1]; $value = $matches[2]; if (isset($matches[3])) { $parts = explode("/", $matches[3]); $libraryID = (int) $parts[0]; $itemKey = $parts[1]; if (Zotero_Libraries::getType($libraryID) == 'group') { $groupID = Zotero_Groups::getGroupIDFromLibraryID($libraryID); $group = Zotero_Groups::get($groupID); $libraryName = "the group '" . $group->name . "'"; } else { $libraryName = "your personal library"; } } else { $libraryName = "one of your libraries"; $itemKey = false; } $this->error(400, "ERROR_PROCESSING_UPLOAD_DATA", "The {$fieldName} field value '{$value}…' in {$libraryName} is " . "too long to sync to zotero.org.\n\n" . "Search for the excerpt above " . ($itemKey === false ? "using " : "or copy and paste " . "'{$itemKey}' into ") . "the Zotero search bar. " . "Shorten the field, or delete the item and empty the " . "Zotero trash, and then try syncing again."); } break; case Z_ERROR_ARRAY_SIZE_MISMATCH: $this->error(400, 'DATABASE_TOO_LARGE', "Databases of this size cannot yet be synced. Please check back soon. (Report ID: {$id})"); break; case Z_ERROR_TAG_LINKED_ITEM_NOT_FOUND: $this->error(400, 'WRONG_LIBRARY_TAG_ITEM', "Error processing uploaded data (Report ID: {$id})"); break; case Z_ERROR_SHARD_READ_ONLY: case Z_ERROR_SHARD_UNAVAILABLE: $this->error(503, 'SERVER_ERROR', Z_CONFIG::$MAINTENANCE_MESSAGE); break; } if (strpos($msg, "Lock wait timeout exceeded; try restarting transaction") !== false || strpos($msg, "MySQL error: Deadlock found when trying to get lock; try restarting transaction") !== false) { $this->error(500, 'TIMEOUT', "Sync upload timed out. Please try again in a few minutes. (Report ID: {$id})"); } if (strpos($msg, "Data too long for column 'xmldata'") !== false) { $this->error(400, 'DATABASE_TOO_LARGE', "Databases of this size cannot yet be synced. Please check back soon. (Report ID: {$id})"); } // On certain messages, send 400 to prevent auto-retry if (strpos($msg, " too long") !== false || strpos($msg, "First and last name are empty") !== false) { $this->error(400, 'ERROR_PROCESSING_UPLOAD_DATA', $explicit ? $msg : "Error processing uploaded data (Report ID: {$id})"); } if (preg_match("/Incorrect datetime value: '([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2})' " . "for column 'date(Added|Modified)'/", $msg, $matches)) { if (isset($_SERVER['HTTP_X_ZOTERO_VERSION'])) { require_once '../model/ToolkitVersionComparator.inc.php'; if (ToolkitVersionComparator::compare($_SERVER['HTTP_X_ZOTERO_VERSION'], "2.1rc1") < 0) { $msg = "Invalid timestamp '{$matches[1]}' in uploaded data. Upgrade to Zotero 2.1rc1 when available to fix automatically."; } else { $msg = "Invalid timestamp '{$matches[1]}' in uploaded data. Sync again to correct automatically."; } } else { $msg = "Invalid timestamp '{$matches[1]}' in uploaded data. Upgrade to Zotero 2.1rc1 when available to fix automatically."; } $this->error(400, 'INVALID_TIMESTAMP', $msg); } $this->error(500, 'ERROR_PROCESSING_UPLOAD_DATA', $explicit ? $msg : "Error processing uploaded data (Report ID: {$id})"); }
private function handleUploadError(Exception $e, $xmldata) { $msg = $e->getMessage(); if ($msg[0] == '=') { $msg = substr($msg, 1); $explicit = true; // TODO: more specific error messages } else { $explicit = false; } switch ($e->getCode()) { case Z_ERROR_TAG_TOO_LONG: break; default: Z_Core::logError($msg); } if (true || !$explicit) { if (Z_ENV_TESTING_SITE) { switch ($e->getCode()) { case Z_ERROR_COLLECTION_NOT_FOUND: case Z_ERROR_CREATOR_NOT_FOUND: case Z_ERROR_ITEM_NOT_FOUND: case Z_ERROR_TAG_TOO_LONG: case Z_ERROR_LIBRARY_ACCESS_DENIED: case Z_ERROR_TAG_LINKED_ITEM_NOT_FOUND: break; default: throw $e; } $id = 'N/A'; } else { $id = substr(md5(uniqid(rand(), true)), 0, 8); $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 .= $e; switch ($e->getCode()) { // Don't log uploaded data for some errors case Z_ERROR_TAG_TOO_LONG: break; default: $str .= "\n\n" . $xmldata; } file_put_contents(Z_CONFIG::$SYNC_ERROR_PATH . $id, $str); } } Zotero_DB::rollback(true); switch ($e->getCode()) { case Z_ERROR_LIBRARY_ACCESS_DENIED: preg_match('/[Ll]ibrary ([0-9]+)/', $e->getMessage(), $matches); $libraryID = $matches ? $matches[1] : null; $this->error(400, 'LIBRARY_ACCESS_DENIED', "Cannot make changes to library (Report ID: {$id})", array('libraryID' => $libraryID)); break; case Z_ERROR_ITEM_NOT_FOUND: case Z_ERROR_COLLECTION_NOT_FOUND: case Z_ERROR_CREATOR_NOT_FOUND: $this->error(500, "FULL_SYNC_REQUIRED", "Please perform a full sync in the Sync->Reset pane of the Zotero preferences. (Report ID: {$id})"); break; case Z_ERROR_TAG_TOO_LONG: $message = $e->getMessage(); preg_match("/Tag '(.+)' too long/s", $message, $matches); if ($matches) { $name = $matches[1]; $this->error(400, "TAG_TOO_LONG", "Tag '" . mb_substr($name, 0, 50) . "…' too long", array(), array("tag" => $name)); } break; case Z_ERROR_COLLECTION_TOO_LONG: $message = $e->getMessage(); preg_match("/Collection '(.+)' too long/s", $message, $matches); if ($matches) { $name = $matches[1]; $this->error(400, "COLLECTION_TOO_LONG", "Collection '" . mb_substr($name, 0, 50) . "…' too long", array(), array("collection" => $name)); } break; case Z_ERROR_ARRAY_SIZE_MISMATCH: $this->error(400, 'DATABASE_TOO_LARGE', "Databases of this size cannot yet be synced. Please check back soon. (Report ID: {$id})"); break; case Z_ERROR_TAG_LINKED_ITEM_NOT_FOUND: $this->error(400, 'WRONG_LIBRARY_TAG_ITEM', "Error processing uploaded data (Report ID: {$id})"); break; case Z_ERROR_SHARD_READ_ONLY: case Z_ERROR_SHARD_UNAVAILABLE: $this->error(503, 'SERVER_ERROR', Z_CONFIG::$MAINTENANCE_MESSAGE); break; } if (strpos($msg, "Lock wait timeout exceeded; try restarting transaction") !== false || strpos($msg, "MySQL error: Deadlock found when trying to get lock; try restarting transaction") !== false) { $this->error(500, 'TIMEOUT', "Sync upload timed out. Please try again in a few minutes. (Report ID: {$id})"); } if (strpos($msg, "Data too long for column 'xmldata'") !== false) { $this->error(400, 'DATABASE_TOO_LARGE', "Databases of this size cannot yet be synced. Please check back soon. (Report ID: {$id})"); } // On certain messages, send 400 to prevent auto-retry if (strpos($msg, " too long") !== false || strpos($msg, "First and last name are empty") !== false) { $this->error(400, 'ERROR_PROCESSING_UPLOAD_DATA', $explicit ? $msg : "Error processing uploaded data (Report ID: {$id})"); } if (preg_match("/Incorrect datetime value: '([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2})' " . "for column 'date(Added|Modified)'/", $msg, $matches)) { if (isset($_SERVER['HTTP_X_ZOTERO_VERSION'])) { require_once '../model/ToolkitVersionComparator.inc.php'; if (ToolkitVersionComparator::compare($_SERVER['HTTP_X_ZOTERO_VERSION'], "2.1rc1") < 0) { $msg = "Invalid timestamp '{$matches[1]}' in uploaded data. Upgrade to Zotero 2.1rc1 when available to fix automatically."; } else { $msg = "Invalid timestamp '{$matches[1]}' in uploaded data. Sync again to correct automatically."; } } else { $msg = "Invalid timestamp '{$matches[1]}' in uploaded data. Upgrade to Zotero 2.1rc1 when available to fix automatically."; } $this->error(400, 'INVALID_TIMESTAMP', $msg); } $this->error(500, 'ERROR_PROCESSING_UPLOAD_DATA', $explicit ? $msg : "Error processing uploaded data (Report ID: {$id})"); }