function copyObjectWithSortedKeys($value) { if (is_array($value)) { return array_map("copyObjectWithSortedKeys", $value); } else { if (is_object($value)) { $valueCopyWithSortedKeys = array(); foreach ($value as $key => $value) { $valueCopyWithSortedKeys[$key] = copyObjectWithSortedKeys($value); } ksort($valueCopyWithSortedKeys); return $valueCopyWithSortedKeys; } else { return $value; } } }
function pointrel20150417_storeMessage($apiRequest) { global $wpdb; $message = $apiRequest->message; // error_log("Called pointrel20150417_storeMessage with: " . json_encode($message)); $journalIdentifier = $apiRequest->journalIdentifier; // Check if topicIdentifier is not defined to avoid PHP warning if (isset($message->_topicIdentifier)) { $topicIdentifier = $message->_topicIdentifier; } else { $topicIdentifier = null; } faiIfNotAuthorized("write", $journalIdentifier, $topicIdentifier); $table_name = tableNameForJournal($journalIdentifier); $receivedTimestamp = getCurrentUniqueTimestamp(); // Need to calculate SHA and length without trace and in canonical form as utf-8 encoded $oldSHA256AndLength = $message->__pointrel_sha256AndLength; $oldTrace = $message->__pointrel_trace; if ($oldTrace && !is_array($oldTrace)) { wp_send_json(makeFailureResponse(400, "Bad Request: trace field should be an array if it is defined")); } if (!$oldTrace) { $oldTrace = array(); } // Calculate the sha256AndLength without the pointrel fields // Remove all "__pointrel_" tags from the top level, which includes sha256AndLength and trace and any other local metadata $cloneArray = $message; foreach ($cloneArray as $key => $value) { if (startsWith($key, "__pointrel_")) { unset($message->{$key}); } } $canonicalMessageWithoutHashAndTrace = copyObjectWithSortedKeys($message); // $canonicalFormInUTF8 = my_json_encode($canonicalMessageWithoutHashAndTrace, JSON_UNESCAPED_UNICODE); $canonicalFormInUTF8 = my_json_encode($canonicalMessageWithoutHashAndTrace); $sha256AndLength = hash('sha256', $canonicalFormInUTF8) . "_" . strlen($canonicalFormInUTF8); if ($oldSHA256AndLength && $oldSHA256AndLength !== $sha256AndLength) { error_log("Problem with new calculated SHA not matching old supplied one: {$oldSHA256AndLength} new: {$sha256AndLength} "); error_log("canonicalFormInUTF8 was: {$canonicalFormInUTF8}"); wp_send_json(makeFailureResponse(400, "Bad Request: sha256AndLength was supplied in message but it does not match the calculated value; old: {$oldSHA256AndLength} new: {$sha256AndLength}")); } if (isSHA256AndLengthIndexed($journalIdentifier, $sha256AndLength)) { wp_send_json(makeFailureResponse(409, "Conflict: The message already exists on the server", array("sha256AndLength" => $sha256AndLength))); } $message->__pointrel_sha256AndLength = $sha256AndLength; // TODO: Determine server name $serverName = "UNFINISHED_SERVER_NAME"; // Maybe could improve on this; see: http://stackoverflow.com/questions/3003145/how-to-get-the-client-ip-address-in-php $senderIPAddress = $_SERVER['REMOTE_ADDR']; $userIdentifier = wp_get_current_user()->user_login; $receivedTimestamp = getCurrentUniqueTimestamp(); // Put in some local information // TODO: More thinking about the meaning of a trace??? // TODO: Add more info about who stored this information // TODO: Maybe add other things, like requester IP or user identifier? $traceEntry = array("receivedByJournalIdentifier" => $journalIdentifier, "receivedByServer" => $serverName, "senderIPAddress" => $senderIPAddress, "userIdentifier" => $userIdentifier, "receivedTimestamp" => $receivedTimestamp); $oldTrace[] = $traceEntry; $message->__pointrel_trace = $oldTrace; $canonicalMessage = copyObjectWithSortedKeys($message); // $messageText = my_json_encode($canonicalMessage, JSON_UNESCAPED_UNICODE); $messageText = my_json_encode($canonicalMessage); // TODO: Check for empty topic and maybe act differently $topicSHA256 = hash('sha256', $topicIdentifier); $topicTimestamp = $message->_topicTimestamp; /* id int(9) UNSIGNED NOT NULL AUTO_INCREMENT, sha256_and_length char(73) NOT NULL, received_timestamp char(30) NOT NULL, topic_sha256 char(64) NOT NULL, topic_timestamp char(30) NOT NULL, message mediumtext NOT NULL, */ // TODO: Remove this // error_log("pointrel20150417_storeMessage: everything OK and ready to store -- but not storing for now as test"); // throw new Exception("storing disabled for now while test canonical JSON SHA calculation"); $wpdb->insert($table_name, array('sha256_and_length' => $sha256AndLength, 'received_timestamp' => $receivedTimestamp, 'topic_sha256' => $topicSHA256, 'topic_timestamp' => $topicTimestamp, 'message' => $messageText)); $insert_id = $wpdb->insert_id; // error_log("pointrel20150417_storeMessage inserted row " . $insert_id); $response = makeSuccessResponse(200, "Success", array('detail' => 'Wrote content', 'sha256AndLength' => $sha256AndLength, 'receivedTimestamp' => $receivedTimestamp, 'insert_id' => $insert_id)); wp_send_json($response); }