Explorar o código

27-3-2020 updates

+ Added Cantonese localization
+ Fixed issue #8
+ Added customized Desktop icon pack
+ Added desktop support for UTF-8 basename (Linux)
+ Added Desktop PHP7.4.4 support for non UTF-8 system
- Remove sudo dependencies in ntfs-3g
- Remove sudo dependencies in getCPULoad
Toby Chui %!s(int64=6) %!d(string=hai) anos
pai
achega
c7bb35ed9d
Modificáronse 37 ficheiros con 1160 adicións e 209 borrados
  1. 4 0
      src/Audio/embedded.php
  2. 1 1
      src/Audio/index.js
  3. 6 0
      src/Audio/index.php
  4. 18 2
      src/Desktop/desktopFileLoader.php
  5. 25 10
      src/Desktop/dragDropUploader.php
  6. BIN=BIN
      src/Desktop/img/bg/nature/1.jpg
  7. BIN=BIN
      src/Desktop/img/system_icon/files/cube.png
  8. BIN=BIN
      src/Desktop/img/system_icon/files/cubes.png
  9. BIN=BIN
      src/Desktop/img/system_icon/files/file archive outline.png
  10. BIN=BIN
      src/Desktop/img/system_icon/files/file audio outline.png
  11. BIN=BIN
      src/Desktop/img/system_icon/files/file code outline.png
  12. BIN=BIN
      src/Desktop/img/system_icon/files/file excel outline.png
  13. BIN=BIN
      src/Desktop/img/system_icon/files/file image outline.png
  14. BIN=BIN
      src/Desktop/img/system_icon/files/file pdf outline.png
  15. BIN=BIN
      src/Desktop/img/system_icon/files/file powerpoint outline.png
  16. BIN=BIN
      src/Desktop/img/system_icon/files/file text outline.png
  17. BIN=BIN
      src/Desktop/img/system_icon/files/file video outline.png
  18. BIN=BIN
      src/Desktop/img/system_icon/files/file word outline.png
  19. BIN=BIN
      src/Desktop/img/system_icon/folder-shortcut.png
  20. BIN=BIN
      src/Desktop/img/system_icon/folder.png
  21. 204 180
      src/Desktop/index.php
  22. 19 0
      src/Desktop/script/html2canvas.min.js
  23. 19 4
      src/NotepadA/index.php
  24. 6 1
      src/NotepadA/utils/gcci/compile.php
  25. 1 1
      src/SystemAOB/functions/file_system/delete.php
  26. 1 1
      src/SystemAOB/functions/file_system/index.php
  27. 1 1
      src/SystemAOB/functions/info/version.inf
  28. 3 3
      src/SystemAOB/functions/ntfs-3g.php
  29. 1 1
      src/SystemAOB/functions/system_statistic/getCPUinfo.php
  30. BIN=BIN
      src/SystemAOB/system/aobws/aobws.exe
  31. 147 0
      src/SystemAOB/system/aobws/client.go
  32. 340 0
      src/SystemAOB/system/aobws/hub.go
  33. 117 0
      src/SystemAOB/system/aobws/main.go
  34. BIN=BIN
      src/SystemAOB/system/lang/flags/zh-hk-Yue.png
  35. 216 0
      src/SystemAOB/system/lang/zh-hk-Yue.json
  36. 14 1
      src/Video/index.php
  37. 17 3
      src/Video/new_folder.php

+ 4 - 0
src/Audio/embedded.php

@@ -27,6 +27,10 @@ include '../auth.php';
 	background-color:rgba(255, 255, 255, 0.7) !important;
 	border:1px solid transparent;
 }
+body {
+	background: rgba(255,255,255,0.7);
+}
+
 </style>
 </head>
 <body>

+ 1 - 1
src/Audio/index.js

@@ -58,7 +58,7 @@
 	
 	if (inVDI){
 		//It is currently in Virtual Desktop Mode, make it semi-transparent!
-		$('body').css('background','rgba(255,255,255,0.7)');
+		//$('body').css('background','rgba(255,255,255,0.7)');
 		setInterval(function() {
 			GvolDisplay();
 		},1000);

+ 6 - 0
src/Audio/index.php

@@ -22,6 +22,12 @@ if (!file_exists("uploads/")){
 <script src="../script/tocas/tocas.js"></script>
 <script src="../script/jquery.min.js"></script>
 <script src="../script/ao_module.js"></script>
+<style>
+body {
+	background: rgba(255,255,255,0.7);
+}
+
+</style>
 </head>
 <body><?php
 	function formatSizeUnits($bytes)

+ 18 - 2
src/Desktop/desktopFileLoader.php

@@ -37,6 +37,22 @@ function getLineContain($content,$keyword){
 	return "";
 }
 
+function ubasename($file,$keepExt){
+    if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
+        //No need special handling for windows.
+        return basename($file);
+    }
+    //Replace the build in basename function on Linux
+	$ufilepath = str_replace("\\","/",$file);
+	$ext = pathinfo($file, PATHINFO_EXTENSION);
+	$basename = array_pop(explode("/",$ufilepath));
+	if (!$keepExt){
+	   $basename = str_replace("." . $ext, "",$basename); 
+	}
+	
+    return $basename;
+}
+
 
 if (isset($_GET['username']) && $_GET['username'] != ""){
 	$username = $_GET['username'];
@@ -56,9 +72,9 @@ if (isset($_GET['username']) && $_GET['username'] != ""){
 		foreach ($files as $file){
 			$decodedFilename = "";
 			if (is_file($file) || is_dir($file)){
-				array_push($validfile,urlencode(basename($file)));
+				array_push($validfile,ubasename($file,true));
 				$fileDesktopPosition = getLineContain($filePositions,basename($file).",");
-				$decodedFilename = getDecodeFileName(basename($file));
+				$decodedFilename = getDecodeFileName(ubasename($file,true));
 				//The filename has to be encoded into base64 first before sending to the Desktop as some UTF issue is happening here
 				array_push($decodeFileList,$decodedFilename);
 				array_push($DesktopPos,$fileDesktopPosition);

+ 25 - 10
src/Desktop/dragDropUploader.php

@@ -18,16 +18,31 @@ if (isset($_GET['username']) && $_GET['username'] != ""){
 	}
 	$ext = pathinfo($filename, PATHINFO_EXTENSION);
 	$filenameonly = str_replace(".$ext","",$filename);
-	if (preg_match("/^[a-zA-Z0-9 ]*$/u", $filenameonly) == 1 || preg_match("/\p{Han}+/u", $filenameonly) || strpos($filenameonly,".")){
-		//There is non alphbet and numeric char inside this string
-		$encodedName = "inith" . bin2hex($filenameonly) . "." . $ext;
-	}else{
-		if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
-			$encodedName = "inith" . bin2hex($filenameonly) . "." . $ext;
-		}else{
-			$encodedName = $filenameonly  . "." . $ext;
-		}	
-	}
+	
+	//Update 24-3-2020
+	//Since PHP7.4.4, there is no need to use bin2hex mode for filenaming. 
+	if (!defined('PHP_VERSION_ID')) {
+        $version = explode('.', PHP_VERSION);
+        define('PHP_VERSION_ID', ($version[0] * 10000 + $version[1] * 100 + $version[2]));
+    }
+    
+    if (PHP_VERSION_ID >= 70404){
+        //Use its original name as filename
+        $encodedName = $filenameonly  . "." . $ext;
+    }else{
+        //Use umfilename method
+    	if (preg_match("/^[a-zA-Z0-9 ]*$/u", $filenameonly) == 1 || preg_match("/\p{Han}+/u", $filenameonly) || strpos($filenameonly,".")){
+    		//There is non alphbet and numeric char inside this string
+    		$encodedName = "inith" . bin2hex($filenameonly) . "." . $ext;
+    	}else{
+    		if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
+    			$encodedName = "inith" . bin2hex($filenameonly) . "." . $ext;
+    		}else{
+    			$encodedName = $filenameonly  . "." . $ext;
+    		}	
+    	}
+    }
+	
 	if (move_uploaded_file($_FILES['file']['tmp_name'], $path . $encodedName)){
 		echo $encodedName;
 	}else{

BIN=BIN
src/Desktop/img/bg/nature/1.jpg


BIN=BIN
src/Desktop/img/system_icon/files/cube.png


BIN=BIN
src/Desktop/img/system_icon/files/cubes.png


BIN=BIN
src/Desktop/img/system_icon/files/file archive outline.png


BIN=BIN
src/Desktop/img/system_icon/files/file audio outline.png


BIN=BIN
src/Desktop/img/system_icon/files/file code outline.png


BIN=BIN
src/Desktop/img/system_icon/files/file excel outline.png


BIN=BIN
src/Desktop/img/system_icon/files/file image outline.png


BIN=BIN
src/Desktop/img/system_icon/files/file pdf outline.png


BIN=BIN
src/Desktop/img/system_icon/files/file powerpoint outline.png


BIN=BIN
src/Desktop/img/system_icon/files/file text outline.png


BIN=BIN
src/Desktop/img/system_icon/files/file video outline.png


BIN=BIN
src/Desktop/img/system_icon/files/file word outline.png


BIN=BIN
src/Desktop/img/system_icon/folder-shortcut.png


BIN=BIN
src/Desktop/img/system_icon/folder.png


+ 204 - 180
src/Desktop/index.php

@@ -1,190 +1,197 @@
 <?php
 include_once '../auth.php';
+
+//Check which icons are replacable with the Desktop icon set
+$icons = glob("img/system_icon/files/*.png");
+$iconList = [];
+foreach($icons as $icon){
+	$iconName = basename($icon,".png");
+	$iconList[$iconName] = $icon;
+}
 ?>
 <html>
 <head>
     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
     <title>Virtual Desktop Interface</title>
     <link rel="stylesheet" href="../script/tocas/tocas.css">
-	<script src="../script/tocas/tocas.js"></script>
-	<script src="script/jquery-1.12.4.js"></script>
-	<script src="script/jquery-ui.js"></script>
+	<script type="text/javascript" src="../script/tocas/tocas.js"></script>
+	<script type="text/javascript" src="script/jquery-1.12.4.js"></script>
+	<script type="text/javascript" src="script/jquery-ui.js"></script>
+</head>
+<body style="background-color:black;">
 	<style>
-	
-	.selectedApp {
-		background:rgba(255,255,255,0.5) !important;
-		border-color: white;
-		border-top:1px dotted #fff;
-		border-bottom:1px dotted #fff;
-		border-left:1px dotted #fff;
-		border-right:1px dotted #fff;
-	}
-	
-	.launchIcon{
-		width:70px; 
-		height:auto;
-		position:fixed;
-		cursor: pointer;
-		text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;	
-		color: white; 
-		font-size: 90%;
-		word-break: break-all;
-		text-overflow: ellipsis;
-		-webkit-line
-		-clamp: 2; /* number of lines to show */
-		-webkit-box-orient: vertical;
-		line-height: 16px;
-		min-height:80px;
-		max-height:118px;
-		overflow-y:hidden;
-		overflow-x: hidden;
-		font-variant-numeric: tabular-nums lining-nums;
-		border: 1px solid transparent;
-		border-radius: 3px;
-	}
-	
+		.selectedApp {
+			background:rgba(255,255,255,0.5) !important;
+			border-color: white;
+			border-top:1px dotted #fff;
+			border-bottom:1px dotted #fff;
+			border-left:1px dotted #fff;
+			border-right:1px dotted #fff;
+		}
+		
+		.launchIcon{
+			width:70px; 
+			height:auto;
+			position:fixed;
+			cursor: pointer;
+			text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;	
+			color: white; 
+			font-size: 90%;
+			word-break: break-all;
+			text-overflow: ellipsis;
+			-webkit-line
+			-clamp: 2; /* number of lines to show */
+			-webkit-box-orient: vertical;
+			line-height: 16px;
+			min-height:80px;
+			max-height:118px;
+			overflow-y:hidden;
+			overflow-x: hidden;
+			font-variant-numeric: tabular-nums lining-nums;
+			border: 1px solid transparent;
+			border-radius: 3px;
+		}
+		
 
-	.launchIcon:hover{
-		background:rgba(255,255,255,0.2);
-	}
+		.launchIcon:hover{
+			background:rgba(255,255,255,0.2);
+		}
 
-	.fileDescription {
-		width:200px;
-		/*height:50px;*/
-		position:fixed;
-		z-index:10;
-		background:rgba(255,255,255,0.8);
-		border-color: #abacad;
-		border: 1px solid;
-		padding: 10px;
-		box-shadow: 1px 1px #3a3a3a;
-		padding:2px;
-		word-wrap: break-word;
-	}
+		.fileDescription {
+			width:200px;
+			/*height:50px;*/
+			position:fixed;
+			z-index:10;
+			background:rgba(255,255,255,0.8);
+			border-color: #abacad;
+			border: 1px solid;
+			padding: 10px;
+			box-shadow: 1px 1px #3a3a3a;
+			padding:2px;
+			word-wrap: break-word;
+		}
 
-	.non_selectable {
-		-webkit-user-select: none; /* Chrome/Safari */        
-		-moz-user-select: none; /* Firefox */
-		-ms-user-select: none; /* IE10+ */
-	}
+		.non_selectable {
+			-webkit-user-select: none; /* Chrome/Safari */        
+			-moz-user-select: none; /* Firefox */
+			-ms-user-select: none; /* IE10+ */
+		}
 
-	.fileSelector {
-		position:fixed;
-		z-index:10;
-		background:rgba(52,114,229,0.4);
-		border:1px solid #3472e5;
-		border-width:1px;
-		border-style:solid;
-		border-color:#3472e5;
-	}
+		.fileSelector {
+			position:fixed;
+			z-index:10;
+			background:rgba(52,114,229,0.4);
+			border:1px solid #3472e5;
+			border-width:1px;
+			border-style:solid;
+			border-color:#3472e5;
+		}
 
-	.leftClickMenu{
-		position:fixed;
-		z-index:99;
-	}
-	
-	#lcm {
-		padding-bottom:10px;
-	}
-	
-	#lcm .item{
-		height:auto;
-		padding-top:5px !important;
-		padding-bottom:5px !important;
-		font-size:90%;
-		border: 1px solid transparent;
-	}
-	
-	#rcm {
-		padding-bottom:5px;
-	}
-	
-	#rcm .item{
-		height:auto;
-		padding-top:5px !important;
-		padding-bottom:5px !important;
-		font-size:90%;
-		border: 1px solid transparent;
-	}
-	
-	.hovering{
-		background-color: rgb(226, 253, 255);
-		border: 1px solid rgb(89, 152, 255) !important;
-	}
-	
-	.selectedMenu{
-		background-color: rgb(226, 253, 255);
-		border: 1px solid rgb(89, 152, 255) !important;
-	}
-	
-	.chooseFolderDialog{
-		position:fixed;
-		z-index:103;
-		height:300;
-		width:240;
-		font-size:90%;
-		border: 1px solid transparent;
-		background-color:#f0f0f0 !important;
-		border-radius: 0px !important;
-		padding-top:1px;
-		overflow-y:hidden;
-	}
-	
-	.menubox{
-		padding-bottom: 2px;
-		padding-left: 16px;
-		padding-right: 16px;
-		padding-top: 1px;
-		height:auto;
-		color: rgb(90,90,90);
-		font-size:90%;
-		border: 1px solid transparent;
-		cursor: pointer;
-	}
-	
-	.displaybox{
-		padding-bottom: 2px;
-		padding-left: 16px;
-		padding-right: 16px;
-		padding-top: 1px;
-		height:auto;
-		color: rgb(90,90,90);
-		font-size:90%;
-		border: 1px solid transparent;
-		background-color:#dbdbdb;
-	}
-	
-	.menubox:hover{
-		background-color: rgb(226, 253, 255);
-		border: 1px solid rgb(89, 152, 255) !important;
-	}
-	
-	.renamebox{
-		background-color:#f0f0f0;
-		border: 1px solid #303030;
-		width:70px;
-		word-wrap: break-word;
-		position:absolute;
-		left:0px;
-		overflow-wrap: break-word;
-		overflow: hidden;
-	}
-	
-	.renameboxfolder{
-		background-color:#f0f0f0;
-		border: 1px solid #303030;
-		width:70px;
-		word-wrap: break-word;
-		position:absolute;
-		top:58px;
-		left:0px;
-		overflow-wrap: break-word;
-		overflow: hidden;
-	}
+		.leftClickMenu{
+			position:fixed;
+			z-index:99;
+		}
+		
+		#lcm {
+			padding-bottom:10px;
+		}
+		
+		#lcm .item{
+			height:auto;
+			padding-top:5px !important;
+			padding-bottom:5px !important;
+			font-size:90%;
+			border: 1px solid transparent;
+		}
+		
+		#rcm {
+			padding-bottom:5px;
+		}
+		
+		#rcm .item{
+			height:auto;
+			padding-top:5px !important;
+			padding-bottom:5px !important;
+			font-size:90%;
+			border: 1px solid transparent;
+		}
+		
+		.hovering{
+			background-color: rgb(226, 253, 255);
+			border: 1px solid rgb(89, 152, 255) !important;
+		}
+		
+		.selectedMenu{
+			background-color: rgb(226, 253, 255);
+			border: 1px solid rgb(89, 152, 255) !important;
+		}
+		
+		.chooseFolderDialog{
+			position:fixed;
+			z-index:103;
+			height:300;
+			width:240;
+			font-size:90%;
+			border: 1px solid transparent;
+			background-color:#f0f0f0 !important;
+			border-radius: 0px !important;
+			padding-top:1px;
+			overflow-y:hidden;
+		}
+		
+		.menubox{
+			padding-bottom: 2px;
+			padding-left: 16px;
+			padding-right: 16px;
+			padding-top: 1px;
+			height:auto;
+			color: rgb(90,90,90);
+			font-size:90%;
+			border: 1px solid transparent;
+			cursor: pointer;
+		}
+		
+		.displaybox{
+			padding-bottom: 2px;
+			padding-left: 16px;
+			padding-right: 16px;
+			padding-top: 1px;
+			height:auto;
+			color: rgb(90,90,90);
+			font-size:90%;
+			border: 1px solid transparent;
+			background-color:#dbdbdb;
+		}
+		
+		.menubox:hover{
+			background-color: rgb(226, 253, 255);
+			border: 1px solid rgb(89, 152, 255) !important;
+		}
+		
+		.renamebox{
+			background-color:#f0f0f0;
+			border: 1px solid #303030;
+			width:70px;
+			word-wrap: break-word;
+			position:absolute;
+			left:0px;
+			overflow-wrap: break-word;
+			overflow: hidden;
+		}
+		
+		.renameboxfolder{
+			background-color:#f0f0f0;
+			border: 1px solid #303030;
+			width:70px;
+			word-wrap: break-word;
+			position:absolute;
+			top:58px;
+			left:0px;
+			overflow-wrap: break-word;
+			overflow: hidden;
+		}
 	</style>
-</head>
 
-<body style="background-color:black;">
 <img id="dbg1" src="img/bg/init.jpg" style="position:fixed;background-position: center;
 		background-repeat: no-repeat;
 		background-size: cover;
@@ -259,8 +266,9 @@ My Host
 <div style="display:none;">
 	<input id="forceFocus" type="text" name="focusTool"></input>
 	<div id="setting_sessionUsername"><?php echo $_SESSION['login'];?></div>
+	<div id="usableIconSet"><?php echo json_encode($iconList); ?></div>
 </div>
-</body>
+
 
 <script>
 var bgNumber = 0;
@@ -302,8 +310,10 @@ var copyingFiles = 0;
 var bgfileformat = "jpg";
 var preventRefresh = false;
 var newItemList = [];
-var launchIconMaxHeight = 103;//Pixel
+var launchIconMaxHeight = 120;//Pixel
 var firstInit = true; //Indicate if this is the first time that the Desktop initiate
+var desktopIconSet = JSON.parse($("#usableIconSet").text().trim()); //Load icon set from img/systemicon/files/*.png
+
 
 initUserDesktop(showNotification,"<i class='checkmark icon'></i> Your Desktop is ready to use.");
 initLaunchIconEvents();
@@ -640,14 +650,14 @@ function createFolderIcon(foldername,pos,decodedname=""){
 			assignNextSlot(true);
 		}
 		setFileDesktopPositionFromFilename(foldername,nextSlot[0],nextSlot[1]);
-		var html = '<div class="launchIcon" style="left:'+nextSlot[0]+'px; top: '+nextSlot[1]+'px;" align="center" path="'+path+'" targetType="' + targetType + '"  uid="'+ foldername +'" foldername="'+displayName+'"><img class="ts image non_selectable" src="img/system_icon/folder.png" style="cursor: pointer;margin-top:-15px;" onload="adjustLaunchIconHeight(this);">'+displayName+'</div>';
+		var html = '<div class="launchIcon" style="left:'+nextSlot[0]+'px; top: '+nextSlot[1]+'px;" align="center" path="'+path+'" targetType="' + targetType + '"  uid="'+ foldername +'" foldername="'+displayName+'"><img class="ts image non_selectable" src="img/system_icon/folder.png" style="cursor: pointer;" onload="try{adjustLaunchIconHeight(this);}catch{}">'+displayName+'</div>';
 		assignNextSlot();
 		initLaunchIconEvents();		
 		addDraggingEvents();
 		addContextMenuEvents();
 		
 	}else{
-		var html = '<div class="launchIcon" style="left:'+pos[0]+'px; top: '+pos[1]+'px;" align="center" path="'+path+'" targetType="' + targetType + '"  uid="'+ foldername +'" foldername="'+displayName+'"><img class="ts image non_selectable" src="img/system_icon/folder.png" style="cursor: pointer;margin-top:-15px;" onload="adjustLaunchIconHeight(this);">'+displayName+'</div>';	
+		var html = '<div class="launchIcon" style="left:'+pos[0]+'px; top: '+pos[1]+'px;" align="center" path="'+path+'" targetType="' + targetType + '"  uid="'+ foldername +'" foldername="'+displayName+'"><img class="ts image non_selectable" src="img/system_icon/folder.png" style="cursor: pointer;" onload="try{adjustLaunchIconHeight(this);}catch{}">'+displayName+'</div>';	
 	}
 	$(html).appendTo("body");
 	initLaunchIconEvents();
@@ -663,7 +673,7 @@ function adjustLaunchIconHeight(object){
 		//This folder name require shortening
 		var displayText = target.text().trim();
 		var html = target.html().replace(displayText,"");
-		target.html(html + displayText.substring(0,displayText.length - 5) + "...");
+		target.html(html + displayText.substring(0,displayText.length - 2) + "...");
 	}
 }
 
@@ -671,6 +681,11 @@ function createDesktopIcon(filename,rawname,pos){
 	//Filename is the decoded filename under upload manager scheme
 	//while the rawname is the original filename stored on the system
 	var icon = getIconFromPath(filename);
+	var imageMode = false;
+	if (desktopIconSet[icon] !== undefined){
+		//This icon exists in our desktop image set. Use image mode instead.
+		imageMode = true;
+	}
 	//console.log(filename,rawname);
 	var targetType = "file";
 	var iconName = filename;
@@ -687,12 +702,20 @@ function createDesktopIcon(filename,rawname,pos){
 		setFileDesktopPositionFromFilename(rawname,newLocation[0],newLocation[1],undefined,undefined,true);
 		appendToCurrentFileLocationList(filename,newLocation);
 		assignNextSlot(true);
-		var html = '<div class="launchIcon" style="left:'+newLocation[0]+'px; top: '+newLocation[1]+'px;padding-top:10px;" align="center" path="'+path+'" targetType="'+targetType+'" filename="'+iconName+'" uid="'+rawname+'"><i class="big '+moduleIcon+' icon"></i><div>'+shortenedName+'</div></div>';
+		var html = '<div class="launchIcon" style="left:'+newLocation[0]+'px; top: '+newLocation[1]+'px;padding-top:10px;" align="center" path="'+path+'" targetType="'+targetType+'" filename="'+iconName+'" uid="'+rawname+'"><i class="big '+moduleIcon+' icon" style="font-size:270%;margin-top:13px;margin-bottom:10px;"></i><div>'+shortenedName+'</div></div>';
+		if (imageMode){
+			//Use image icon to replace tocas icon
+			html = '<div class="launchIcon" style="left:'+newLocation[0]+'px; top: '+newLocation[1]+'px;" align="center" path="'+path+'" targetType="' + targetType + '" filename="'+iconName+'" uid="'+rawname+'"><img class="ts image non_selectable" src="' + desktopIconSet[moduleIcon] + '" style="cursor: pointer;">'+shortenedName+'</div>';
+		}
 		initLaunchIconEvents();
 		addDraggingEvents();
 		addContextMenuEvents();
 	}else{
-		var html = '<div class="launchIcon" style="left:'+pos[0]+'px; top: '+pos[1]+'px;padding-top:10px;" align="center" path="'+path+'" targetType="'+targetType+'" filename="'+iconName+'" uid="'+rawname+'"><i class="big '+moduleIcon+' icon"></i><div>'+shortenedName+'</div></div>';
+		var html = '<div class="launchIcon" style="left:'+pos[0]+'px; top: '+pos[1]+'px;padding-top:10px;" align="center" path="'+path+'" targetType="'+targetType+'" filename="'+iconName+'" uid="'+rawname+'"><i class="big '+moduleIcon+' icon" style="font-size:270%;margin-top:13px;margin-bottom:10px;"></i><div>'+shortenedName+'</div></div>';
+		if (imageMode){
+			//Use image icon to replace tocas icon
+			html = '<div class="launchIcon" style="left:'+pos[0]+'px; top: '+pos[1]+'px;" align="center" path="'+path+'" targetType="' + targetType + '" filename="'+iconName+'" uid="'+rawname+'"><img class="ts image non_selectable" src="' + desktopIconSet[moduleIcon] + '" style="cursor: pointer;">'+shortenedName+'</div>';
+		}
 	}
 	$("body").append(html);
 	initLaunchIconEvents();
@@ -772,7 +795,7 @@ function createShortcutIcon(filename,position){
 			    var html = '<div class="launchIcon" style="left:'+position[0]+'px; top: '+position[1]+'px;" align="center" path="'+path+'" targetType="' + targetType + '" acceptFileExt="'+acceptFileType+'" uid="'+filename+'"><img class="ts image non_selectable" src="'+iconpath+'" style="cursor: pointer;width:68px;padding:8px;">'+iconName+'</div>';	
 		    }else if (targetType == "foldershrct"){
 		        //margin-top:-15px;
-		        var html = '<div class="launchIcon" style="left:'+position[0]+'px; top: '+position[1]+'px;" align="center" path="'+path+'" targetType="' + targetType + '" acceptFileExt="'+acceptFileType+'" uid="'+filename+'"><img class="ts image non_selectable" src="'+iconpath+'" style="cursor: pointer;width:68px;height:68px;margin-top:-15px;">'+iconName+'</div>';	
+		        var html = '<div class="launchIcon" style="left:'+position[0]+'px; top: '+position[1]+'px;" align="center" path="'+path+'" targetType="' + targetType + '" acceptFileExt="'+acceptFileType+'" uid="'+filename+'"><img class="ts image non_selectable" src="'+iconpath+'" style="cursor: pointer;width:68px;height:68px;">'+iconName+'</div>';	
 		    }else{
 			    var html = '<div class="launchIcon" style="left:'+position[0]+'px; top: '+position[1]+'px;" align="center" path="'+path+'" targetType="' + targetType + '" acceptFileExt="'+acceptFileType+'" uid="'+filename+'"><img class="ts image non_selectable" src="'+iconpath+'" style="cursor: pointer;width:68px;height:68px;">'+iconName+'</div>';	
 			}
@@ -3067,6 +3090,7 @@ function downloadURI(uri, name){
 	a.click();
 	document.body.removeChild(a);
 }
-
 </script>
+</body>
+
 </html>

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 19 - 0
src/Desktop/script/html2canvas.min.js


+ 19 - 4
src/NotepadA/index.php

@@ -1,5 +1,8 @@
-<?php
-include '../auth.php';
+<?php
+include_once '../auth.php';
+if (!file_exists("tmp")){
+	mkdir("tmp",0777,true);
+}
 ?>
 <!DOCTYPE html>
 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
@@ -391,7 +394,7 @@ function initNotepadA(){
 
 //Add a new editor window to the editor (?) -->ALL FILE PATH PASSED IN MUST BE FROM AOR OR /media/storage*
 function newEditor(filepath){
-    if (openedFilePath.includes(filepath)){
+     if (openedFilePath.includes(filepath)){
         //This page is already opened. Ignore this request and focus on the existing tab.
         $(".fileTab").each(function(){
             if ($(this).attr("filename").includes(filepath)){
@@ -403,14 +406,26 @@ function newEditor(filepath){
     }
 	let tabid = Math.round((new Date()).getTime());
 	var fileInternalIdentifier = "../../";
+	var external = false;
 	if (filepath.substring(0,14) == "/media/storage"){
 		//This file is stored in external stoarge path. Use real path instead.
 		fileInternalIdentifier = "";
+		external = true;
+	}else if (filepath.includes("SystemAOB/functions/extDiskAccess.php?file=")){
+		
+		//This file is opened using file explorer and it has attached an opener with the file.
+		filepath = filepath.replace("SystemAOB/functions/extDiskAccess.php?file=","");
+		external = true;
 	}
 	var tab = '<div class="fileTab" tabid="'+tabid+'" framematch="ca'+tabid+'" filename="' + fileInternalIdentifier + filepath +'">\
 					loading... <div class="closeBtn">⨯</div>\
 					</div>';
-	var frame = '<div style="position:fixed;width:100%;"><iframe id="ca'+tabid+'" class="editor" src="ace/editor.php?theme='+theme+'&filename=../../'+filepath+'&fontsize=' + fontsize + '" width="100%" frameBorder="0"></iframe></div>';
+	
+	var editorRelative = "../../";
+	if (external){
+		editorRelative = "";
+	}
+	var frame = '<div style="position:fixed;width:100%;"><iframe id="ca'+tabid+'" class="editor" src="ace/editor.php?theme='+theme+'&filename=' + editorRelative +filepath+'&fontsize=' + fontsize + '" width="100%" frameBorder="0"></iframe></div>';
 	$("#tabs").append(tab);
 	$("#codeArea").append(frame);
 	updateFrameAttr('ca' + tabid);

+ 6 - 1
src/NotepadA/utils/gcci/compile.php

@@ -5,7 +5,12 @@ $configs = getConfig("encoding",true);
 putenv('LANG=en_US.UTF-8'); 
 //Check if the source file is valid
 if (!isset($_GET['source']) || !file_exists("../" . $_GET['source'])){
-	die("Error. Source file not defined or not found. " . $_GET['source'] . ' given.');
+	if (file_exists($_GET['source'])){
+		//File located in external storage
+		die("Error. You cannot compile in external storage. " . $_GET['source'] . ' given.');
+	}else{
+		die("Error. Source file not defined or not found. " . $_GET['source'] . ' given.');
+	}
 }
 
 //Check if the path is located inside AOR (Yes, AOR Only, not external media)

+ 1 - 1
src/SystemAOB/functions/file_system/delete.php

@@ -41,7 +41,7 @@ function listFiles($dir){
 
     // prevent empty ordered elements
     if (count($ffs) < 1)
-        return;
+        return [];
 	
     foreach($ffs as $ff){
 		if (is_dir($dir . '/' . $ff)){

+ 1 - 1
src/SystemAOB/functions/file_system/index.php

@@ -28,7 +28,7 @@ if (file_exists("../personalization/sysconf/fsaccess.config")){
         body {
             padding-top: 2em;
             background-color: rgb(250, 250, 250);
-            overflow: scroll;
+            overflow-y: scroll;
         }
 		
 		.UMfilename.active{

+ 1 - 1
src/SystemAOB/functions/info/version.inf

@@ -1 +1 @@
-AOB-DEVB_v21-3-2020
+AOB-DEVB_v25-3-2020

+ 3 - 3
src/SystemAOB/functions/ntfs-3g.php

@@ -22,7 +22,7 @@ if (isset($_GET['md']) && isset($_GET['mpo'])&& isset($_GET['mpn'])){
 	
 }else if (isset($_GET['mpo'])){
 	//Umount the disk and do nothing
-	$out = shell_exec('sudo umount ' . '"' . $_GET['mpo'] . '"');
+	$out = shell_exec('umount ' . '"' . $_GET['mpo'] . '"');
 	echo 'sudo umount ' . '"' . $_GET['mpo'] . '"'. '<br>';
 	echo $out;
 	foreach ($out as $line){
@@ -39,7 +39,7 @@ if (isset($_GET['md']) && isset($_GET['mpo'])&& isset($_GET['mpn'])){
 	}
 	echo 'DONE';
 }else if (isset($_GET['lsblk'])){
-	exec("sudo lsblk",$out);
+	exec("lsblk",$out);
 	$result = [];
 	foreach ($out as $line){
 		array_push($result, $line);
@@ -47,7 +47,7 @@ if (isset($_GET['md']) && isset($_GET['mpo'])&& isset($_GET['mpn'])){
 	header('Content-Type: application/json');
 	echo json_encode($result);
 }else if (isset($_GET['blkid'])){
-	exec("sudo blkid",$out);
+	exec("blkid",$out);
 	$result = [];
 	foreach ($out as $line){
 		array_push($result, $line);

+ 1 - 1
src/SystemAOB/functions/system_statistic/getCPUinfo.php

@@ -10,7 +10,7 @@ if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
 	header('Content-Type: application/json');
 	echo json_encode($result);
 }else{
-    $temp = shell_exec('sudo cat /proc/cpuinfo');
+    $temp = shell_exec('cat /proc/cpuinfo');
 	$temp = str_replace(":","=",$temp);
 	$temp = str_replace("\t","",$temp);
 	$temp = str_replace("\n",",",$temp);

BIN=BIN
src/SystemAOB/system/aobws/aobws.exe


+ 147 - 0
src/SystemAOB/system/aobws/client.go

@@ -0,0 +1,147 @@
+// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"bytes"
+	"log"
+	"net/http"
+	"time"
+
+	"github.com/gorilla/websocket"
+)
+
+const (
+	// Time allowed to write a message to the peer.
+	writeWait = 10 * time.Second
+
+	// Time allowed to read the next pong message from the peer.
+	pongWait = 60 * time.Second
+
+	// Send pings to peer with this period. Must be less than pongWait.
+	pingPeriod = (pongWait * 9) / 10
+
+	// Maximum message size allowed from peer.
+	maxMessageSize = 512
+)
+
+var (
+	newline = []byte{'\n'}
+	space   = []byte{' '}
+)
+
+var upgrader = websocket.Upgrader{
+	ReadBufferSize:  1024,
+	WriteBufferSize: 1024,
+}
+
+// Client is a middleman between the websocket connection and the hub.
+type Client struct {
+	hub *Hub
+
+	// The websocket connection.
+	conn *websocket.Conn
+
+	// Buffered channel of outbound messages.
+	send chan msgpackage
+}
+
+type msgpackage struct{
+	message []byte
+
+	clientID *Client
+}
+
+// readPump pumps messages from the websocket connection to the hub.
+//
+// The application runs readPump in a per-connection goroutine. The application
+// ensures that there is at most one reader on a connection by executing all
+// reads from this goroutine.
+func (c *Client) readPump() {
+	defer func() {
+		c.hub.unregister <- c
+		c.conn.Close()
+	}()
+	c.conn.SetReadLimit(maxMessageSize)
+	c.conn.SetReadDeadline(time.Now().Add(pongWait))
+	c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
+	for {
+		_, message, err := c.conn.ReadMessage()
+		if err != nil {
+			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
+				log.Printf("error: %v", err)
+			}
+			break
+		}
+		message = bytes.TrimSpace(bytes.Replace(message, newline, space, -1))
+		//log.Println(message)
+		c.hub.broadcast <- msgpackage{message, c}
+	}
+}
+
+// writePump pumps messages from the hub to the websocket connection.
+//
+// A goroutine running writePump is started for each connection. The
+// application ensures that there is at most one writer to a connection by
+// executing all writes from this goroutine.
+func (c *Client) writePump() {
+	ticker := time.NewTicker(pingPeriod)
+	defer func() {
+		ticker.Stop()
+		c.conn.Close()
+	}()
+	for {
+		select {
+		case msg, ok := <-c.send:
+			c.conn.SetWriteDeadline(time.Now().Add(writeWait))
+			if !ok {
+				// The hub closed the channel.
+				c.conn.WriteMessage(websocket.CloseMessage, []byte{})
+				return
+			}
+
+			w, err := c.conn.NextWriter(websocket.TextMessage)
+			if err != nil {
+				return
+			}
+			w.Write(msg.message)
+
+			// Add queued chat messages to the current websocket message.
+			/*
+			n := len(c.send)
+			for i := 0; i < n; i++ {
+				w.Write(newline)
+				w.Write(<-c.send)
+			}
+			*/
+
+			if err := w.Close(); err != nil {
+				return
+			}
+		case <-ticker.C:
+			c.conn.SetWriteDeadline(time.Now().Add(writeWait))
+			if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
+				return
+			}
+		}
+	}
+}
+
+// serveWs handles websocket requests from the peer.
+func serveWs(hub *Hub, w http.ResponseWriter, r *http.Request) {
+	upgrader.CheckOrigin = func(r *http.Request) bool { return true }
+	conn, err := upgrader.Upgrade(w, r, nil)
+	if err != nil {
+		log.Println(err)
+		return
+	}
+	client := &Client{hub: hub, conn: conn, send: make(chan msgpackage, 256)}
+	client.hub.register <- client
+
+	// Allow collection of memory referenced by the caller by doing all work in
+	// new goroutines.
+	go client.writePump()
+	go client.readPump()
+}

+ 340 - 0
src/SystemAOB/system/aobws/hub.go

@@ -0,0 +1,340 @@
+// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//Modified by ArOZ Online Project for websocket message redirection purpose
+
+package main
+
+import (
+	"log"
+	"net/http"
+	"encoding/json"
+	uuid "github.com/google/uuid"
+	"strings"
+)
+// Hub maintains the set of active clients and broadcasts messages to the
+// clients.
+type Hub struct {
+	// Registered clients.
+	clients map[*Client]bool
+
+	// Inbound messages from the clients.
+	broadcast chan msgpackage
+
+	// Register requests from the clients.
+	register chan *Client
+
+	// Unregister requests from clients.
+	unregister chan *Client
+
+	//Register the username from clients
+	usernames map[*Client]string
+
+	//State if the user has logged in
+	loggedin map[*Client]bool
+
+	//Module name for application registry
+	channel map[*Client] string
+
+	//Instance UUID for direct messaging between two clients
+	uuids map[*Client] string
+
+}
+
+func newHub() *Hub {
+	return &Hub{
+		broadcast:  make(chan msgpackage),
+		register:   make(chan *Client),
+		unregister: make(chan *Client),
+		clients:    make(map[*Client]bool),
+		usernames: make(map[*Client]string),
+		loggedin: make(map[*Client]bool),
+		channel: make(map[*Client] string),
+		uuids: make(map[*Client] string),
+	}
+}
+type validAuthJSON struct {
+	username string
+	signDevice string
+	createdTime int
+	expTime int
+	discarded bool
+}
+
+func checkLogin(h *Hub, sender *Client) bool{
+	if (h.loggedin[sender] == true){
+		return true
+	}
+	return false
+}
+
+func sendResp(h *Hub, reciver *Client, message []byte){
+	select {
+	case reciver.send <- msgpackage{message,reciver}:
+	default:
+		close(reciver.send)
+		delete(h.clients, reciver)
+		
+	}
+}
+
+func sendBadReq(h *Hub, reciver *Client, command string){
+	message := `{"type":"resp","command":"` + command + `", "data":"400 Bad Request"}`
+	select {
+	case reciver.send <- msgpackage{[]byte(message),reciver}:
+	default:
+		close(reciver.send)
+		delete(h.clients, reciver)
+		
+	}
+}
+
+func runCommand(h *Hub, sender *Client, message string) string{
+	commandChunks := strings.Split(message[1:]," ")
+	commandOutput := `{"type":"resp","command":"` + commandChunks[0] + `", "data":"405 Method Not Allowed"}`;
+	//log.Println(commandChunks)
+	switch commandChunks[0]{
+	case "login":
+		if (len(commandChunks) != 3){
+			//Malformated command input.
+			sendBadReq(h, sender, "login");
+			log.Println("Bad command received.")
+			return "";
+		}
+		registerModuleName := commandChunks[1]
+		thisJWT := commandChunks[2]
+		validationURL := string(*endpt) + "?token=" + string(thisJWT)
+		//Get response from server for authentication check
+		resp, err := http.Get(validationURL)
+		if err != nil {
+			//Cannot reach auth services.
+			commandOutput = `{"type":"resp","command":"login", "data":"503 Service Unavailable"}`;
+			sendResp(h, sender, []byte(commandOutput))
+			log.Println("Failed to access auth service. Is the jwt validation endpoint correct?")
+			return "";
+		}
+		defer resp.Body.Close()
+
+		//log.Printf("%#v\n", resp)
+
+		dec := json.NewDecoder(resp.Body)
+		if dec == nil {
+			//JSON parse error
+			commandOutput = `{"type":"resp","command":"login", "data":"500 Internal Server Error"}`;
+			sendResp(h, sender, []byte(commandOutput))
+			log.Println("Failed parse JSON return value. Is the aobws version match the ArOZ Online System version?")
+			return "";
+			//panic("Failed to start decoding JSON data")
+		}
+
+		json_map := make(map[string]interface{})
+		err = dec.Decode(&json_map)
+		if err != nil {
+			panic(err)
+		}
+
+		if (json_map["error"] == nil){
+			//Check if token discarded. If yes, also report as failed to login.
+			if (json_map["discarded"] == true){
+				//This token is discarded. Ignore login
+				h.loggedin[sender] = false
+				commandOutput = `{"type":"resp","command":"login", "data":"401 Unauthorized"}`;
+				sendResp(h, sender, []byte(commandOutput))
+				return "";
+			}else{
+				//This authentication is successful
+				h.loggedin[sender] = true
+				h.channel[sender] = registerModuleName
+				h.usernames[sender] = json_map["username"].(string)
+				commandOutput = `{"type":"resp","command":"login", "data":"202 Accepted"}`;
+			}
+			
+
+		}else{
+			//This authentication failed
+			h.loggedin[sender] = false
+			commandOutput = `{"type":"resp","command":"login", "data":"401 Unauthorized"}`;
+			sendResp(h, sender, []byte(commandOutput))
+			return "";
+		}
+
+	case "chklogin":
+		if (len(commandChunks) != 1){
+			//Malformated command input.
+			sendBadReq(h, sender, "chklogin");
+			log.Println("Bad command received.")
+			return "";
+		}
+		if (checkLogin(h, sender)){
+			commandOutput = `{"type":"resp","command":"chklogin", "data":"` + "Logged in as " + h.usernames[sender] + `"}`;
+		}else{
+			sendResp(h, sender, []byte(`{"type":"resp","command":"chklogin", "data":"Not logged in"}`))
+			return ""
+		}
+	case "logout":
+		if (len(commandChunks) != 1){
+			//Malformated command input.
+			sendBadReq(h, sender, "logout");
+			log.Println("Bad command received.")
+			return "";
+		}
+		if (checkLogin(h, sender)){
+			h.loggedin[sender] = false
+			sendResp(h, sender, []byte(`{"type":"resp","command":"logout", "data":"202 Accepted"}`))
+			return ""
+		}else{
+			commandOutput = `{"type":"resp","command":"logout", "data":"401 Unauthorized"}`;
+		}
+	case "chkchannel":
+		if (len(commandChunks) != 1){
+			//Malformated command input.
+			sendBadReq(h, sender, "chkchannel");
+			log.Println("Bad command received.")
+			return "";
+		}
+		if (checkLogin(h, sender)){
+			commandOutput = `{"type":"resp","command":"chkchannel", "data":"` + h.channel[sender] + `"}`;
+		}else{
+			commandOutput = `{"type":"resp","command":"chkchannel", "data":"401 Unauthorized"}`;
+		}
+	case "chkuuid":
+		if (len(commandChunks) != 1){
+			//Malformated command input.
+			sendBadReq(h, sender, "chkuuid");
+			log.Println("Bad command received.")
+			return "";
+		}
+		if (checkLogin(h, sender)){
+			commandOutput = `{"type":"resp","command":"chkuuid", "data":"` + h.uuids[sender] + `"}`;
+		}else{
+			commandOutput = `{"type":"resp","command":"chkuuid", "data":"401 Unauthorized"}`;
+		}
+	case "tell":
+		//Tell comamnd, given another username
+		targetUsername := commandChunks[1]
+		messageToBeDelivered := strings.Join(commandChunks[2:]," ")
+		//Parse the common communication protocol
+		msgpack := "{\"type\": \"tell\",\"sender\": \"" + h.usernames[sender] + "\", \"connUUID\": \"" + h.uuids[sender] + "\", \"data\": \"" +  messageToBeDelivered + "\"}"
+		if (checkLogin(h, sender)){
+			//Send the message to all the clients with given username
+			for client := range h.clients {
+				if h.usernames[client] == targetUsername && client != sender{
+					sendResp(h, client, []byte(msgpack))
+				}
+			}
+			commandOutput = `{"type":"resp","command":"tell", "data":"200 OK"}`;
+		}else{
+			commandOutput = `{"type":"resp","command":"tell", "data":"401 Unauthorized"}`;
+		}
+	case "utell":
+		//Tell comamnd, but using UUID instead of username
+		targetUUID := commandChunks[1]
+		messageToBeDelivered := strings.Join(commandChunks[2:]," ")
+		msgpack := "{\"type\": \"utell\",\"sender\": \"" + h.usernames[sender] + "\", \"connUUID\": \"" + h.uuids[sender] + "\", \"data\": \"" +  messageToBeDelivered + "\"}"
+		if (checkLogin(h, sender)){
+			//Send the message to all the clients with given username
+			for client := range h.clients {
+				if h.uuids[client] == targetUUID{
+					sendResp(h, client, []byte(msgpack))
+				}
+			}
+			commandOutput = `{"type":"resp","command":"utell", "data":"200 OK"}`;
+		}else{
+			commandOutput = `{"type":"resp","command":"utell", "data":"401 Unauthorized"}`;
+		}
+
+	case "help":
+		//The standard help command. Shows the list of command usable via this websocket reflector
+		resp := `ArOZ Online Base System WebSocket Reflector
+		Usage: 
+		/login {channel} {shadowJWT token}
+		/chklogin
+		/logout
+		/chkchannel
+		/checkuuid
+		/tell {username} {message}
+		/utell	{connnection UUID} {message}`;
+		sendResp(h, sender, []byte(resp))
+		return ""
+	}
+
+	return commandOutput
+}
+
+func handleMessage(h *Hub, sender *Client, message []byte) (bool, []byte){
+	if (string(message)[0:1] == "/"){
+		//This is a command.
+		returnString := runCommand(h, sender, string(message))
+		return false,[]byte(returnString)
+	}
+	return true,message
+}
+
+func (h *Hub) run() {
+	for {
+		select {
+		case client := <-h.register:
+			h.clients[client] = true //Enable this client as registered user
+			h.usernames[client] = "anonymous" //Set its name to anonymous before it login
+			h.loggedin[client] = false //Set to not logged in
+			h.uuids[client] = (uuid.Must(uuid.NewRandom())).String() //Given this connection an uuid
+		case client := <-h.unregister:
+			if _, ok := h.clients[client]; ok {
+				delete(h.clients, client)
+				close(client.send)
+			}
+		case msg := <-h.broadcast:
+			message := msg.message
+			sender := msg.clientID
+			log.Println(string(message), h.usernames[sender])
+			//Handle the message
+			brodcaseMessage, message := handleMessage(h,sender, message)
+			if (string(message) == ""){
+				//Do not return anything
+				break;
+			}
+			//Check if the sender has already logged in
+			if (h.loggedin[sender] == true){
+				//User logged in
+				if brodcaseMessage{
+					//Broadcast the message to the Clients in the same channel
+					senderChannel := h.channel[sender]
+					msgpack := "{\"type\": \"broadcast\",\"sender\": \"" + h.usernames[sender] + "\", \"connUUID\": \"" + h.uuids[sender] + "\", \"data\": \"" +  string(message) + "\"}"
+					for client := range h.clients {
+						//Send the message to all clients in the same channel
+						if h.channel[client] == senderChannel{
+							select {
+							case client.send <- msgpackage{[]byte(msgpack),client}:
+							default:
+								close(client.send)
+								delete(h.clients, client)
+								
+							}
+						}
+					}
+				}else{
+					//Only send reply back to the user
+					select {
+					case sender.send <- msgpackage{message,sender}:
+					default:
+						close(sender.send)
+						delete(h.clients, sender)
+						
+					}
+				}
+			}else{
+				//User not logged in
+				select {
+				case sender.send <- msgpackage{[]byte(`{"type":"resp","command":"generic", "data":"401 Unauthorized"}`),sender}:
+				default:
+					close(sender.send)
+					delete(h.clients, sender)
+					
+				}
+			}
+
+			
+		}
+	}
+}

+ 117 - 0
src/SystemAOB/system/aobws/main.go

@@ -0,0 +1,117 @@
+// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"flag"
+	"log"
+	"time"
+	"os"
+	"net/http"
+	"github.com/rs/cors"
+	"github.com/marcsauter/single"
+)
+
+var addr = flag.String("port", "8000", "HTTP service address")
+var endpt = flag.String("endpt","http://localhost/AOB/SystemAOB/system/jwt/validate.php", "ShadowJWT Validation Endpoint")
+var useTLS = flag.Bool("tls", false, "Enable TLS support on websocket (aka wss:// instead of ws://). Reqire -cert and -key")
+var cert = flag.String("cert","server.crt", "Certification for TLS encription")
+var key = flag.String("key","server.key", "Server key for TLS encription")
+
+func serveHome(w http.ResponseWriter, r *http.Request) {
+	log.Println(r.URL)
+	if r.URL.Path != "/" {
+		http.Error(w, "Not found", http.StatusNotFound)
+		return
+	}
+	if r.Method != "GET" {
+		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+		return
+	}
+	http.ServeFile(w, r, "home.html")
+}
+
+func checkForTerminate(){
+	if (fileExists("terminate.inf")){
+		os.Remove("terminate.inf");
+		os.Exit(0);
+	}
+}
+
+func fileExists(filename string) bool {
+    info, err := os.Stat(filename)
+    if os.IsNotExist(err) {
+        return false
+    }
+    return !info.IsDir()
+}
+
+func setInterval(someFunc func(), milliseconds int, async bool) chan bool {
+	interval := time.Duration(milliseconds) * time.Millisecond
+	ticker := time.NewTicker(interval)
+	clear := make(chan bool)
+	go func() {
+		for {
+			select {
+			case <-ticker.C:
+				if async {
+					go someFunc()
+				} else {
+					someFunc()
+				}
+			case <-clear:
+				ticker.Stop()
+				return
+			}
+		}
+	}()
+	return clear
+}
+
+func main() {
+	//Parse input flags
+	flag.Parse()
+
+	//Set terminate check on auto
+	setInterval(checkForTerminate,1000,false)
+
+	//Check if another instance is running
+	s := single.New("aobws")
+    if err := s.CheckLock(); err != nil && err == single.ErrAlreadyRunning {
+        log.Fatal("Another instance of the app is already running, exiting")
+    } else if err != nil {
+        // Another error occurred, might be worth handling it as well
+        log.Fatalf("Failed to acquire exclusive app lock: %v", err)
+    }
+	defer s.TryUnlock()
+	
+	//Create new websocket hub
+	hub := newHub()
+	go hub.run()
+	log.Println("ArOZ Online WebSocket Server - CopyRight ArOZ Online Project 2020");
+	mux := http.NewServeMux()
+	mux.HandleFunc("/", serveHome)
+	mux.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Access-Control-Allow-Origin", "*")
+		serveWs(hub, w, r)
+	})
+	/*
+	http.HandleFunc("/", serveHome)
+	http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
+		serveWs(hub, w, r)
+	})
+	*/
+	handler := cors.Default().Handler(mux)
+	var err error
+	if (*useTLS == true){
+		err = http.ListenAndServeTLS(":" + *addr, *cert, *key, nil)
+	}else{
+		err = http.ListenAndServe(":" + *addr, handler)
+	}
+	
+	if err != nil {
+		log.Fatal("ListenAndServe: ", err)
+	}
+}

BIN=BIN
src/SystemAOB/system/lang/flags/zh-hk-Yue.png


+ 216 - 0
src/SystemAOB/system/lang/zh-hk-Yue.json

@@ -0,0 +1,216 @@
+{
+    "lang": "zh-HK-Yue",
+    "author": "alanyeung, tobychui",
+    "version": "1.0",
+    "keys": {
+        "index/menu/title": "ArOZ Online",
+        "index/identify/welcomeback": "歡迎返嚟",
+        "index/identify/notfound": "你仲未設定你嘅身份啊",
+        "index/banner/identify": "你依家用緊呢個名登入: ",
+        "index/launcher/launch": "開啟",
+        "index/mobile/yourip": "你嘅IP位址: ",
+        "index/mobile/logout": "登出 ",
+        "index/button/desktop": "開啟網絡桌面 ",
+        "index/button/extdesktop": "啟動擴展桌面 ",
+        "index/button/logout": "登出",
+
+        "listmenu/sidebar/music": "音樂",
+        "listmenu/sidebar/video": "影片",
+        "listmenu/sidebar/picture": "圖片",
+        "listmenu/sidebar/document": "文件",
+        "listmenu/sidebar/myhost": "我嘅電腦",
+        "listmenu/sidebar/fileview": "檔案管理",
+        "listmenu/sidebar/trashbin": "垃圾桶",
+        "listmenu/sidebar/setting": "設定",
+        "listmenu/sidebar/help": "搵幫手",
+        "listmenu/powermenu/powermanagement": "電源管理",
+        "listmenu/powermenu/logout": "登出",
+        "listmenu/powermenu/restartapache": "重啟你嘅網頁服務 ",
+        "listmenu/powermenu/reboot": "重啟主機",
+        "listmenu/powermenu/shutdown": "熄機",
+
+        "filesystem/topbar/fileviewer": "ArOZβ 檔案總管",
+        "filesystem/sidebar/": "ArOZβ 檔案總管",
+        "filesystem/sidebar/noSelectedFile": "你依家冇選擇到任何一個檔案",
+        "filesystem/sidebar/fullpath": "檔案完整路徑",
+        "filesystem/sidebar/shortcut": "捷徑",
+        "filesystem/sidebar/done": "熄咗檔案管理員",
+        "filesystem/sidebar/refresh": "Reload",
+        "filesystem/sidebar/cancel": "取消",
+        "filesystem/sidebar/fileviewer": "ArOZβ 檔案總管",
+        "filesystem/sidebar/ongoingtask": "依家電腦做緊嘅嘢",
+        "filesystem/newfolder/newfolder": "開新 folder",
+        "filesystem/newfolder/tips": "檔案名稱淨喺可以有英文數字同埋空格",
+        "filesystem/newfolder/tips2": " 如果你想用其他語言請剔「將呢個folder encode」。",
+        "filesystem/newfolder/efcb": "將呢個 folder encode(資料夾名將會儲存為兼容編碼模式)",
+        "filesystem/newfolder/confirm": "新 folder",
+        "filesystem/newfolder/cancel": "取消",
+        "filesystem/rename/rename": "改名",
+        "filesystem/rename/renamefile": "改檔案名",
+        "filesystem/rename/renamefolder": "改 folder名",
+        "filesystem/rename/tips": "檔案名稱淨喺可以有英文數字同埋空格",
+        "filesystem/rename/tips2": " 如果你想用其他語言請剔「將呢個folder encode」。",
+        "filesystem/rename/efcbr": "將呢個 folder 編碼(資料夾名將會儲存為兼容編碼模式)",
+        "filesystem/rename/label": "夾硬讀取唔支援嘅檔案名可能會導致系統出現故障,所以你唔肯定嘅最好轉返做兼容模式",
+        "filesystem/rename/orgname": "原本嘅檔案名",
+        "filesystem/rename/newname": "新嘅檔案名",
+        "filesystem/rename/confirm": "好",
+        "filesystem/rename/cancel": "唔好",
+        "filesystem/upload/upload": "Upload 啲 File",
+        "filesystem/upload/tips": "所有上載的檔案都會根據上載管理員所使用的檔案名稱編碼格式儲存。",
+        "filesystem/upload/target": "你upload緊上去嘅 Folder",
+        "filesystem/upload/files": "你揀咗嘅檔案",
+        "filesystem/upload/cancel": "唔好 ",
+        "filesystem/upload/preview": "睇吓選取左嘅檔案列表",
+        "filesystem/upload/uploadconfirm": "確定upload",
+        "filesystem/upload/pending": "仲等緊 upload 嘅 list",
+        "filesystem/openwith/openwith": "用 xxx 開啟檔案",
+        "filesystem/openwith/tips": "睇吓錶揀你想用邊一個 module開檔案:",
+        "filesystem/openwith/init": "load緊模組資訊…",
+        "filesystem/openwith/openGuide": "請揀一個你想開呢個 File 嘅模式",
+        "filesystem/openwith/floatWindow": "Float window(建議)",
+        "filesystem/openwith/redirect": "喺呢一頁",
+        "filesystem/openwith/newWindow": "喺另一頁",
+        "filesystem/newfile/newfile": "開新檔案",
+        "filesystem/newfile/tips": "請揀下邊其中一個檔案類型",
+        "filesystem/newfile/loading": "Load緊檔案類型…",
+        "filesystem/newfile/manual": "或者自己填一個檔案名落去:",
+        "filesystem/newfile/create": "開新檔案",
+        "filesystem/delete/confirm": "確定delete",
+        "filesystem/rightClickMenu/open": "開",
+        "filesystem/rightClickMenu/openwith": "用 … 開",
+        "filesystem/rightClickMenu/openInNewWindow": "用新 window 開",
+        "filesystem/rightClickMenu/copy": "複制",
+        "filesystem/rightClickMenu/paste": "貼上",
+        "filesystem/rightClickMenu/cut": "剪落嚟",
+        "filesystem/rightClickMenu/newfile": "開新檔案",
+        "filesystem/rightClickMenu/newfolder": "新 folder",
+        "filesystem/rightClickMenu/upload": "上載",
+        "filesystem/rightClickMenu/rename": "改名",
+        "filesystem/rightClickMenu/filenameconv": "改編碼模式",
+        "filesystem/rightClickMenu/delete": "剷咗佢",
+        "filesystem/rightClickMenu/download": "Down 落嚟",
+        "filesystem/rightClickMenu/properties": "檔案屬性",
+        "filesystem/operations/fileSelected": " 個檔案/資料夾已經揀咗",
+        "filesystem/operations/download": "Down 落嚟",
+        "filesystem/operations/zipAndDown": "壓縮之後download",
+        "filesystem/delete/reminder": "剷咗之後呢個檔案以後都開唔返, 你喺咪要剷除 ?",
+        "filesystem/delete/yes": "喺",
+        "filesystem/delete/cancel": "都喺諗諗先",
+        "filesystem/help/helpmanual": "檔案管理嘅說明書",
+        "filesystem/help/tips": "下邊喺圖案嘅解釋",
+        "filesystem/help/back": "返去",
+        "filesystem/help/backdesc": "返去上一個 folder",
+        "filesystem/help/open": "開野 ",
+        "filesystem/help/opendesc": "用你設定好既 module 嚟開檔案",
+        "filesystem/help/openwith": "使用 … 開啟",
+        "filesystem/help/openwithdesc": "使用手動選擇的應用程式或模組開啟所選擇的檔案。",
+        "filesystem/help/download": "壓縮然後 download",
+        "filesystem/help/downloaddesc": "將一個檔案壓縮然後下載落嚟嘅電腦上邊",
+        "filesystem/help/copy": "複製",
+        "filesystem/help/copydesc": "複製上去剪貼簿上邊",
+        "filesystem/help/paste": "Paste",
+        "filesystem/help/pastedesc": "將資料貼上去目的地嘅資料㗎",
+        "filesystem/help/newfile": "開新檔案",
+        "filesystem/help/newfilesesc": "開新檔案",
+        "filesystem/help/newfolder": "開folder",
+        "filesystem/help/newfolderdesc": "喺依家嘅 folder入邊開一個新嘅folder",
+        "filesystem/help/upload": "Upload file",
+        "filesystem/help/uploaddesc": "將 File upload 上去依家個 folder",
+        "filesystem/help/cut": "剪落嚟",
+        "filesystem/help/cutdesc": "剪落去網頁剪貼簿上邊",
+        "filesystem/help/rename": "改名",
+        "filesystem/help/renamedesc": "將一個簡單嘅檔案或者資料夾改名",
+        "filesystem/help/filenameconv": "改編碼",
+        "filesystem/help/filenameconvdesc": "將你揀緊嘅嗰個檔案或者資料夾嘅編碼方式改咗做兼容模式或者還原做普通模式",
+        "filesystem/help/remove": "剷除",
+        "filesystem/help/removedesc": "剷咗你揀緊嘅檔案",
+        "filesystem/help/properties": "屬性",
+        "filesystem/help/propertiesdesc": "顯示返個檔案屬性",
+        "filesystem/help/refresh": "Reload",
+        "filesystem/help/refreshdesc": "重新 load 依家個檔案列表",
+        "filesystem/help/help": "幫手",
+        "filesystem/help/helpdesc": "顯示返呢個信息",
+        "filesystem/help/close": "接埋",
+        "filesystem/windowTitle/default": "ArOZβ file管理器",
+        "filesystem/windowTitle/folderview": " - Folder預覽",
+        "filesystem/popups/newfileerror": "開緊新檔案嘅時候唔知點解香咗",
+        "filesystem/popups/newfilesuccess": "成功整咗個新檔案",
+        "filesystem/popups/redirectionTips": "揀一個程式俾你重新導向啦",
+        "filesystem/popups/multiDownloadFolderError": "你唔可以將資料夾包含喺選取列表入邊 ",
+        "filesystem/popups/zippingTips": "壓縮folder可能要好耐, 唔該等一陣",
+        "filesystem/popups/zipready": "已經可以下載啦。",
+        "filesystem/popups/zipfailed": "你個壓縮要求香咗。",
+        "filesystem/popups/permissionDenied": "發生錯誤 你冇權限存取 ",
+        "filesystem/popups/itemCopied": " 已經複製去到剪貼簿",
+        "filesystem/popups/folderCopied": "已經複製資料夾到剪貼簿 ",
+        "filesystem/popups/filecopied": "複製呢個檔案喺剪貼簿",
+        "filesystem/popups/nothingToCopy": "你冇揀緊任何檔案",
+        "filesystem/popups/readyToMove": " 已經準備好郁",
+        "filesystem/popups/folderReadyToMove": "資料夾已經準備好郁",
+        "filesystem/popups/fileMoveSysScript": "你唔可以郁系統程式",
+        "filesystem/popups/fileReadyToMove": "Folder已經準備好郁",
+        "filesystem/popups/nothingToCut": "你冇揀緊任何檔案",
+        "filesystem/popups/nothingToPaste": "冇嘢可以俾你貼上",
+        "filesystem/popups/pasting": "貼上緊…",
+        "filesystem/popups/onFolderPasteFinish": "貼上完啦,依家load緊檔案列表",
+        "filesystem/popups/somethingWentWrong": "你個嘢壞咗: ",
+        "filesystem/popups/pasteError": "貼唔到上: ",
+        "filesystem/popups/filePasted": "貼上完成。",
+        "filesystem/popups/folderPasted": "資料夾已經貼上",
+        "filesystem/popups/fileRemoved": "File已經剷咗",
+        "filesystem/popups/nothingToRename": "你冇 file 揀緊",
+        "filesystem/popups/itemsConverted": " 嘅File 編碼已經轉換",
+        "filesystem/popups/prepareUploadWait": "你要等之前嘅上載完咗先可以再upload",
+        "filesystem/popups/uploadStart": "Upload 將會喺背景進行, 請等upload完先做其他嘢啦",
+        "filesystem/popups/uploadSucceed": "Upload 完",
+        "filesystem/ongoingTasks/copying": "複製緊",
+        "filesystem/ongoingTasks/moving": "郁緊",
+        "filesystem/ongoingTasks/zipping": "壓縮緊",
+        "filesystem/ongoingTasks/unzipping": "解壓縮緊",
+        "filesystem/ongoingTasks/item": " 個file",
+        "filesystem/ongoingTasks/items": " 個file",
+        "filesystem/ongoingTasks/oprInto": " 去到 ",
+        "filesystems/error/directoryNotExists": "開唔到 folder",
+        "filesystems/error/directoryNotExistsInfo": "呢個 folder 開唔到, 有可能喺你冇適當嘅權限",
+        "filesystems/error/directoryGone": "呢個 folder 可能俾人郁咗或者俾人剷咗",
+        "filesystems/error/directoryGoneInfo": "你開緊嘅folder 收咗皮。有可能喺佢俾人郁咗或者剷咗",
+
+        "fileproperties/contents/shortcut": "捷徑",
+        "fileproperties/contents/location": "檔案位置",
+        "fileproperties/contents/directPath": "完整嘅路徑",
+        "fileproperties/contents/size": "File szie",
+        "fileproperties/contents/datemodified": "最後一次改嘅日期",
+        "fileproperties/contents/md5": "MD5 值",
+        "fileproperties/contents/openwith": "預設用嚟開嘅程式",
+        "fileproperties/contents/changeDefaultWebApp": "改預設程式",
+        "fileproperties/contents/shortcutTargetFullpath": "捷徑 指向完整路徑",
+        "fileproperties/contents/shortcutTarget": "捷徑指向資料夾",
+
+        "systemsetting/index/title": "ArOZ 在線系統設定",
+        "systemsetting/index/titlebutton": "系統設定",
+        "systemsetting/index/host": "Server",
+        "systemsetting/index/hostdesc": "主機資訊",
+        "systemsetting/index/device": "你部 devices",
+        "systemsetting/index/devicedesc": "IOT 或者你嘅鄰近裝置",
+        "systemsetting/index/network": "網絡連線",
+        "systemsetting/index/networkdesc": "WiFi ,或者 LAN",
+        "systemsetting/index/personalization": "個人化設定",
+        "systemsetting/index/personalizationdesc": "系統風格",
+        "systemsetting/index/user": "使用者",
+        "systemsetting/index/userdesc": "你嘅帳戶, 裝置識別碼",
+        "systemsetting/index/time": "日期同埋時間",
+        "systemsetting/index/timedesc": "系統時間同日曆",
+        "systemsetting/index/storage": "檔案同埋USB",
+        "systemsetting/index/storagedesc": "搵檔案同埋HDD管理",
+        "systemsetting/index/arozsync": "ArOZ Sync",
+        "systemsetting/index/arozsyncdesc": "分散式系統同埋Sync",
+        "systemsetting/index/arozcluster": "ArOZ Clusert",
+        "systemsetting/index/arozclusterdesc": "MapReduce,Cluster setting",
+        "systemsetting/index/backupAndRestore": "備份同回復",
+        "systemsetting/index/backupAndRestoreDesc": "整還原點/build系統",
+        "systemsetting/index/about": "有關 ArOZ",
+        "systemsetting/index/aboutdesc": "聯絡我們或者想了解多啲"
+
+    }
+}

+ 14 - 1
src/Video/index.php

@@ -1,5 +1,10 @@
 <?php
 include '../auth.php';
+
+//Patch for uploads folder not found exception during upload with Upload Manager
+if (!file_exists("uploads/")){
+	mkdir("uploads",0777,true);
+}
 ?>
 <!DOCTYPE html>
 <meta name="apple-mobile-web-app-capable" content="yes" />
@@ -100,7 +105,15 @@ if (isset($_GET['filepath']) && $_GET['filepath'] != "" ){
 	foreach($playlists as $playlist){
 		if (is_dir($playlist)){
 			$videos = glob($playlist . '/*.mp4');
-			$playlistName = hex2bin(basename($playlist));
+			
+			//check if PHP was higher than 7.4, if true then not using inith filename
+			if(ctype_xdigit($playlist)){
+				$playlistName = hex2bin(basename($playlist));
+			}else{
+				$playlistName = basename($playlist);
+			}
+
+			
 			$box = str_replace("%PlayListName%",$playlistName,$templateA);
 			if (count($videos) != 0){
 				$box = str_replace("%PlayPlayList%","vidPlay.php?src=".$videos[0]."&playlist=".$playlist."",$box);

+ 17 - 3
src/Video/new_folder.php

@@ -6,8 +6,22 @@ include '../auth.php';
 $foldername = $_POST['name'];
 $storage = $_POST['storage'];
 
-if (file_exists($storage . $foldername . "/") == false){
-	mkdir($storage . bin2hex($foldername) . "/", 0777);
-	echo 'DONE';
+//declare the PHP_VERSION_ID
+if (!defined('PHP_VERSION_ID')) {
+   $version = explode('.', PHP_VERSION);
+   define('PHP_VERSION_ID', ($version[0] * 10000 + $version[1] * 100 + $version[2]));
+}
+		
+//check if PHP was higher than 7.4, if true then not using inith filename
+if(PHP_VERSION_ID >= 70404){
+    if (file_exists($storage . $foldername . "/") == false){
+		mkdir($storage . $foldername . "/", 0777);
+		echo 'DONE';
+	}
+}else{
+	if (file_exists($storage . $foldername . "/") == false){
+		mkdir($storage . bin2hex($foldername) . "/", 0777);
+		echo 'DONE';
+	}
 }
 ?>

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio