function delete_edges_with($source = '*', $label = '*', $destination = '*') { $paths = edges_with($source, $label, $destination); foreach ($paths as $link_path) { if (is_link($link_path)) { //Get the name of the parent label directory so that we can check whether it is empty after we delete the destination link. $label_path = dirname($link_path); //Remove the link itself. if (!unlink($link_path)) { $unlink_error = error_get_last(); exit_with_error_json("Deletion of link \"" . $link_path . "\" failed: " . $unlink_error['message']); } //If the label directory is now empty, delete it. $remaining_destinations = glob($label_path . PATH_SEPARATOR . NODE_NAME_WILDCARD, GLOB_ONLYDIR); if ($remaining_destinations === FALSE) { $glob_error = error_get_last(); exit_with_error_json("Check for destination symlinks in \"" . $label_path . "\" failed: " . $glob_error['message']); } if (count($remaining_destinations) < 1) { if (!rmdir($label_path)) { $rmdir_error = error_get_last(); exit_with_error_json("Deletion of empty label directory \"" . $label_path . "\" failed: " . $rmdir_error['message']); } //end of if we failed to delete the empty label directory } //end of if the label folder is now empty. } //end of if the link exists and is a symbolic link } //end of loop through all edges found }
function node_text_with_path($path) { $text_file_path = $path . FILE_PATH_SEPARATOR . TEXT_FILE_NAME; $text = file_get_contents($text_file_path); if ($text === FALSE) { $fgc_error = error_get_last(); exit_with_error_json("Attempt to read text from file \"" . $text_file_path . "\" failed: " . $fgc_error['message']); } return $text; }
function edges_with($source = '*', $label = '*', $destination = '*') { $link_path_pattern = NODE_PATH . $source . PATH_SEPARATOR . $label . PATH_SEPARATOR . $destination; $paths = glob($link_path_pattern); if ($paths === FALSE) { $glob_error = error_get_last(); exit_with_error_json("Search for paths matching pattern \"" . $link_path_pattern . "\" failed: " . $glob_error['message']); } //end of if we failed to search for edge links return $paths; }
function get_node_or_exit($node_role) { if (!isset($_REQUEST[$node_role])) { exit_with_error_json("The request did not include a " . $node_role . " node."); } $node = $_REQUEST[$node_role]; exit_on_bad_node_name($node); if (!is_dir(NODE_PATH . $node)) { exit_with_error_json("The requested " . $node_role . " node, " . $node . ", does not exist."); } return $node; }
function exit_on_bad_node_name($name) { //Check that the name includes only Perl word characters, i.e., alphanumeric characters and '_'s. //This is more strict than we need to be but safer than letting the user use every character other than '/' //and still flexible enough that the user can devise a wide variety of descriptive node names. $name_has_bad_char = preg_match('/[\\W]+/', $name); //Check that preg_match has not failed. if ($name_has_bad_char === FALSE) { switch (preg_last_error()) { case PREG_NO_ERROR: $error_message = "no error"; break; case PREG_INTERNAL_ERROR: $error_message = "internal error"; break; case PREG_BACKTRACK_LIMIT_ERROR: $error_message = "too many backtracks"; break; case PREG_RECURSION_LIMIT_ERROR: $error_message = "too many recursions"; break; case PREG_BAD_UTF8_ERROR: $error_message = "bad UTF8 character"; break; case PREG_BAD_UTF8_OFFSET_ERROR: $error_message = "bad UTF8 offset"; break; default: $error_message = "unknown error"; } //end of switch over different error codes for regular expression matcher exit_with_error_json("An error occurred during checking of the filename for bad characters: " . $error_message); } //end of if preg_match failed if ($name_has_bad_char) { exit_with_error_json("The name \"" . $name . "\" contains a character that was not a letter, number, or underscore."); } //Check that the name is not too long. if (strlen($name) > MAX_NAME_LENGTH) { exit_with_error_json("The name \"" . $name . "\" is longer than " . MAX_NAME_LENGTH . " bytes."); } //Check that the name is not the empty string. if (strlen($name) < 1) { exit_with_error_json("The name is an empty string."); } //Check that creating the directory will leave a safe amount of free disk space. //Since Linux keeps various temporary files around until it needs to clear them, this may not reflect all the memory potentially available. //if( disk_free_space('.') - strlen($name) < MEMORY_SAFETY_MARGIN) { // exit_with_error_json("Creating a file or directory named \"".$name."\" would leave fewer than ".MEMORY_SAFETY_MARGIN." bytes of disk space. ".disk_free_space('.')." bytes currently free."); //} }
//end of loop through paths } //end of if we have a target name //echo("array of view info arrays with distances:"); //print_r($view_array); function compare_closeness($view1, $view2) { $output = 0; //Try case-insensitive name similarity. if (!$output && isset($view1[DISTANCE_BY_NAME_CASE_INSENSITIVE])) { $output = $view1[DISTANCE_BY_NAME_CASE_INSENSITIVE] - $view2[DISTANCE_BY_NAME_CASE_INSENSITIVE]; //Then try case-sensitive name similarity. if (!$output) { $output = $view1[DISTANCE_BY_NAME] - $view2[DISTANCE_BY_NAME]; } } //If we did not receive a target name or got ties on both case-insensitive and case-sensitive comparisons, sort lexicographically. //Recall that names must be unique. if (!$output) { $output = strcmp($view1[NAME], $view2[NAME]); } return $output; } //end of function compare_closeness $result = usort($view_array, 'compare_closeness'); if ($result === FALSE) { exit_with_error_json("Attempt to sort views failed."); } //echo("array of view info arrays sorted:"); //print_r($view_array); exit(json_encode(array('results' => $view_array)));
<?php //SUMMARY: Given an HTTP request with the name of a view, deletes that view. require_once 'includes/consts.php'; require_once 'includes/exit_with_error_json.php'; require_once 'includes/exit_on_bad_node_name.php'; //Check that the request includes a name. if (!isset($_REQUEST[NAME])) { exit_with_error_json("The request did not contain a view name."); } //end of if no name provided $name = $_REQUEST[NAME]; //We hold views to the same naming conventions as we do nodes. exit_on_bad_node_name($name); $view_path = VIEW_PATH . $name . VIEW_FILE_TYPE; if (!unlink($view_path)) { $unlink_error = error_get_last(); exit_with_error_json("Deletion of file \"" . $view_path . "\" failed: " . $unlink_error['message']); } exit(json_encode(array('success' => TRUE)));
if (isset($node1[DISTANCE_BY_TEXT_CASE_INSENSITIVE])) { $output = $node1[DISTANCE_BY_TEXT_CASE_INSENSITIVE] - $node2[DISTANCE_BY_TEXT_CASE_INSENSITIVE]; //Then try case-sensitive text similarity. if (!$output) { $output = $node1[DISTANCE_BY_TEXT] - $node2[DISTANCE_BY_TEXT]; } } //If we had a target name but no target text or a tie when comparing by text, try case-insensitive name similarity. if (!$output && isset($node1[DISTANCE_BY_NAME_CASE_INSENSITIVE])) { $output = $node1[DISTANCE_BY_NAME_CASE_INSENSITIVE] - $node2[DISTANCE_BY_NAME_CASE_INSENSITIVE]; //Then try case-sensitive name similarity. if (!$output) { $output = $node1[DISTANCE_BY_NAME] - $node2[DISTANCE_BY_NAME]; } } //If we did not receive either kind of target or got ties on both targets, sort lexicographically by text. if (!$output) { $output = strcmp($node1[TEXT], $node2[TEXT]); } //If we are still tied, sort lexicographically by name. Recall that names must be unique. if (!$output) { $output = strcmp($node1[NAME], $node2[NAME]); } return $output; } //end of function compare_closeness $result = usort($node_array, 'compare_closeness'); if ($result === FALSE) { exit_with_error_json("Attempt to sort nodes failed."); } exit(json_encode(array('nodes' => $node_array)));
} //Give it permissions that will allow the scripts to work. if (chmod($view_path, FILE_PERMISSIONS) === FALSE) { $chmod_error = error_get_last(); exit_with_error_json("Setting of permissions of \"" . $view_path . "\" failed: " . $chmod_error['message']); } $dest_edit_history_path = EDIT_HISTORY_PATH . $name; //Serialize the edits. //For this to work, the top-level array must be associative. $dest_edits_assoc = array(); $dest_edits_assoc[UPDATES] = $dest_edits; $dest_edits_string = json_encode($dest_edits_assoc); if ($dest_edits_string === FALSE) { exit_with_error_json("Attempt to serialize edit history failed."); } //Check that we have enough free disk space. //This check produces false positives on Linux servers, because they tend to use free memory for temporary files and clear it as needed. //if( disk_free_space(".") - strlen($dest_edits_string) < MEMORY_SAFETY_MARGIN) { // exit_with_error_json("Creating the new edit history \"".$dest_edit_history_path."\" would leave fewer than ".MEMORY_SAFETY_MARGIN." bytes of disk space."); //} //Write the edits to the file. if (file_put_contents($dest_edit_history_path, $dest_edits_string, LOCK_EX) == FALSE) { $fpc_error = error_get_last(); exit_with_error_json("Attempt to write to file " . $dest_edit_history_path . " failed: " . $fpc_error['message']); } //Give it permissions that will allow the script to access the file in the future. if (chmod($dest_edit_history_path, FILE_PERMISSIONS) === FALSE) { $chmod_error = error_get_last(); exit_with_error_json("Setting of permissions of \"" . $dest_edit_history_path . "\" failed: " . $chmod_error['message']); } exit(json_encode(array('view_path' => $view_path)));
exit_with_error_json("Edit history file is incorrectly formatted, missing key " . UPDATES . "."); } $new_update_count = count($edits); //If there are more updates we have not yet sent, send them. //There should be, since the file was modified, but check just in case some other process somehow modified the file. if ($new_update_count > $update_count) { //If the client has received 0 edits, send all of them. // ...1 edits, send all but the first (index 0). // ...2 edits, send all but the first, and second (index 0 and index 1). // ...3 edits, send all but the first, second, and third (index 0, index 1, index 2). $new_edits_numeric = array_slice($edits, $update_count); $new_edits_assoc = array(); $new_edits_assoc[UPDATES] = $new_edits_numeric; $new_edits_string = json_encode($new_edits_assoc); if ($new_edits_string === FALSE) { exit_with_error_json("Attempt to serialize new edits failed: " . json_last_error()); } //Set the retry "The reconnection time to use when attempting to send the event." echo "retry: 1000\n"; //milliseconds //A stream broadcast must begin with "data:". echo "data: " . $new_edits_string; //A stream broadcast must end with an empty line. $should_send = TRUE; //We now have content to send. //Set update_count so we do not send the same updates again. $update_count = $new_update_count; } //end of if we have updates to send } //end of if the file has been modified since we last checked
exit_with_error_json("Creation of directory \"" . $node_path . "\" failed: " . $mkdir_error['message']); } //end of if directory creation failed } //end of if directory does not exist //If the request does not include a separate text string, use the directory name for the text content. if (isset($_REQUEST[TEXT])) { $text = $_REQUEST[TEXT]; } else { $text = $name; } //Do not allow PHP scripts in the text file. $safe_text = preg_replace('/<\\?php/', "<!notphp", $text); $text_file_path = $node_path . PATH_SEPARATOR . TEXT_FILE_NAME; //Take into account that, since we overwrite any old content, take that into account when we consider memory use. if (is_file($text_file_path)) { $existing_file_size = filesize($text_file_path); } else { $existing_file_size = 0; } //Check that the text will leave a safe amount of free disk space. if (disk_free_space(".") - strlen($safe_text) + $existing_file_size < MEMORY_SAFETY_MARGIN) { exit_with_error_json("Writing text \"" . $safe_text . "\" to file \"" . $text_file_path . "\" would leave fewer than " . MEMORY_SAFETY_MARGIN . " bytes of disk space."); } //Try to save the text to a file. $text_write_outcome = file_put_contents($text_file_path, $safe_text, LOCK_EX); if ($text_write_outcome === FALSE) { $fpc_error = error_get_last(); exit_with_error_json("Attempt to write text \"" . $safe_text . "\" to file \"" . $text_file_path . "\" failed: " . $fpc_error['message']); } exit(json_encode(array('text_file_path' => $text_file_path)));
$node_path = NODE_PATH . $name; $files = scandir($node_path); foreach ($files as $file) { //Skip over the loopback link to the directory itself and the parent directory backlink. if ($file == '.' || $file == '..') { continue; } //Create the path to the file relative to our current directory. $file_path = $node_path . '/' . $file; //For now, we only try to delete files instead of looking for non-label directories and trying to delete them recursively. if (is_file($file_path)) { if (!unlink($file_path)) { $unlink_error = error_get_last(); exit_with_error_json("Deletion of file \"" . $file_path . "\" failed: " . $unlink_error['message']); } //end of if file deletion failed } //end of if is a file } //end of deleting any files in the node directory //reminder: scandir includes '.' and '..' even when the directory is empty, so a directory with 1 item has 3 scandir results if (count(scandir($node_path)) >= 3) { exit_with_error_json("Directory \"" . $node_path . "\" contains something other than label directories and regular files, so we cannot delete it."); } //end of if the directory is still not empty if (!rmdir($node_path)) { $rmdir_error = error_get_last(); exit_with_error_json("Deletion of empty node directory \"" . $node_path . "\" failed: " . $rmdir_error['message']); } //end of if we failed to delete the empty node directory exit(json_encode(array('success' => TRUE)));
//It checks beforehand that all three directories exist as subdirectories of the nodes directory, //and that the link does not already exist. require_once 'includes/consts.php'; require_once 'includes/exit_with_error_json.php'; require_once 'includes/get_node_or_exit.php'; $source = get_node_or_exit(SOURCE); $label = get_node_or_exit(LABEL); $destination = get_node_or_exit(DESTINATION); //Since this is a relative symbolic link, we have to specify the destination relative to the location of the link. //In this case, that means going up two levels, //out of the label directory and out of the directory folder, //to the nodes directory then down to the destination directory. $target_path = '../../' . $destination; $label_path = NODE_PATH . $source . PATH_SEPARATOR . $label; $link_path = $label_path . PATH_SEPARATOR . $destination; if (is_link($link_path)) { exit_with_error_json("The link \"" . $link_path . "\"->\"" . $target_path . "\" already exists."); } if (!is_dir($label_path)) { if (mkdir($label_path, DIRECTORY_PERMISSIONS, true) === FALSE) { $mkdir_error = error_get_last(); exit_with_error_json("Creation of directory \"" . $label_path . "\" failed:" . $mkdir_error['message']); } //end of if we failed to make the label sub-directory } //end of if the label sub-directory does not exist if (!symlink($target_path, $link_path)) { $link_error = error_get_last(); exit_with_error_json("Creation of link \"" . $link_path . "\"->\"" . $target_path . "\" failed: " . $link_error['message']); } exit(json_encode(array('edge_href' => $link_path)));