diff --git a/classes/Backup.php b/classes/Backup.php new file mode 100644 index 0000000..59c2102 --- /dev/null +++ b/classes/Backup.php @@ -0,0 +1,100 @@ +fileClass = new File(); + } + + public function makeBackup($fileLoc, $fileName, $contents) { + global $ftpSite, $t, $ICEcoder; + + $backupDirFormat = "Y-m-d"; + + // Establish the base, host and date dir parts... + $backupDirBase = str_replace("\\", "/", dirname(__FILE__)) . "/../data/backups/"; + $backupDirHost = isset($ftpSite) ? parse_url($ftpSite, PHP_URL_HOST) : "localhost"; + $backupDirDate = date($backupDirFormat); + + // Establish an array of dirs from base to our file location + $subDirsArray = explode("/", ltrim($fileLoc, "/")); + array_unshift($subDirsArray, $backupDirHost, $backupDirDate); + // Make any dirs that don't exist if full path isn't there + if (!is_dir($backupDirBase . implode("/", $subDirsArray))) { + $pathIncr = ""; + for ($i = 0; $i < count($subDirsArray); $i++) { + $pathIncr .= $subDirsArray[$i] . "/"; + // If this subdir isn't there, make it + if (!is_dir($backupDirBase . $pathIncr)) { + mkdir($backupDirBase . $pathIncr); + } + } + } + // We should have our dir path now so set that + $backupDir = $backupDirBase . implode("/", $subDirsArray); + // Work out an available filename (we postfix a number in parens) + for ($i = 1; $i < 1000000000; $i++) { + if (!file_exists($backupDir . '/' . $fileName . " (" . $i . ")")) { + $backupFileName = $fileName . " (" . $i . ")"; + $backupFileNum = $i; + $i = 1000000000; + } + } + + // Now save within that backup dir and clear the statcache + $fh = fopen($backupDir . "/" . $backupFileName, "w") or die($t['Sorry, cannot save...']); + fwrite($fh, $contents); + fclose($fh); + clearstatcache(); + + // Log the version count in an index file, which contains saved version counts + $backupIndex = $backupDirBase . $backupDirHost . "/" . $backupDirDate . "/.versions-index"; + // Have a version index already? Update contents + if (file_exists($backupIndex)) { + $versionsInfo = ""; + $versionsInfoOrig = getData($backupIndex); + $versionsInfoOrig = explode("\n", $versionsInfoOrig); + $replacedLine = false; + // For each line, either re-set number or simply include the line + for ($i = 0; $i < count($versionsInfoOrig); $i++) { + if (0 === strpos($versionsInfoOrig[$i], $fileLoc . "/" . $fileName . " = ")) { + $versionsInfo .= $fileLoc . "/" . $fileName . " = " . $backupFileNum . PHP_EOL; + $replacedLine = true; + } else { + $versionsInfo .= $versionsInfoOrig[$i] . PHP_EOL; + } + } + // Didn't find our line in the file? Add it to the end + if (!$replacedLine) { + $versionsInfo .= $fileLoc . "/" . $fileName . " = " . $backupFileNum . PHP_EOL; + } + // No version file yet, set the first line + } else { + $versionsInfo = $fileLoc . "/" . $fileName . " = " . $backupFileNum . PHP_EOL; + } + $versionsInfo = rtrim($versionsInfo, PHP_EOL); + $fh = fopen($backupIndex, 'w') or die($t['Sorry, cannot save...']); + fwrite($fh, $versionsInfo); + fclose($fh); + clearstatcache(); + + // Finally, clear any old backup dirs than user set X days (inclusive) + $backupDirsList = scandir($backupDirBase . $backupDirHost); + $backupDirsKeep = array(); + for ($i = 0; $i <= $ICEcoder["backupsDays"]; $i++) { + $backupDirsKeep[] = date($backupDirFormat, strtotime('-' . $i . ' day', strtotime($backupDirDate))); + } + for ($i = 0; $i < count($backupDirsList); $i++) { + if ("." !== $backupDirsList[$i] && ".." !== $backupDirsList[$i] && !in_array($backupDirsList[$i], $backupDirsKeep)) { + $this->fileClass->rrmdir($backupDirBase . $backupDirHost . "/" . $backupDirsList[$i]); + } + } + } +} diff --git a/classes/FTP.php b/classes/FTP.php new file mode 100644 index 0000000..a504711 --- /dev/null +++ b/classes/FTP.php @@ -0,0 +1,63 @@ +system = new System(); + } + + public function writeFile() { + global $fileLoc, $fileName, $ftpConn, $ftpRoot, $ftpHost, $ftpMode, $ICEcoder, $doNext, $filemtime; + + $ftpFilepath = ltrim($fileLoc . "/" . $fileName, "/"); + if (isset($_POST['changes'])) { + // Get existing file contents as lines + $loadedFile = toUTF8noBOM(ftpGetContents($ftpConn, $ftpRoot . $fileLoc . "/" . $fileName, $ftpMode), false); + $fileLines = explode("\n", str_replace("\r", "", $loadedFile)); + // Need to add a new line at the end of each because explode will lose them, + // want want to end up with same array that 'file($file)' produces for a local file + // - it keeps the line endings at the end of each array item + for ($i = 0; $i < count($fileLines); $i++) { + if ($i < count($fileLines) - 1) { + $fileLines[$i] .= $ICEcoder["lineEnding"]; + } + } + // Stitch changes onto it + $contents = $this->system->stitchChanges($fileLines, $_POST['changes']); + + // get old file contents and count stats on usage \n and \r there + // in this case we can keep line endings, which file had before, without + // making code version control systems going crazy about line endings change in whole file. + $unixNewLines = preg_match_all('/[^\r][\n]/u', $loadedFile); + $windowsNewLines = preg_match_all('/[\r][\n]/u', $loadedFile); + } else { + $contents = $_POST['contents']; + } + + // replace \r\n (Windows), \r (old Mac) and \n (Linux) line endings with whatever we chose to be lineEnding + $contents = str_replace("\r\n", $ICEcoder["lineEnding"], $contents); + $contents = str_replace("\r", $ICEcoder["lineEnding"], $contents); + $contents = str_replace("\n", $ICEcoder["lineEnding"], $contents); + if (isset($_POST['changes']) && ($unixNewLines > 0) || ($windowsNewLines > 0)) { + if ($unixNewLines > $windowsNewLines){ + $contents = str_replace($ICEcoder["lineEnding"], "\n", $contents); + } elseif ($windowsNewLines > $unixNewLines){ + $contents = str_replace($ICEcoder["lineEnding"], "\r\n", $contents); + } + } + // Write our file contents + if (!ftpWriteFile($ftpConn, $ftpFilepath, $contents, $ftpMode)) { + $doNext .= 'ICEcoder.message("Sorry, could not write ' . $ftpFilepath . ' at ' . $ftpHost . '");'; + } else { + $doNext .= 'ICEcoder.openFileMDTs[ICEcoder.selectedTab - 1]="' . $filemtime . '";'; + $doNext .= '(function() {var x = ICEcoder.openFileVersions; var y = ICEcoder.selectedTab-1; x[y] = "undefined" != typeof x[y] ? x[y] + 1 : 1})(); ICEcoder.updateVersionsDisplay();'; + } + } +} diff --git a/classes/File.php b/classes/File.php new file mode 100644 index 0000000..976e5e3 --- /dev/null +++ b/classes/File.php @@ -0,0 +1,687 @@ +system = new System(); + } + + public function check() { + global $file, $fileOrig, $docRoot, $iceRoot, $fileLoc, $fileName, $error, $errorStr, $errorMsg; + // Replace pipes with slashes, then establish the actual name as we may have HTML entities in filename + $file = html_entity_decode(str_replace("|", "/", $file)); + + // Put the original $file var aside for use + $fileOrig = $file; + + // Trim any +'s or spaces from the end of file + $file = rtrim(rtrim($file, '+'), ' '); + + // Also remove [NEW] from $file, we can consider $_GET['action'] or $fileOrig to pick that up + $file = preg_replace('/\[NEW\]$/', '', $file); + + // Make each path in $file a full path (; separated list) + $allFiles = explode(";", $file); + for ($i = 0; $i < count($allFiles); $i++) { + if (false === strpos($allFiles[$i],$docRoot) && "getRemoteFile" !== $_GET['action']) { + $allFiles[$i] = str_replace("|", "/", $docRoot . $iceRoot . $allFiles[$i]); + } + }; + $file = implode(";", $allFiles); + + // Establish the $fileLoc and $fileName (used in single file cases, eg opening. Multiple file cases, eg deleting, is worked out in that loop) + $fileLoc = substr(str_replace($docRoot, "", $file), 0, strrpos(str_replace($docRoot, "", $file), "/")); + $fileName = basename($file); + + // Check through all files to make sure they're valid/safe + $allFiles = explode(";", $file); + for ($i = 0; $i < count($allFiles); $i++) { + + // Uncomment to alert and console.log the action and file, useful for debugging + // echo ";alert('" . xssClean($_GET['action'], "html") . " : " . $allFiles[$i] . "');console.log('" . xssClean($_GET['action'], "html") . " : " . $allFiles[$i] . "');"; + + $bannedFileFound = false; + for ($j = 0; $j < count($_SESSION['bannedFiles']); $j++) { + $thisFile = str_replace("*", "", $_SESSION['bannedFiles'][$j]); + if ("" != $thisFile && false !== strpos($allFiles[$i], $thisFile)) { + $bannedFileFound = true; + } + } + + // Die if the file requested isn't something we expect + if ( + // On the banned file/dir list + ($bannedFileFound) || + // A local folder that isn't the doc root or starts with the doc root + ("getRemoteFile" !== $_GET['action'] && !isset($ftpSite) && + rtrim($allFiles[$i], "/") !== rtrim($docRoot, "/") && + 0 !== strpos(realpath(rtrim(dirname($allFiles[$i]), "/")), realpath(rtrim($docRoot, "/"))) + ) || + // Or a remote URL that doesn't start http + ("getRemoteFile" === $_GET['action'] && 0 !== strpos($allFiles[$i], "http")) + ) { + $error = true; + $errorStr = "true"; + $errorMsg = "Sorry! - problem with file requested"; + }; + } + } + + public function updateUI($doNext) { + global $fileLoc, $fileName; + + // Reload file manager, rename tab & remove old file highlighting if it was a new file + if (isset($_POST['newFileName']) && "" != $_POST['newFileName']) { + $doNext .= 'ICEcoder.selectedFiles=[]; ICEcoder.updateFileManagerList(\'add\', \'' . $fileLoc . '\', \'' . $fileName . '\', false, false, false, \'file\');'; + $doNext .= 'ICEcoder.renameTab(ICEcoder.selectedTab, \'' . $fileLoc . "/" . $fileName . '\');'; + if (!strpos($_REQUEST['file'], "[NEW]")) { + // We're saving as a new file, so unhighlight the old name in the file manager if visible + $doNext .= "fileLink = ICEcoder.filesFrame.contentWindow.document.getElementById('" . str_replace("/", "|", $fileLoc) . "|". basename($_REQUEST['file']). "');"; + $doNext .= "if (fileLink) {fileLink.style.backgroundColor = ICEcoder.tabBGnormal; fileLink.style.color = ICEcoder.tabFGnormalFile};"; + } + } + + return $doNext; + } + + public function updateFileManager($action, $fileLoc, $fileName, $perms, $oldFile, $uploaded, $fileOrFolder) { + global $doNext; + $doNext .= "ICEcoder.selectedFiles=[]; ICEcoder.updateFileManagerList('" . + $action . "', '" . + $fileLoc . "', '" . + $fileName . "', '" . + $perms . "', '" . + $oldFile . "', '" . + $uploaded . "', '" . + $fileOrFolder . "');"; + + return $doNext; + } + + public function load() { + global $file, $fileLoc, $fileName, $t, $ftpConn, $ftpHost, $ftpLogin, $ftpRoot, $ftpUser, $ftpMode; + echo 'action="load";'; + $lineNumber = max(isset($_REQUEST['lineNumber']) ? intval($_REQUEST['lineNumber']) : 1, 1); + // Check this file isn't on the banned list at all + $canOpen = true; + for ($i = 0; $i < count($_SESSION['bannedFiles']); $i++) { + if ("" !== str_replace("*", "", $_SESSION['bannedFiles'][$i]) && false !== strpos($file, str_replace("*", "", $_SESSION['bannedFiles'][$i]))) { + $canOpen = false; + } + } + + if (false === $canOpen) { + echo 'fileType="nothing"; parent.parent.ICEcoder.message(\'' . $t['Sorry, could not...'] . ' ' . $fileLoc . "/" . $fileName . '\');'; + } elseif (isset($ftpSite) || file_exists($file)) { + $finfo = "text"; + // Determine what to do based on mime type + if (!isset($ftpSite) && function_exists('finfo_open')) { + $finfoMIME = finfo_open(FILEINFO_MIME); + $finfo = finfo_file($finfoMIME, $file); + finfo_close($finfoMIME); + } else { + $fileExt = explode(" ", pathinfo($file, PATHINFO_EXTENSION)); + $fileExt = $fileExt[0]; + if (false !== array_search($fileExt, ["gif", "jpg", "jpeg", "png"])) { + $finfo = "image"; + }; + if (false !== array_search($fileExt, ["doc", "docx", "ppt", "rtf", "pdf", "zip", "tar", "gz", "swf", "asx", "asf", "midi", "mp3", "wav", "aiff", "mov", "qt", "wmv", "mp4", "odt", "odg", "odp"])) { + $finfo = "other"; + }; + } + if (0 === strpos($finfo, "text") || 0 === strpos($finfo, "application/xml") || false !== strpos($finfo, "empty")) { + echo 'fileType="text";'; + echo 'parent.parent.ICEcoder.shortURL = parent.parent.ICEcoder.thisFileFolderLink = "' . $fileLoc . "/" . $fileName . '";'; + + // Get file over FTP? + if (isset($ftpSite)) { + ftpStart(); + // Show user warning if no good connection + if (!$ftpConn || !$ftpLogin) { + die('parent.parent.ICEcoder.message("Sorry, no FTP connection to ' . $ftpHost . ' for user ' . $ftpUser . '");parent.parent.ICEcoder.serverMessage();parent.parent.ICEcoder.serverQueue("del",0);'); + } + // Get our file contents and close the FTP connection + $loadedFile = toUTF8noBOM(ftpGetContents($ftpConn, $ftpRoot . $fileLoc . "/" . $fileName, $ftpMode), false); + ftpEnd(); + // Get local file + } else { + $loadedFile = toUTF8noBOM(getData($file), true); + } + $encoding = ini_get("default_charset"); + if ("" == $encoding) { + $encoding = "UTF-8"; + } + // Get content and set HTML entities on it according to encoding + $loadedFile = htmlentities($loadedFile, ENT_COMPAT, $encoding); + // Remove \r chars and replace \n with carriage return HTML entity char + $loadedFile = preg_replace('/\\r/', '', $loadedFile); + $loadedFile = preg_replace('/\\n/', ' ', $loadedFile); + echo '