patch_apply($dmp->patch_fromText($patchFromClient), $serverText); file_put_contents($serverTextFilename, $patchedServerText[0]); $patchedShadowText = $dmp->patch_apply($dmp->patch_fromText($patchFromClient), $shadowText); /* Make a diff between server text and shadow to get the edits to send * back to the client. */ $patchFromServer = $dmp->patch_toText($dmp->patch_make($patchedShadowText[0], $patchedServerText[0])); /* Apply it to the shadow. */ $patchedShadowText = $dmp->patch_apply($dmp->patch_fromText($patchFromServer), $patchedShadowText[0]); file_put_contents($shadowTextFilename, $patchedShadowText[0]); /* Release locks. */ flock($serverTextFilename, LOCK_UN); flock($shadowTextFilename, LOCK_UN); echo formatJSEND('success', $patchFromServer); break; case 'sendHeartbeat': updateHeartbeatMarker($_SESSION['user']); echo formatJSEND('success'); break; default: exit(formatJSEND('error', 'Unknown Action ' . $_POST['action'])); } // -------------------- /* Helper functions to make the marker filenames corresponding to the given * parameters. */ function makeRegisteredMarkerFilename($filename, $user) { $sanitizedFilename = str_replace('/', '_', $filename); validatePathOrDie($sanitizedFilename); validatePathOrDie($user); return BASE_PATH . '/data/' . $sanitizedFilename . '%%' . $user . '%%registered'; } function makeSelectionMarkerFilename($filename, $user) { $sanitizedFilename = str_replace('/', '_', $filename); validatePathOrDie($sanitizedFilename); validatePathOrDie($user); return $sanitizedFilename . '%%' . $user . '%%selection'; } function makeChangesMarkerFilename($filename, $user) { $sanitizedFilename = str_replace('/', '_', $filename); validatePathOrDie($sanitizedFilename); validatePathOrDie($user); return $sanitizedFilename . '%%' . $user . '%%changes'; } function makeShadowMarkerFilename($filename, $user) { $sanitizedFilename = str_replace('/', '_', $filename); validatePathOrDie($sanitizedFilename); validatePathOrDie($user); return BASE_PATH . '/data/' . $sanitizedFilename . '%%' . $username . '%%shadow'; } function makeServerTextMarkerFilename($filename) { $sanitizedFilename = str_replace('/', '_', $filename); validatePathOrDie($sanitizedFilename); validatePathOrDie($user); return BASE_PATH . '/data/' . $sanitizedFilename . '%%shadow'; } function makeHeartbeatMarkerFilename($user) { validatePathOrDie($user); return BASE_PATH . '/data/' . $user . '%%heartbeat'; } // TODO Put this in a more robust way in common.php /* Validate that a path does not contain '..' or stuff like that for security. */ function validatePathOrDie($path) { if (strstr($path, '/') || strstr($path, '\\') || strstr($path, '..') ) { // Security fault. die(); } } // -------------------- /* $filename must contain only the basename of the file. */ function isUserRegisteredForFile($user, $filename) { $marker = BASE_PATH . '/data/' . str_replace('/', '_', $filename) . '%%' . $user . '%%registered'; return file_exists($marker); } /* Touch the heartbeat marker file for the given user. Return true on * success, false on failure. */ function updateHeartbeatMarker($user) { $marker = BASE_PATH . '/data/' . $user . '%%heartbeat'; return touch($marker); } /* $filename must contain only the basename of the file. */ function getRegisteredUsersForFile($filename) { $usernames = array(); $markers = getMarkerFilesForFilename($filename); if (!empty($markers)) { foreach ($markers as $entry) { if (strpos($entry, 'registered')) { /* $entry is a marker file marking a registered user. * Extract the user name from the filename. */ $matches = array(); $entry = substr($entry, 0, strlen($entry) - 12); // Remove '%%registered' from $entry. preg_match('/\w+$/', $entry, $matches); if (count($matches) !== 1) { exit(formatJSEND('error', 'Unable To Match Username in getMarkerFilesForFilename')); } $usernames[] = $matches[0]; } } } return $usernames; } /* Return all marker files related to $filename. $filename must contain * only the basename of the file. */ function getMarkerFilesForFilename($filename) { $markers = array(); $basePath = BASE_PATH . '/data/'; if ($handle = opendir($basePath)) { $sanitizedFilename = str_replace('/', '_', $filename); while (false !== ($entry = readdir($handle))) { if (strpos($entry, $sanitizedFilename) !== false) { $markers[] = $entry; } } } return $markers; } /* Return the selection object, if any, for the given filename and user. * $filename must contain only the basename of the file. */ function getSelection($filename, $user) { $sanitizedFilename = str_replace('/', '_', $filename); $json = getJSON($sanitizedFilename . '%%' . $user . '%%selection'); return $json; } /* Return the list of changes, if any, for the given filename, user and * from the given revision number. * $filename must contain only the basename of the file. */ function getChanges($filename, $user, $fromRevision) { $sanitizedFilename = str_replace('/', '_', $filename); $json = getJSON($sanitizedFilename . '%%' . $user . '%%changes'); /* print_r(array_slice($json, $fromRevision, NULL, true)); */ return array_slice($json, $fromRevision, NULL, true); } /* Set the server shadow acquiring an exclusive lock on the file. $shadow * is a string. */ function setShadow($filename, $username, $shadow) { $sanitizedFilename = BASE_PATH . '/data/' . str_replace('/', '_', $filename) . '%%' . $username . '%%shadow'; file_put_contents($sanitizedFilename, $shadow, LOCK_EX); } /* Return the shadow for the given filename as a string or an empty string * if no shadow exists. */ function getShadow($filename) { $shadow = ''; $markers = getMarkerFilesForFilename($filename); if (!empty($markers)) { foreach ($markers as $entry) { if (strpos($entry, 'shadow')) { $shadow = file_get_contents($entry); print_r($shadow); } } } return $shadow; } function existsServerText($filename) { $sanitizedFilename = BASE_PATH . '/data/' . str_replace('/', '_', $filename) . '%%text'; return file_exists($sanitizedFilename); } /* Set the server text acquiring an exclusive lock on the file. $serverText * is a string. */ function setServerText($filename, $serverText) { $sanitizedFilename = BASE_PATH . '/data/' . str_replace('/', '_', $filename) . '%%text'; file_put_contents($sanitizedFilename, $serverText, LOCK_EX); } /* Return the server text for the given filename as a string or an empty string * if no server text exists. */ function getServerText($filename) { $serverText = ''; $markers = getMarkerFilesForFilename($filename); if (!empty($markers)) { foreach ($markers as $entry) { if (strpos($entry, 'text')) { $serverText = file_get_contents($entry); print_r($serverText); } } } return $serverText; } ?>