From d642ed0fab06f3e78c4c006ef1c9a20e11a9cbe8 Mon Sep 17 00:00:00 2001 From: mattpass Date: Tue, 27 Jul 2021 09:12:03 +0100 Subject: [PATCH 01/21] Setup screen checks update box if needed, set bool not string --- lib/login.php | 3 ++- lib/settings.php | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/login.php b/lib/login.php index 6a4530e7..1f843ed5 100644 --- a/lib/login.php +++ b/lib/login.php @@ -103,7 +103,8 @@ echo '
' . $t['disable further registrations'] . '
'; } if ("" === $ICEcoder["password"] || true === $ICEcoder["multiUser"]) { - echo '
' . $t['auto-check for updates'] . '
'; + $tickCheckUpdates = true === $ICEcoder['checkUpdates'] ? " checked" : ""; + echo '
' . $t['auto-check for updates'] . '
'; } if (false === $ICEcoder["multiUser"]) { echo '
' . $t['multi-user'] . '?
';}; ?> diff --git a/lib/settings.php b/lib/settings.php index 25bbbba9..624262ef 100644 --- a/lib/settings.php +++ b/lib/settings.php @@ -228,7 +228,7 @@ // If the password hasn't been set and we're setting it if ("" === $ICEcoder["password"] && true === isset($_POST['submit']) && -1 < strpos($_POST['submit'], "set password")) { $password = generateHash($_POST['password']); - $settingsClass->updateConfigUsersSettings($settingsFile, ["password" => $password, "checkUpdates" => $_POST["checkUpdates"]]); + $settingsClass->updateConfigUsersSettings($settingsFile, ["password" => $password, "checkUpdates" => isset($_POST["checkUpdates"])]); $settingsClass->createIPSettingsFileIfNotExist(); if (true === isset($_POST['disableFurtherRegistration'])) { $settingsClass->updateConfigGlobalSettings(['enableRegistration' => false]); From fef19276903ecb391cb537176355a57451f8e4c6 Mon Sep 17 00:00:00 2001 From: Matt Pass Date: Mon, 25 Oct 2021 12:02:25 +0100 Subject: [PATCH 02/21] Update README.md to use S3 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2abc35ef..580c7088 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ICEcoder is a browser based code editor, which provides a modern approach to building websites. By allowing you to code directly within the web browser, online or offline, it means you only need one program (your browser) to develop sites, plus can test on actual web servers. After development, you can also maintain the website easily, all of which make for speedy and smart development. -ICEcoder code editor +ICEcoder code editor ### Requirements From 517be5a5e0b590ad74fbe2b94b6934971b6dd0b0 Mon Sep 17 00:00:00 2001 From: Matt Pass Date: Wed, 24 Nov 2021 13:26:42 +0000 Subject: [PATCH 03/21] Change plugin icon path --- lib/plugins-manager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins-manager.php b/lib/plugins-manager.php index 0d18d03b..653236d5 100644 --- a/lib/plugins-manager.php +++ b/lib/plugins-manager.php @@ -242,7 +242,7 @@ function deletePlugin($dir) { } $reloadExtra = "true" === $pluginsData[$i]['reload'] ? '
' . $t['Reload after install...'] . '' : ''; - echo ''.$pluginsData[$i]['name'] . ''; + echo ''.$pluginsData[$i]['name'] . ''; echo '' . $pluginsData[$i]['name'] . $reloadExtra . ''; $styleExtra = (1 === $i % 2 || $i === count($pluginsData) - 1) ? "0" : "30px"; echo '' . $installUninstallButton . ''; From d6124b08115939ac06f88ff90f96a2a11561175b Mon Sep 17 00:00:00 2001 From: Matt Pass Date: Fri, 17 Dec 2021 17:23:18 +0000 Subject: [PATCH 04/21] Fix for installed plugin images dir --- lib/plugins-manager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins-manager.php b/lib/plugins-manager.php index 653236d5..fe16e2d9 100644 --- a/lib/plugins-manager.php +++ b/lib/plugins-manager.php @@ -63,7 +63,7 @@ $ICEcoder["plugins"][] = [ $pluginsData[$_GET['plugin']]['name'], - $pluginsData[$_GET['plugin']]['icon'], + str_replace("images/", "plugins/", $pluginsData[$_GET['plugin']]['icon']), $pluginsData[$_GET['plugin']]['style'], $pluginsData[$_GET['plugin']]['URL'], $pluginsData[$_GET['plugin']]['target'], From 99f5d1a7c9e4e1829713fcc353ac0a78a844e25b Mon Sep 17 00:00:00 2001 From: Jamie Slome Date: Sun, 16 Jan 2022 03:40:08 +0000 Subject: [PATCH 05/21] Create SECURITY.md --- SECURITY.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..f2562d0d --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,5 @@ +# Security Policy + +## Reporting a Vulnerability + +Please report security issues to `info@icecoder.net` \ No newline at end of file From 51cf24b2a39138e6a7b5739ef59eb38cd7c39763 Mon Sep 17 00:00:00 2001 From: Matt Pass Date: Mon, 17 Jan 2022 12:38:29 +0000 Subject: [PATCH 06/21] rXSS cleaned username in editor info display --- editor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor.php b/editor.php index 417ba43f..7b7fa26f 100644 --- a/editor.php +++ b/editor.php @@ -150,7 +150,7 @@ ?>


-

+

From 509b5b8fb8877cf5ddcbe39194ae983e7e55b750 Mon Sep 17 00:00:00 2001 From: Matt Pass Date: Sat, 14 May 2022 16:21:20 +0100 Subject: [PATCH 07/21] PHP v7.0 fallback added re session_create_id --- lib/settings-common.php | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/settings-common.php b/lib/settings-common.php index e3ede92c..cb5e9ed5 100644 --- a/lib/settings-common.php +++ b/lib/settings-common.php @@ -25,15 +25,24 @@ ini_set('session.httponly', true); // Only allow http protocol (ie, not JS) access to the cookie ini_set('session.cookie_httponly', true); // Only allow cookie via http protocol (ie, not JS) access to the cookie // ini_set('session.save_path', dirname(__FILE__) . '/../tmp'); // Localise the session files to /tmp - if(false === isset($_COOKIE['ICEcoder'])) { - $_COOKIE['ICEcoder'] = session_create_id(); - } - session_id($_COOKIE['ICEcoder']); + if (false === isset($_COOKIE['ICEcoder'])) { + // PHP v7.1+ + if (function_exists('session_create_id')) { + $_COOKIE['ICEcoder'] = session_create_id(); + session_id($_COOKIE['ICEcoder']); + // PHP v7.0 fallback + } else { + session_start(); + $_COOKIE['ICEcoder'] = session_id(); + } + } if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') { ini_set('session.cookie_secure', '1'); // Only allows access to session ID when protocol is HTTPS, switched on under 'if https' condition } - session_start(); // Finally, start the session! + if (false === isset($_SESSION)) { + session_start(); + } if (false === isset($_SESSION['csrf'])){ session_regenerate_id(true); // Create a new ID to help prevent fixation & hijacking $_COOKIE['ICEcoder'] = session_id(); From e21c16e1eeeeb177cfdb7092832b0c4f7c838662 Mon Sep 17 00:00:00 2001 From: Matt Pass Date: Sat, 21 May 2022 13:15:52 +0100 Subject: [PATCH 08/21] No selectNext if selected already on findOnInput --- assets/js/icecoder.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/assets/js/icecoder.js b/assets/js/icecoder.js index 4c224b64..dd53e02d 100644 --- a/assets/js/icecoder.js +++ b/assets/js/icecoder.js @@ -2899,10 +2899,14 @@ var ICEcoder = { }, findOnInput: function() { + let thisCM, selectNext; // Realtime finding - only action for finding in current doc if ("" !== get('find').value && t['this document'] === document.findAndReplace.target.value) { - // Considers selecting next on value input, according to user setting - ICEcoder.findReplace(get('find').value, true === ICEcoder.selectNextOnFindInput, false, false); + // Get CM pane + thisCM = this.getThisCM(); + // Consider selecting next on value input, according to not having result selected already and user setting + selectNext = thisCM.getSelection() !== get('find').value && true === ICEcoder.selectNextOnFindInput; + ICEcoder.findReplace(get('find').value, selectNext, false, false); get("find").focus(); // Reset results display } else { From 958e91a67f594474299a01be35830172e1eb12d5 Mon Sep 17 00:00:00 2001 From: Matt Pass Date: Sun, 29 May 2022 14:48:53 +0100 Subject: [PATCH 09/21] Set editor to fixed position --- assets/css/icecoder.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/css/icecoder.css b/assets/css/icecoder.css index 02b207af..3ac2e21e 100644 --- a/assets/css/icecoder.css +++ b/assets/css/icecoder.css @@ -74,7 +74,7 @@ h2 {font-size: 18px; font-weight: normal; color: #fff} .files .tools .error {background: #800} .files .tools .info {background: #080} -.editor {position: absolute; display: inline-block; top: 0; left: 15px; width: 2400px} +.editor {position: fixed; display: inline-block; top: 0; left: 15px; width: 2400px} .editor .tabsBar {display: inline-block; height: 27px; width: 2400px; margin-top: 15px; padding-left: 53px; background: #fff} .tabsBar .tab {position: absolute; display: none; height: 15px; padding: 6px 8px 6px 9px; border-right: 1px solid #ddd; color: #fff; white-space: nowrap; overflow: hidden; cursor: pointer; z-index: 1; transition: width, left 0.15s ease-in-out; From e720ba5d5a6b3ec02f16de1d7a62e572c7bf1a46 Mon Sep 17 00:00:00 2001 From: mattpass Date: Sat, 11 Jun 2022 21:53:45 +0100 Subject: [PATCH 10/21] Dynamic assets path, tweak and fix in settings, ignore .idea --- .gitignore | 1 + assets/css/icecoder.css | 1 + assets/js/icecoder.js | 9 +++--- classes/File.php | 2 +- classes/Settings.php | 7 +++-- editor.php | 22 +++++++-------- files.php | 8 +++--- images/file-manager-icons.png | Bin 15788 -> 0 bytes index.php | 26 ++++++++--------- lib/auto-logout-warning.php | 8 ++++-- lib/backup-versions.php | 18 +++++++----- lib/bug-report.php | 8 ++++-- lib/help.php | 8 ++++-- lib/login.php | 12 +++++--- lib/multiple-results.php | 8 ++++-- lib/plugins-manager.php | 8 ++++-- lib/properties.php | 8 ++++-- lib/requirements.php | 8 +++--- lib/settings-screen.php | 51 ++++++++++++++++++++-------------- lib/settings-update.php | 3 +- terminal.php | 2 +- 21 files changed, 133 insertions(+), 85 deletions(-) delete mode 100644 images/file-manager-icons.png diff --git a/.gitignore b/.gitignore index f00fed8e..c932383a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.idea/* data/* !data/.gitkeep plugins/* diff --git a/assets/css/icecoder.css b/assets/css/icecoder.css index 3ac2e21e..56983d30 100644 --- a/assets/css/icecoder.css +++ b/assets/css/icecoder.css @@ -13,6 +13,7 @@ h2 {font-size: 18px; font-weight: normal; color: #fff} .blackMask {position: fixed; display: table; width: 100%; height: 100%; top: 0; left: 0; visibility: hidden; background-color: rgba(0,0,0,0.8); text-align: center; z-index: 100} .blackMask .popupVCenter {#position: absolute; display: table-cell; #top: 50%; vertical-align: middle; text-align: center} .popupVCenter .popup {#position: relative; #top: -50%; text-align: center; color: #fff; font-size: 10px} +.popupVCenter .popup .imgDisplay {border: solid 10px #fff; max-width: 700px; max-height: 500px; background-color: #000; background-image: url('http://github.com/images/checkerboard.png')} .floatingContainer {position: absolute; top: 0; left: 0; width: 55px; height: 55px; visibility: hidden; border: solid 1px #444; image-rendering: pixelated} .floatingContainer:before {position: absolute; display: inline-block; width: 3px; height: 3px; left: 25px; top: 25px; content: ''; border: solid 1px #b00} diff --git a/assets/js/icecoder.js b/assets/js/icecoder.js index dd53e02d..da7c4b4e 100644 --- a/assets/js/icecoder.js +++ b/assets/js/icecoder.js @@ -10,8 +10,9 @@ var ICEcoder = { // INIT // ==== - // URL we're viewing ICEcoder from + // URLs we're viewing ICEcoder and its assets from iceLoc: window.location.origin + window.location.pathname.replace(/\/$/, ""), + assetsLoc: get('icecoderJSFile').dataset.assetsRoot, // Define settings filesW: 250, // Width of files pane @@ -4429,7 +4430,7 @@ var ICEcoder = { this.openFiles.push(shortURL); // Setup a new tab - closeTabLink = ''; + closeTabLink = ''; get('tab' + (this.openFiles.length)).style.display = "inline-block"; fileName = this.openFiles[this.openFiles.length - 1]; fileExt = fileName.substr(fileName.lastIndexOf(".") + 1); @@ -4477,7 +4478,7 @@ var ICEcoder = { this.openFiles[tabNum - 1] = newName; // Setup a new tab - closeTabLink = ''; + closeTabLink = ''; fileName = this.openFiles[tabNum - 1]; fileExt = fileName.substr(fileName.lastIndexOf(".") + 1); get('tab' + tabNum).innerHTML = closeTabLink + "" + fileName.slice(fileName.lastIndexOf("/")).replace(/\//, ""); @@ -5301,7 +5302,7 @@ var ICEcoder = { "height": 55, "top": -55, "left": 0, - "title": "

Code editor awesomeness ...in your browser", + "title": "

Code editor awesomeness ...in your browser", "message": "View the quick start tutorial? (Well worthwhile!) or skip it.", "button": "view tutorial" }, diff --git a/classes/File.php b/classes/File.php index e9522a05..b392607f 100644 --- a/classes/File.php +++ b/classes/File.php @@ -234,7 +234,7 @@ public function returnLoadImageScript() { parent.parent.document.getElementById(\'blackMask\').style.visibility = "visible"; parent.parent.document.getElementById(\'mediaContainer\').innerHTML = "" + - " 700 || this.naturalHeight > 500) ? \', ' .$t['displayed at'] . '\' + this.width + \' x \' + this.height : \'\'; document.getElementById(\'imgInfo\').innerHTML += \' (\' + this.naturalWidth + \' x \' + this.naturalHeight + reducedImgMsg + \')\'; ICEcoder.initCanvasImage(this); ICEcoder.interactCanvasImage(this)\">
" + + " 700 || this.naturalHeight > 500) ? \', ' .$t['displayed at'] . '\' + this.width + \' x \' + this.height : \'\'; document.getElementById(\'imgInfo\').innerHTML += \' (\' + this.naturalWidth + \' x \' + this.naturalHeight + reducedImgMsg + \')\'; ICEcoder.initCanvasImage(this); ICEcoder.interactCanvasImage(this)\">
" + "
" + "' . $fileLoc . "/" . $fileName . '" + "

" + diff --git a/classes/Settings.php b/classes/Settings.php index e7172b03..4c82d261 100644 --- a/classes/Settings.php +++ b/classes/Settings.php @@ -7,8 +7,10 @@ class Settings public function __construct() { // Set version number and document root as core settings - $this->versionNo = "8.1"; - $this->docRoot = $_SERVER['DOCUMENT_ROOT']; + // Defaults to the right + $this->versionNo = "8.1"; // "8.1"; + $this->docRoot = $_SERVER['DOCUMENT_ROOT']; // $_SERVER['DOCUMENT_ROOT'] + $this->assetsRoot = "assets"; // "assets" (relative or absolute) } public function getCoreDetails() @@ -16,6 +18,7 @@ public function getCoreDetails() return [ "versionNo" => $this->versionNo, "docRoot" => $this->docRoot, + "assetsRoot" => $this->assetsRoot, ]; } diff --git a/editor.php b/editor.php index 7b7fa26f..e9160a0a 100644 --- a/editor.php +++ b/editor.php @@ -10,16 +10,16 @@ ICEcoder <?php echo $ICEcoder["versionNo"];?> editor - - - + + + - + "> - - - + + + - - - + + + diff --git a/files.php b/files.php index 95a9ad88..4ae7e440 100644 --- a/files.php +++ b/files.php @@ -9,10 +9,10 @@ ICEcoder <?php echo $ICEcoder["versionNo"];?> file manager - - - - + + + + - - + + diff --git a/lib/bug-report.php b/lib/bug-report.php index 03aeb927..ac724272 100644 --- a/lib/bug-report.php +++ b/lib/bug-report.php @@ -2,6 +2,10 @@ include "headers.php"; include "settings.php" ; $t = $text['bug-report']; + +$assetsPath = "assets" === $settingsClass->assetsRoot + ? "../" . $settingsClass->assetsRoot + : $settingsClass->assetsRoot; ?> @@ -10,8 +14,8 @@ ICEcoder <?php echo $ICEcoder["versionNo"];?> bug report - - + + diff --git a/lib/help.php b/lib/help.php index 9d7e0d75..9db374d3 100644 --- a/lib/help.php +++ b/lib/help.php @@ -2,6 +2,10 @@ include "headers.php"; include "settings.php"; $t = $text['help']; + +$assetsPath = "assets" === $settingsClass->assetsRoot + ? "../" . $settingsClass->assetsRoot + : $settingsClass->assetsRoot; ?> @@ -10,8 +14,8 @@ ICEcoder <?php echo $ICEcoder["versionNo"];?> help - - + + diff --git a/lib/login.php b/lib/login.php index 1f843ed5..aa6338f6 100644 --- a/lib/login.php +++ b/lib/login.php @@ -25,6 +25,10 @@ } closedir($handle); } + +$assetsPath = "assets" === $settingsClass->assetsRoot + ? "../" . $settingsClass->assetsRoot + : $settingsClass->assetsRoot ?> @@ -37,9 +41,9 @@ - - - + + + setTimeout(function(){document.getElementById('screenContainer').style.opacity = '1'}, 50)"> @@ -47,7 +51,7 @@
- ICEcoder + ICEcoder
onsubmit="return checkCanSubmit();"> diff --git a/lib/multiple-results.php b/lib/multiple-results.php index c8712332..04af6d80 100644 --- a/lib/multiple-results.php +++ b/lib/multiple-results.php @@ -8,6 +8,10 @@ $selectedFiles = true === isset($_GET['selectedFiles']) ? explode(":", $_GET['selectedFiles']) : []; + +$assetsPath = "assets" === $settingsClass->assetsRoot + ? "../" . $settingsClass->assetsRoot + : $settingsClass->assetsRoot ?> @@ -16,8 +20,8 @@ ICEcoder <?php echo $ICEcoder["versionNo"];?> multiple results screen - - + + diff --git a/lib/plugins-manager.php b/lib/plugins-manager.php index fe16e2d9..29b80c67 100644 --- a/lib/plugins-manager.php +++ b/lib/plugins-manager.php @@ -149,6 +149,10 @@ function deletePlugin($dir) { closedir($theDir); rmdir($dir); } + +$assetsPath = "assets" === $settingsClass->assetsRoot + ? "../" . $settingsClass->assetsRoot + : $settingsClass->assetsRoot ?> @@ -157,8 +161,8 @@ function deletePlugin($dir) { ICEcoder <?php echo $ICEcoder["versionNo"];?> plugins manager - - + + diff --git a/lib/properties.php b/lib/properties.php index a4e9b20e..95cd70be 100644 --- a/lib/properties.php +++ b/lib/properties.php @@ -9,6 +9,10 @@ if (!file_exists($fileName) || 0 !== strpos(str_replace("\\", "/", $fileName),$docRoot)) { die(""); } + +$assetsPath = "assets" === $settingsClass->assetsRoot + ? "../" . $settingsClass->assetsRoot + : $settingsClass->assetsRoot ?> @@ -17,8 +21,8 @@ ICEcoder <?php echo $ICEcoder["versionNo"];?> file/folder properties - - + + diff --git a/lib/requirements.php b/lib/requirements.php index 4b1f69c2..836b4227 100644 --- a/lib/requirements.php +++ b/lib/requirements.php @@ -32,9 +32,9 @@ - - - + + + @@ -42,7 +42,7 @@
- ICEcoder + ICEcoder
versionNo;?>
diff --git a/lib/settings-screen.php b/lib/settings-screen.php index c4a8a73b..3d612ecd 100644 --- a/lib/settings-screen.php +++ b/lib/settings-screen.php @@ -2,6 +2,10 @@ include "headers.php"; include "settings.php"; $t = $text['settings-screen']; + +$assetsPath = "assets" === $settingsClass->assetsRoot + ? "../" . $settingsClass->assetsRoot + : $settingsClass->assetsRoot ?> @@ -10,9 +14,9 @@ ICEcoder <?php echo $ICEcoder["versionNo"];?> settings screen - - - + + + - + ' . PHP_EOL; + echo '' . PHP_EOL; } -// Do we have a tab to switch to? -$tabSwitchExtra = ""; -if (true === isset($_GET['tab'])) { - $tabSwitchExtra = "switchTab('" . $_GET['tab'] . "');"; -} ?> - + - +
- +

@@ -205,14 +204,18 @@ // Display number of days backups available $backupDirBase = str_replace("\\", "/", dirname(__FILE__)) . "/../data/backups/"; $backupDirHost = "localhost"; - $backupDirsList = scandir($backupDirBase . $backupDirHost); - // Remove . and .. from array - for ($i = 0; $i < count($backupDirsList); $i++) { - if ($backupDirsList[$i] === "." || $backupDirsList[$i] === "..") { - array_splice($backupDirsList, $i, 1); - $i--; - } - } + if (true === is_dir($backupDirBase . $backupDirHost)) { + $backupDirsList = scandir($backupDirBase . $backupDirHost); + // Remove . and .. from array + for ($i = 0; $i < count($backupDirsList); $i++) { + if ($backupDirsList[$i] === "." || $backupDirsList[$i] === "..") { + array_splice($backupDirsList, $i, 1); + $i--; + } + } + } else { + $backupDirsList = []; + } // Display text re the number of days backups have taken place $backupNumDays = "" != $backupDirsList[0] && count($backupDirsList) > 0 ? count($backupDirsList) : 0; echo $backupNumDays . " " . (1 !== $backupNumDays ? $t['days'] : $t['day']) . " " . $t['of backups stored...']; @@ -594,6 +597,12 @@ function switchTab(tab) { function submitSettings() { ; } +
update
diff --git a/lib/settings-update.php b/lib/settings-update.php index 96978281..0ec5b2ea 100644 --- a/lib/settings-update.php +++ b/lib/settings-update.php @@ -95,7 +95,8 @@ // Work out the theme to use now $themeURL = - "assets/css/theme/" . + $settingsClass->assetsRoot . + "/css/theme/" . ("default" === $ICEcoder["theme"] ? 'icecoder.css' : $ICEcoder["theme"] . '.css') . "?microtime=" . microtime(true); diff --git a/terminal.php b/terminal.php index 0c51e389..12477a7f 100644 --- a/terminal.php +++ b/terminal.php @@ -9,7 +9,7 @@ - + "; -} // Establish the dir ICEcoders running from $ICEcoderDirFullPath = rtrim(str_replace("\\", "/", dirname($_SERVER['SCRIPT_FILENAME'])), "/lib"); From 4a61847ef7bb0360735cf1d55c45e5de9746e24e Mon Sep 17 00:00:00 2001 From: Matt Pass Date: Thu, 14 Dec 2023 11:26:43 +0000 Subject: [PATCH 21/21] ICEcoder is for sale --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 580c7088..d6ed4183 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +# ICEcoder is for sale! Please contact info@icecoder.net. Serious offers only. + +--- + # ICEcoder ## Code editor awesomeness ...in your browser