diff --git a/colony.src b/colony.src
deleted file mode 100644
index 48865b63c8346126836d38bf7d38860ce3ba407f..0000000000000000000000000000000000000000
--- a/colony.src
+++ /dev/null
@@ -1,93 +0,0 @@
-// Tool to send to rented servers the default binaries from computers
-// Note that the utility methods to transfer a file only accepts complete path
-// "connect" and "transfer" should be easy reusable for a code pulling tool (issue #7)
-
-imports = {}
-imports.temp = {}
-
-imports.utils = {}
-imports.utils.ParamLength = function(params)
-	if params == null then return 0
-	return params.len
-end function
-imports.utils.GetParam = function(params, index)
-	if index < 0 or index >= imports.utils.ParamLength(params) then return null
-	return params[index]
-end function
-imports.utils.HasFlag = function(params, short, long)
-	if imports.utils.ParamLength(params) == 0 then return null
-	param = params[0]
-	return param == "-"+short or param == "-"+long
-end function
-imports.utils.GetProgName = function()
-	return program_path.split("/")[-1]
-end function
-
-imports.remote = {}
-
-imports.remote.getService = function(port)
-	if port == 22 then return "ssh"
-	if port == 21 then return "ftp"
-	return null
-end function
-imports.remote.connect = function(shell, user, password, ip, port=null, service=null)
-	if not shell then shell = get_shell
-	if not port then port = 22
-	if typeof(port) == "string" then port = port.to_int
-	if not service then
-		service = imports.remote.getService(port)
-		if not service then return null
-	end if
-	return shell.connect_service(ip, port, user, password, service)
-end function
-imports.remote.transfer = function(shell, origin, dest, upload=true)
-	if upload then
-		sourceShell = get_shell
-		targetShell = shell
-	else
-		sourceShell = shell
-		targetShell = get_shell
-	end if
-	if dest[dest.len-2] != "/" then dest = dest + "/"
-	fileName = origin[path.lastIndexOf("/")+1:]
-	destPath = dest+fileName
-	if targetShell.host_computer.File(destPath) != null then return destPath+" already exists"
-	return sourceShell.scp_upload(origin, dest, targetShell)
-end function
-
-imports.file = {}
-
-imports.temp.AddCmd = function(arr, files)
-	for file in files
-		arr.push("/bin/"+file)
-	end for
-end function
-imports.temp.AddSoft = function(arr, files)
-	for file in files
-		arr.push("/usr/bin/"+file+".exe")
-	end for
-end function
-
-imports.file.MISSING = []
-imports.temp.AddCmd(imports.file.MISSING,["airmon","aireplay","aircrack","nmap","smtp-user-list","decipher","scanlib","scanrouter"])
-imports.temp.AddSoft(imports.file.MISSING,["Map","Manual","AdminMonitor"])
-
-imports.temp = null
-
-//command: colony
-length = imports.utils.ParamLength(params)
-if length < 3 or length > 5 or imports.utils.HasFlag(params, "h", "help") then exit("<b>Usage: "+imports.utils.GetProgName()+" [user] [password] [ip] [(opt) port] [(opt) service]</b>")
-comp = get_shell.host_computer
-
-user = imports.utils.GetParam(params, 0)
-password = imports.utils.GetParam(params, 1)
-ip = imports.utils.GetParam(params, 2)
-port = imports.utils.GetParam(params, 3)
-service = imports.utils.GetParam(params, 4)
-
-remote = imports.remote.connect(get_shell, user, password, ip, port, service)
-if not remote then exit("Can't connect to remote server")
-for path in imports.file.MISSING
-	result = imports.remote.transfer(remote, path, path[:path.lastIndexOf("/")], true)
-	if result != 1 then print(result)
-end for
\ No newline at end of file
diff --git a/example_scripts/xbuild.src b/example_scripts/xbuild.src
deleted file mode 100644
index c7d5c8a3f9af522475db0411d54554bf8594233c..0000000000000000000000000000000000000000
--- a/example_scripts/xbuild.src
+++ /dev/null
@@ -1,288 +0,0 @@
-"#import includes/utils.inc.src"
-"#import includes/file.inc.src"
-
-// To allow self-building : build it with build, then use it to build it's own source to include libraries
-"#ifbuild"
-if not globals.hasIndex("imports") then
-	imports = {}
-	imports.utils = {}
-	imports.file = {}
-	utils = imports.utils
-	file = imports.file
-	
-	utils.GetProgName = function()
-		// To make sure we notice it's not updated
-		return program_path.split("/")[-1] +" (by build)"
-	end function
-	utils.Print = function(reason)
-		print(imports.utils.GetProgName()+": "+reason)
-	end function
-	utils.Exit = function(reason)
-		exit(imports.utils.GetProgName()+": "+reason)
-	end function
-	utils.ParamLength = function(params)
-		length = 0
-		for param in params
-			if param[0] != "-" then length = length + 1
-		end for
-		return length
-	end function
-	utils.GetParam = function(params, index)
-		i = 0
-		for param in params
-			if param[0] == "-" then continue
-			if (i == index) then return param
-			i = i+1
-		end for
-		return null
-	end function
-	utils.HasFlag = function(params, short, long)
-		// Not critical for a self-build
-		return long != "help"
-	end function
-	utils.leftSlice = function(str,stop)
-		if stop == 0 then return ""
-		return str[:stop]
-	end function
-	file.NEW_LINE = char(10)
-	file.AbsolutePath = function(path, current)
-		if path[0] == "/" then return path
-		if current == null then current = get_shell.host_computer.current_path
-		path = current + "/" + path
-		return path
-	end function
-	file.getFiles = function(path, recursive)
-		return [get_shell.host_computer.File(path)]
-	end function
-	file.ReadAllLines = function(target)
-		utils.Print("Reading file '"+path+"'")
-		// Enough to self-build
-		if typeof(target) == "string" then
-			target = get_shell.host_computer.File(target)
-		end if
-		return target.content.split(file.NEW_LINE)
-	end function
-	file.GetDir = function(path)
-		if typeof(path) == "file" then path = path.path
-		return path[0:path.lastIndexOf("/")]
-	end function
-	file.GetName = function(path)
-		return  path[path.lastIndexOf("/")+1:]
-	end function
-	file.FileNameNoExt = function(path)
-		index = path.lastIndexOf("/")
-		if index != null then path = path[index+1:]
-		index = path.indexOf(".")
-		if index != null then path = path[:index]
-	return path
-end function
-end if
-"#endif"
-
-//command: xbuild
-length = imports.utils.ParamLength(params)
-if length < 2 or length > 4 or imports.utils.HasFlag(params, "h", "help") then exit("<b>Usage: "+imports.utils.GetProgName()+" [file] [build dir] [(opt) preproc dir] --save</b>")
-comp = get_shell.host_computer
-
-// Return null for an empty String, returns the last character otherwise
-// Used to identify Strings
-GetLastChar = function(str)
-	if not str then return null
-	if str.len < 1 then return null
-	return str[str.len-1]
-end function
-
-source = imports.utils.GetParam(params,0)
-build = imports.utils.GetParam(params,1)
-preproc = imports.utils.GetParam(params,2)
-save = imports.utils.HasFlag(params,"s","save")
-if not preproc then
-	preproc = comp.current_path
-else
-	if GetLastChar(preproc) == "/" then preproc = preproc[0:preproc.len-1]	
-	print(preproc)
-	preproc = imports.file.AbsolutePath(preproc)
-	print(preproc)
-end if
-
-// A " character
-QUOTE = """"
-
-// If a line is made of a String with the flag character, returns the text inside
-StringInstruction = function(str)
-	if GetLastChar(str) != QUOTE then return null
-	if str[0] != QUOTE then return null
-	str = str[1:str.len-1]
-	if str.indexOf(QUOTE) != null then return null
-	str = str.trim()
-	if str[0] != "#" then return ""
-	return str[1:str.len]
-end function
-
-// Remove the comment at the end of a line, and the extra spaces at the start and the end
-// Shouldn't affect the result of the script, but reduce the total character count
-WithoutComment = function(str)
-	Count = function(str, char)
-		count = 0
-		for car in str
-			if car == char then count = count + 1
-		end for
-		return count
-	end function
-
-	COM = "//"
-	index = str.indexOf(COM)
-	if index == null then return str
-	result = imports.utils.leftSlice(str,index)
-	if Count(result, QUOTE) % 2 != 0 then
-		result = result + COM + WithoutComment(str[index+2:str.len])
-	end if
-	return result.trim()
-end function
-
-// List of already imported libraries to avoid duplicating the code
-imported = []
-// If two imports are trying to import each other *before the other*, will make sure the script errors instead of going in an infinite loop
-check = []
-
-// Method called to read-copy a source file (either the original script or its libraries)
-InsertCode = function(path, original=false)
-	IMPORT = "import "
-	IFBUILD = "ifbuild"
-	ENDIF = "endif"
-	
-	// Store the processed source for this specific file
-	content = ""
-	nextLine = false
-	skip = false
-	followImport = false
-	
-	for line in imports.file.ReadAllLines(path)
-	    // Removes the extra space, the comment and AGAIN the extra space once the comment is removed
-		line = WithoutComment(line.trim())
-		//if line == null or line.len == 0 then continue
-		if line.len == 0 then continue
-		isImport = false
-		
-		instruct = StringInstruction(line)
-		if instruct != null then
-			if instruct.len == 0 then continue
-			
-		    // There's use of the "#command abc" syntax
-			line = null
-			isImport = false
-			for tag in [IMPORT, IFBUILD, ENDIF]
-				if instruct[0:tag.len] == tag then
-					if tag == IMPORT then 
-						isImport = true
-						followImport = true
-						
-						// If currently in "skip mode", obviously nothing should change :D  
-						if not skip then
-							importPath = imports.file.AbsolutePath(instruct[tag.len:], imports.file.GetDir(path))
-							// If already imported, skip to save characters
-							if imported.indexOf(importPath) == null then
-							    // If two imports tries to import each other, which one goes first? Nice paradox, right?
-								if check.indexOf(importPath) != null then exit("ERROR, cyclic import of "+importPath)
-								check.push(importPath)
-								// Recursive call
-								line = "imports.temp={}"+ imports.file.NEW_LINE + InsertCode(imports.file.AbsolutePath(importPath))
-								imported.push(importPath)
-							end if
-						end if
-					else
-						skip = (tag == IFBUILD)
-					end if
-					break		
-				end if
-			end for
-			// No valid command? Unused string then skip it
-			if line == null then continue
-		end if
-			
-		// followImport is memorized, isImport is reset after each line
-		if not isImport and followImport then
-			followImport = false	
-			line = "imports.temp={}" + imports.file.NEW_LINE + line
-		end if
-	
-	    // No writing in skip mode
-		if skip or line == null then continue
-
-        // A new line char is not required for the first line 
-		if nextLine then
-			content = content + imports.file.NEW_LINE
-		else
-			nextLine = true
-		end if
-		// FINALLY, we can add the preprocessed source :D
-		content = content + line
-	end for
-
-    // Wrong src path?
-	if content == "" then imports.utils.Print("Unable to read " + (path) + " !")
-	return content
-end function
-
-
-// Oh yes, we prepared the processing of source files, but we didn't even load any file at the moment!
-sourcePath = imports.file.AbsolutePath(source,preproc)
-// If we pass a dir path, all files in this dir will be loaded
-for sourceFile in imports.file.getFiles(sourcePath, false)
-	check = sourceFile.name.split(".")
-	found = false
-	// Useful when building a whole folder, but it's maybe a bit too intrusive?
-	for i in range(1, check.len-1)
-		if check[i] == "inc" or check[i] == "temp" then 
-			found = true
-			print("Temporary or library file found, skipping "+sourceFile.name)
-			break
-		end if
-	end for
-	if found then continue
-	
-	// Imports are based on the file containing the currently read script
-	// In other words, an "import importing another import" is based on the path of the IMPORT, not the main script
-	sourceLoc = sourceFile.path[preproc.len+1:]	
-	imported = []
-	check = []
-	content = InsertCode(sourceFile, true)
-	
-	// Adding the imports map if there's at least one import
-	if imported.len > 0 then content = "imports={}" + imports.file.NEW_LINE + content
-	// No need to call build if there's no source!
-	if content.len == 0 then exit("Empty source!")
-
-    // Create a temp source file for build, then either delete it or store it as .temp.src
-	//targetLoc = sourceLoc[0:sourceLoc.lastIndexOf(".")] + ".temp"
-	targetLoc = imports.file.FileNameNoExt(sourceLoc) + ".temp"
-	targetPath = imports.file.AbsolutePath(targetLoc,preproc)
-	
-	// If there's already a temp file, delete it
-	targetFile = comp.File(targetPath)
-	if targetFile then targetFile.delete
-	comp.touch(imports.file.GetDir(targetPath),imports.file.GetName(targetPath))
-	targetFile = comp.File(targetPath)
-	
-	// Build the preprocessed source
-	print("building "+content.len+" characters for "+sourceLoc)
-	targetFile.set_content(content)
-	
-	result = get_shell.build(targetPath, build)
-
-    // If there's no need to save the temp file, delete the OLD saved file to avoid checking an outdated source
-	if save then
-		targetFile.move(imports.file.GetDir(targetPath),imports.file.GetName(targetPath)+".src")
-	else
-		targetFile.delete
-		old = comp.File(targetPath+".src")
-		if old then old.delete
-	end if
-
-    // Pfew, we reached the end!
-	if result.len == 0 then
-		print("build successful.")
-	else
-		print(result)
-	end if
-end for
diff --git a/qtglogo.src b/qtglogo.src
deleted file mode 100644
index 7b0d3b3ded5ca9d0f059c1445be6918feefd2b60..0000000000000000000000000000000000000000
--- a/qtglogo.src
+++ /dev/null
@@ -1,89 +0,0 @@
-"#import includes/ui.inc.src"
-
-"#ifbuild"
-if not globals.hasIndex("imports") then
-	imports = {}
-	imports.ui = {}
-	
-	// Not regular spaces
-	imports.ui.SPACE = char(160)
-	
-	imports.ui.Repeat = function(char, len)
-		result = ""
-		for i in range(0,len-1)
-			result = result + char
-		end for
-		return result
-	end function
-	
-	imports.ui.Color = function(color, text)
-		return "<color="+color+">"+text+"</color>"
-	end function
-end if
-"#endif"
-
-R = @imports.ui.Repeat
-J = function(text)
-	return imports.ui.Color("#f4ae34",text)
-end function
-B = function(text)
-	return imports.ui.Color("#1a508d",text)
-end function
-N = function(text)
-	return imports.ui.Color("#182d53",text)
-end function
-
-// Bord droit
-temp = "&%%%"
-D = "  "+temp
-// Bord gauche
-G = temp+"&  "
-// Taille totale
-L = 80
-// Ligne vide
-V = R(" ",L-G.len-D.len)
-
-// Haut du moniteur
-print(B(R(" ", 6)+R("&", L-6-5)+R(" ", 5)))
-print(B(" "+"&"+R("%", L-2-1)+" "))
-print(B("&"+R("%", 4)+R(" ", L-5-4)+R("%", 4)))
-
-// Dessus vide
-for i in range(1,5)
-	print(B(G+V+D))
-end for
-
-// Pour simplifier un peu...
-R4=R(" ",4)
-R5=R(" ",5)
-R17=R(" ",17)
-
-// Source : univscan.gs
-print(B(G+R5+            J(",***,***,***,***,    ")                 +" "+               R17             +" "+R5+         J("***,***,***,***,*")           +D))
-print(B(G+R5+        J("*")+"%%%%%%%%%%%%%%"+J("**    ")            +" "+               R17             +" "+R5+      J("**")+"%%%%%%%%%%%%%"+J("**")     +D))
-print(B(G+         J("******")+"%%%%%%%%%%%%%%"+J("*****,")         +" "+     J("**************,**")    +" "+     J("*******")+"%%%%%%%%%%%%%"+J("**")    +D))
-print(B(G+   J("**")+"%%%%%"+J("*,         **")+"%%%%"+J("*,")      +" "+J("**")+"%%%%%%%%%%%%%"+J("**")+" "+      J("**")+"%%%%%"+J("**")      +R(" ",13)+D))
-print(B(G+   J("*,")+"%%%%%"+J("*,         **")+"%%%%"+J("*,")      +" "+J("******,")+"%%%%"+J("***,**")+" "+ J("**")+"%%%%%"+J("****")+"%%%%"+J("**") +R5+D))
-print(B(G+    J("**")+"%%%%%"+J("*,******** **")+"%%%%"+J("*,")     +" "+R5+  J("**")+"%%%%"+J("**") +R4+" "+J("**")+"%%%%%"+J("****")+"%%%%"+J("*******")+D))
-print(B(G+J("**")+"%%%%%"+J("*,,*")+"%%%%"+J("** **")+"%%%%"+J("*,")+" "+R5+  J("**")+"%%%%"+J("**") +R4+" "+J("**")+"%%%%%"+J("*****,**")+"%%%%%"+J("**")+D))
-print(B(G+J("**")+"%%%%%"+J("****")+"%%%%"+J("*****")+"%%%%"+J("*,")+" "+R5+  J("**")+"%%%%"+J("**") +R4+" "+J("**")+"%%%%%"+J("*****,**")+"%%%%%"+J("**")+D))
-print(B(G+          J("*,*,*,")+"*%%%%%%%%%%%%%"+J("*,*,*,")        +" "+R5+  J("*,")+"%%%%"+J("*,") +R4+" "+     J(",*,*,*,")+"%%%%%%%%%%%%%"+J(",*")    +D))
-print(B(G+R5+         J("**")+"%%%%%%%%%%%%%"+J("*****,")           +" "+R5+  J("**")+"%%%%"+J("**") +R4+" "+R5+      J("**")+"%%%%%%%%%%%%%"+J("**")     +D))
-print(B(G+R5+            J("****,**********")+"%%%%"+J("*,")        +" "+R5+       J("********")     +R4+" "+R5+          J("*******,*********")          +D))
-print(B(G+R(" ",18)+                           J("**####*,")        +R(" ", 41)+D))
-
-// Dessous vide
-for i in range(1,4)
-	print(B(G+V+D))
-end for
-
-// Bas du moniteur
-print(B("@"+R("%",L-1)))
-print(B(R(" ",4)+"&"+R("%",L-5-2)+"  "))
-print(R(" ",L))
-
-// Support du moniteur
-for i in range(1,2)
-	print(N(R(" ", 31)+R("&",L-31-29)+R(" ",29)))
-end for
-print(B(R(" ",17)+R("%",L-17-16)+R(" ",16)))
\ No newline at end of file
diff --git a/scripts/archives/README.md b/scripts/archives/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..c78bd8733ba55a69807d861b2ba8602eb06b64e7
--- /dev/null
+++ b/scripts/archives/README.md
@@ -0,0 +1,26 @@
+0) Go into the folder in which you want to create the qtg repo  
+1) Copy tools/archive.src into CodeEditor, then as archive.src  
+2) Execute:   
+build archive.src /bin  
+- archive is now available on the system  
+You can delete archive.src  
+3) Copy archives/qtg.txt into Notepad, then as REPO_NAME.tar  
+- archive checks for the .tar extention!  
+4) Execute:  
+archive REPO_NAME  
+- The folder REPO_NAME is now created  
+You can delete REPO_NAME.tar  
+5) Go into the repo, then copy archives/libs.txt as (REPO_NAME/)libs.tar  
+6) Execute:  
+cd REPO_NAME  
+archive libs  
+- The libs are now available on the repo  
+You can delete libs.tar  
+7) Copy the libs folder into all folders to make sure the .gs scripts are able to find them  
+8) Execute:  
+build preprocess.src /bin  
+preprocess is now available on the system to process .gs files  
+9) Execute:  
+cd tools  
+preprocess archive.src  
+This way, you'll have an archive tool which use the libs from the archive :P  
\ No newline at end of file
diff --git a/scripts/archives/libs.txt b/scripts/archives/libs.txt
new file mode 100644
index 0000000000000000000000000000000000000000..109fec4719981d16beff649a247250ed7d4706b9
--- /dev/null
+++ b/scripts/archives/libs.txt
@@ -0,0 +1,1938 @@
+"#archive file.inc.src"
+"#ifbuild"
+if not globals.hasIndex("imports") then imports = {}
+imports.temp = {}
+"#endif"
+imports.file = {}
+"#ifbuild"
+file = imports.file
+"#endif"
+
+// Global variable to block load/save operation to avoid unwanted persistant modifications
+imports.file.ENABLE_DISK = true
+
+// Constants
+imports.file.NEW_LINE = char(10)
+imports.file.NEW_LINE_CHAR = "\"+"n"
+imports.file.BOLD = "<b>"
+imports.file.BOLD_END = "</b>"
+
+// Those methods are only used when loading and will be erased by the preprocessor
+imports.temp.AddCmd = function(arr, files)
+	for file in files
+		arr.push("/bin/"+file)
+	end for
+end function
+imports.temp.AddSoft = function(arr, files)
+	for file in files
+		arr.push("/usr/bin/"+file+".exe")
+	end for
+end function
+
+// Generates more constants : the list of good/bad default commands
+// Intended to be used when securing a server to know which files to lock/delete
+imports.file.USELESS_SHARED = ["/etc/passwd","/server"]
+imports.temp.AddCmd(imports.file.USELESS_SHARED,["build","whois","nslookup"])
+imports.temp.AddSoft(imports.file.USELESS_SHARED,["CodeEditor", "Mail"])
+imports.file.USELESS_PROXY = []
+imports.temp.AddCmd(imports.file.USELESS_PROXY,["cd","ls","pwd","ifconfig","iwconfig","iwlist","cat","rm","mv","cp","ftp","mkdir","rmdir","chmod","reboot","scanLan","sudo","useradd","userdel","touch","chown","chgrp","groupadd","groupdel","groups"])
+imports.temp.AddSoft(imports.file.USELESS_PROXY,["Notepad"])
+// Screen management		"clear"
+// File access				"cd", "ls", "pwd", "cat"
+// File edition			"rm", "mv", "cp", "mkdir", "rmdir", "touch"
+// WAN commands			"ftp", "ssh"
+imports.file.SAFE = []
+imports.temp.AddCmd(imports.file.SAFE,["clear"])
+imports.file.SSH = []
+imports.temp.AddCmd(imports.file.SSH,["ssh"])
+imports.file.ALLOWED_SHARED = []
+imports.temp.AddSoft(imports.file.ALLOWED_SHARED,["Terminal","FileExplorer"])
+imports.file.ALLOWED_SERVICE = []
+imports.temp.AddCmd(imports.file.ALLOWED_SERVICE,["cd","ls","pwd","cat","rm","mv","cp","mkdir","rmdir","touch","ftp"])
+imports.temp.AddSoft(imports.file.ALLOWED_SERVICE,["Notepad"])
+imports.file.MISSING = []
+imports.temp.AddCmd(imports.file.MISSING,["airmon","aireplay","aircrack","nmap","smtp-user-list","decipher","scanlib","scanrouter"])
+imports.temp.AddSoft(imports.file.MISSING,["Map","Manual","AdminMonitor"])
+
+// Loads the existing file and save it with extra content
+imports.file.append = function(content, path, name, separator=imports.file.NEW_LINE, origin=null)
+	data = imports.file.load(path, name, origin)
+	if data then
+		if separator != null then content = separator + content
+		content = data + content
+	end if
+	imports.file.save(content, path, name, origin)
+end function
+
+// Write the specified content to the disk
+imports.file.save = function(content, path, name, origin=null)
+	if not imports.file.ENABLE_DISK then return
+	comp = imports.file.GetOrigin(origin)
+	if name[0] == "/" then
+		path = ""
+	else
+		path = imports.file.AbsolutePath(path)
+	end if
+	data = name.split("/")
+	if data.len > 1 then
+		for i in range(0, data.len-2)
+			name = data[i]
+			if name == "" then continue 
+			comp.create_folder(path, name)
+			path = path +"/"+ name
+		end for
+	end if
+	name = data[data.len-1]
+	
+	path = imports.file.PatchPath(path, origin)
+	file = comp.File(path+name)
+	if not file then
+		comp.touch(path, name)
+		file = comp.File(path+name)
+	end if
+	if not file then print("Could not create " + path + name)
+	if imports.file.FileAccess(file,"w") then file.set_content(content)
+end function
+
+// Load the content of a file, or NULL if the file is not available
+imports.file.load = function(path, name, origin=null)
+	if not imports.file.ENABLE_DISK then return null
+	path = imports.file.AbsolutePath(path)
+	path = imports.file.PatchPath(path, origin)
+	file = path+name
+	file = imports.file.GetOrigin(origin).File(file)
+	if not file or file.is_binary or not imports.file.FileAccess(file,"r") then return null
+	return file.content
+end function
+
+// For remote files, files intended to be written in the user folders should not be written to the server's users!
+imports.file.PatchPath = function(path, origin=null)
+	if path == null then path = ""
+	if path.len == 0 or path[-1:] != "/" then path = path + "/"
+	if origin == null then return path
+	
+	path = path[1:]	
+	isUser = imports.utils.starts_with(path, "home/")
+	isRoot = imports.utils.starts_with(path, "root/")
+	
+	// Strips the user-part of the path : /root/XXX => /home/XXX ; /home/USER/XXX => /home/XXX ; /home/XXX => /home/XXX
+	if isUser or isRoot then		
+		path = path[path.indexOf("/")+1:] // Removes /root/ or /home/
+		
+		// Special case: skipping if home without a user
+		if isUser and imports.utils.char_count(path, "/") != 1 then path = path[path.indexOf("/")+1:] // Removes the username
+		
+		path = "home/"+path
+	end if
+		
+	return "/server/remote/"+path
+end function
+
+// Allows to dynamically handle files on a remote shell (local machine by default)
+imports.file.GetOrigin = function(origin)
+    if origin == null then origin = get_shell
+    if typeof(origin) == "shell" then origin = origin.host_computer
+    if typeof(origin) != "computer" then origin = null
+    return origin
+end function
+
+// If the path is a relative path, append the current_path to it in order to generate a unique file location
+imports.file.AbsolutePath = function(path, current=null, origin=null)
+	if not path then
+		path = ""
+	else if path[0] == "/" then
+		return path
+	end if
+
+	if current == null then current = imports.file.GetOrigin(origin).current_path
+	if path then current = current + "/" + path
+	return current
+end function
+
+// If the path is the one of a file, loads this unique file into an array
+// If the path is the one of a directory, loads all the contained files into said array
+imports.file.getFiles = function(path, recursive, origin=null)
+	result = []
+	imports.file._getFiles(result, imports.file.GetOrigin(origin).File(path), recursive)
+	return result
+end function
+imports.file._getFiles = function(result, file, recursive)
+	if not file.is_folder then
+		result.push(file)
+		return
+	end if
+	for child in file.get_files
+		result.push(child)
+	end for
+	if recursive then
+		for dir in file.get_folders
+			imports.file._getFiles(result, dir, recursive)
+		end for
+	end if
+end function
+
+// Sanity checks a file for allowed permission, return false if one perm is missing + print an error message
+imports.file.FileAccess = function(file, perms, shout=true)
+	if not file then return false
+	for i in range(0,perms.len-1)
+		perm = perms[i]
+		if file.has_permission(perm) then continue
+		if shout then imports.utils.Print("Error: can't use ('"+perm+"') contents of '"+file.path+"'")
+		return false
+	end for
+	return true
+end function
+
+// Tries deleting a file, but checks that you have the required permissions first (returns false if unallowed)
+imports.file.Delete = function(file)
+	if not imports.file.FileAccess(file) then return false
+	file.delete()
+	return true
+end function
+// Removes the file's name from the path
+imports.file.GetDir = function(path)
+	if typeof(path) == "file" then path = path.path
+	return path[0:path.lastIndexOf("/")]
+end function
+// Determines the filename from a String path
+imports.file.GetName = function(path)
+	if typeof(path) == "file" then path = path.path
+	return path[path.lastIndexOf("/")+1:]
+end function
+
+// Checks that a file is a folder in which files can be added
+imports.file.WritableFolder = function(folder)
+	if not imports.file.FileAccess(folder,"w") then return false
+	return folder.is_folder
+end function
+
+// Tries to read a file, it it's possible returns all its lines in an array
+// Returns an empty array if the file can't be read
+imports.file.ReadAllLines = function(file, origin=null)
+	if typeof(file) == "string" then
+		index = file.lastIndexOf("/")
+		path = file[:index]
+		name = file[index+1:]
+		content = imports.file.load(path, name, origin)
+		if content == null then return []
+	else
+		if not file then return []
+		if not imports.file.FileAccess(file,"r") then return []
+		if file.is_binary then return []
+		content = file.content
+	end if
+	return content.split(imports.file.NEW_LINE)
+end function
+
+// Creates an object X of version Y in a dir PATH/X/X.Y.ext
+imports.file.VersionedFilePath = function(path, filename)
+	parts = filename.split(".")
+	name = parts[0]
+	version = []
+	ext = null
+	if parts.len > 1 then
+		ext = parts[parts.len-1]
+		if parts.len > 2 then
+			for i in Range(1,parts.len-2)
+				version.push(parts[i])
+			end for
+		end if
+	end if
+	return imports.file.VersionedFilePath(path, name, version, ext)
+end function
+imports.file.VersionedFilePath = function(path, name, version, ext=null)
+	version = imports.file._DottedString(version)
+	path = path+name+"/"+version+"/"+name+"."+version
+	if ext != null then path = path +"."+ imports.file._DottedString(ext)
+	return path
+end function
+imports.file._DottedString = function(arr)
+	if typeof(arr) == "string" then return arr
+	result = ""
+	next = false
+	for item in arr
+		if next then
+			result = result + "."
+		else
+			next = true
+		end if
+		result = result + item
+	end for
+	return result
+end function
+
+// Get the file name from a path and remove double extensions  
+// "Scripts/preprocessor.gs.src" => "preprocessor"
+imports.file.FileNameNoExt = function(path)
+	index = path.lastIndexOf("/")
+	if index != null then path = path[index+1:]
+	index = path.indexOf(".")
+	if index != null then path = path[:index]
+	return path
+end function
+
+// Generates a String with random characters
+// Perfect for a temp file!
+imports.file.random = function(len) 
+	chars = ["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]
+	result = ""
+	for i in range(1,len)
+		result = result + chars[floor(rnd()*chars.len)]
+	end for
+	return result
+end function
+
+// Countermethod of imports.utils.IncludeLib
+// Deletes an auto-downloaded lib file
+imports.file.RemoveLib = function(includeData)
+	if typeof(includeData) != "list" then return false
+	if includeData.len != 2 then return false
+	path = includeData[1]
+	if typeof(path) != "string" then return false
+	if path.len == 0 then return false
+	return imports.file.Delete(get_shell.host_computer.File(path))
+end function
+
+"#archive filereaper.inc.src"
+"#import libs/utils.src"
+"#import libs/file.src"
+
+"#ifbuild"
+if not globals.hasIndex("imports") then imports = {}
+imports.temp = {}
+"#endif"
+imports.filereaper = {}
+"#ifbuild"
+filereaper = imports.filereaper
+"#endif"
+
+// Missing files are NULL
+// Files with not enough access rights are FALSE
+
+// Part 1 : utility methods
+
+// FileReaper utility methods to load a child from a File object
+imports.filereaper.FindFile = function(origin, children)
+	if origin == null then return null
+	// May be available through another user
+	if not imports.file.FileAccess(origin, "r") then return false	
+	for child in children
+		if typeof(origin) != "file" then return origin
+		origin = imports.filereaper._FindFile(origin, child)
+	end for
+	return origin
+end function
+imports.filereaper._FindFile = function(origin, child)	
+	found = null
+	for file in origin.get_files()
+		if file.name == child then
+			found = file
+			break
+		end if
+	end for
+	if found == null then
+		for file in origin.get_folders()
+			if file.name == child then
+				found = file
+				break
+			end if
+		end for
+	end if
+	if found != null then
+		// May be available through another user
+		if not imports.file.FileAccess(found, "r") then return false
+		if not found.is_binary then return found
+	end if
+	return null
+end function
+
+// Call either onFoundFile or onMissingFile according to the result of FindFile
+imports.filereaper.HandleFile = function(origin, children, onFoundFile, onMissingFile, pars)
+	file = imports.filereaper.FindFile(origin, children)
+	if file == false then return
+	if file == null then
+		if @onMissingFile != null then onMissingFile(pars)
+		return
+	end if
+	pars.push(file)
+	if @onFoundFile != null then onFoundFile(pars)
+end function
+
+// Part 2 : file reading
+
+// Read the lines of creditential files
+imports.filereaper.ReadCredsFile = function(file)
+	creds = []
+	for line in imports.file.ReadAllLines(file)
+		if line.len == 0 then continue
+		creds.push(line)
+	end for
+	return creds
+end function
+imports.filereaper.UsePasswdFile = function(pars)
+	state = pars[0]
+	file = pars[1]
+	imports.filereaper.LoadPasswdList([state, imports.filereaper.ReadCredsFile(file)])
+end function
+imports.filereaper.UseAccFile = function(pars)
+	state = pars[0]
+	user = pars[1]
+	isBank = pars[2]
+	file = pars[3]
+	imports.filereaper.LoadAccList([state,user,isBank,imports.filereaper.ReadCredsFile(file)])
+end function
+
+// Data handling from Filereaper (pars is shorthand for "params")
+// Thoses methods should be overriden by the calling scripts as necessary
+
+imports.filereaper.LoadUserList = function(pars)
+	state = pars[0]
+	users = pars[1]
+	for user in users
+		imports.filereaper.AddUser([state, user])
+	end for
+end function
+
+imports.filereaper.LoadPasswdList = function(pars)
+	state = pars[0]
+	creds = pars[1]
+	
+	for cred in creds
+		imports.filereaper.AddPasswd([state,cred])
+	end for
+end function
+
+imports.filereaper.LoadAccList = function(pars)
+	state = pars[0]
+	user = pars[1]
+	isBank = pars[2]
+	creds = pars[3]
+	for cred in creds
+		imports.filereaper.LoadAcc([state,user,isBank,cred])
+	end for
+end function
+
+imports.temp.X = function(pars)
+	imports.utils.Print("Error, calling a FileReaper default method without overriding it!")
+end function
+
+// state = pars[0]
+// shell = pars[1]
+imports.filereaper.UseShell = @imports.temp.X
+
+// state = pars[0]
+// users = pars[1]
+imports.filereaper.AddUser = @imports.temp.X
+
+// state = pars[0]
+imports.filereaper.MissingUserList = @imports.temp.X
+
+// state = pars[0]
+// cred = pars[1]
+imports.filereaper.AddPasswd = @imports.temp.X
+
+// state = pars[0]
+imports.filereaper.MissingPasswdList = @imports.temp.X
+
+// state = pars[0]
+// user = pars[1]
+// isBank = pars[2]
+// cred = pars[3]
+imports.filereaper.LoadAcc = @imports.temp.X
+
+// state = pars[0]
+// user = pars[1]
+// isBank = pars[2]
+imports.filereaper.MissingAcc = @imports.temp.X
+
+// Part 4 : using the exploits
+
+// "state" would be an object memorizing the state of the process
+// when a script doesn't have one, NULL can be passed as long the script-specific methods accept it
+
+imports.filereaper.exploitShell = function(pars)
+	imports.filereaper.UseShell(pars)
+	pars[1] = pars[1].host_computer
+	imports.filereaper.exploitComp(pars)
+end function
+imports.filereaper.exploitComp = function(pars)
+	state = pars[0]
+	comp = pars[1]
+	ACC_OK = @imports.filereaper.UseAccFile
+	ACC_FAIL = @imports.filereaper.MissingAcc
+	imports.filereaper.HandleFile(comp.File("/etc/passwd"), [], @imports.filereaper.UsePasswdFile, @imports.filereaper.MissingPasswdList, [state])
+	imports.filereaper.HandleFile(comp.File("/root/Config/Bank.txt"), [], @ACC_OK, @ACC_FAIL, [state, "root", true])
+	imports.filereaper.HandleFile(comp.File("/root/Config/Mail.txt"), [], @ACC_OK, @ACC_FAIL, [state, "root", false])
+	imports.filereaper.exploitFile(pars, comp.File("/home/"), false)
+end function
+imports.filereaper.exploitFile = function(pars, file, targetRoot=true)
+	state = pars[0]
+	if file == null or file == false then return file
+	if targetRoot and file.path != "/" then
+		if imports.filereaper.exploitFile(pars, file.parent, targetRoot) == true then return true
+	end if
+	if not imports.file.FileAccess(file, "r") then return false
+	
+	ACC_OK = @imports.filereaper.UseAccFile
+	ACC_FAIL = @imports.filereaper.MissingAcc
+	PASS_OK = @imports.filereaper.UsePasswdFile
+	PASS_FAIL = @imports.filereaper.MissingPasswdList
+	
+	if file.path == "/" then
+		imports.filereaper.HandleFile(file, ["etc","passwd"], @PASS_OK, @PASS_FAIL, [state])
+		imports.filereaper.HandleFile(file, ["root","Config","Bank.txt"], @ACC_OK, @ACC_FAIL, [state, "root", true])
+		imports.filereaper.HandleFile(file, ["root","Config","Mail.txt"], @ACC_OK, @ACC_FAIL, [state, "root", false])
+		
+		home = imports.filereaper._FindFile(file, ["home"])
+		imports.filereaper.HandleFile(home, [], null, @imports.filereaper.MissingUserList, [state])
+		if typeof(home) == "file" then imports.filereaper.exploitFile(pars, home, false)
+		return true
+	end if
+	
+	if imports.utils.starts_with(file.path, "/etc/") then
+		pasd = null
+		if file.path == "/etc/" then pasd = file
+		if file.path == "/etc/passwd" then pasd = imports.filereaper._FindFile(file, ["passwd"])
+		imports.filereaper.HandleFile(pasd, [], @PASS_OK, @PASS_FAIL, [state, pasd])
+	end if
+	
+	if imports.utils.starts_with(file.path, "/home/") then
+		count = imports.utils.char_count(file.path,"/")
+		if count == 2 then
+			for folder in file.getFolders()
+				imports.filereaper.exploitFile(pars, folder, targetRoot)
+			end for
+		else if count == 3 then
+			imports.filereaper.AddUser(file.name)
+		end if
+	else if imports.utils.starts_with(file.path, "/root/") then	
+		count = imports.utils.char_count(file.path,"/")+1
+	else
+		count = 0 //or return
+	end if
+	
+	if count == 4 and file.name == "Config" then
+		imports.filereaper.HandleFile(file, ["Bank.txt"], @ACC_OK, @ACC_FAIL, [state, file.parent.name, true])
+		imports.filereaper.HandleFile(file, ["Mail.txt"], @ACC_OK, @ACC_FAIL, [state, file.parent.name, false])
+	else if count == 5 and file.parent.name == "Config" and (file.name == "Bank.txt" or file.name == "Mail.txt") then
+		imports.filereaper.HandleFile(file, [], @ACC_OK, @ACC_FAIL, [state, file.parent.parent.name, file.name == "Bank.txt"])
+	end if
+end function
+
+"#archive passwords.inc.src"
+"#import libs/utils.inc.src"
+"#import libs/file.inc.src"
+
+"#ifbuild"
+if not globals.hasIndex("imports") then imports = {}
+imports.temp = {}
+"#endif"
+imports.passwords = {}
+"#ifbuild"
+passwords = imports.passwords
+"#endif"
+
+// Load a password file into a map
+imports.passwords.load = function(path, name, origin=null)
+	result = {}
+	// 'save (data, "/", FULL_PATH)' works, so load should work too, right?
+	if path == null then path = ""
+	if path.len > 0 and path[-1:] == "/" and name.len > 0 and name[0] == "/" then path = imports.utils.rightSlice(path,1)
+	for line in imports.file.ReadAllLines(path+name, origin)
+		line = line.split(":");
+		result[line[0]] = line[1]
+	end for
+	return result
+end function
+
+// Saves a password map into a file
+imports.passwords.save = function(data, path, name, origin=null)
+	imports.file.save(imports.passwords.Export(data), path, name, origin)
+end function
+
+imports.passwords.Export = function(data)
+	content = []
+	for entry in data
+		content.push(entry.key+":"+entry.value)
+	end for
+	return imports.utils.PrintList(content, imports.file.NEW_LINE)
+end function
+
+// Add a new user-hash line into a password map
+imports.passwords.AddUser = function(pass, line)
+	if typeof(line) == "string" then line = line.split(":")
+	index = line[0]
+	if imports.utils.SafeMapAccess(pass, index) == line[1] then return false
+	pass[index] = line[1]
+	return true
+end function
+
+"#archive qtglogo.inc.src"
+"#import libs/ui.inc.src"
+
+printLogo = function()
+    R = @imports.ui.Repeat
+    J = function(text)
+    	return imports.ui.Color("#f4ae34",text)
+    end function
+    B = function(text)
+    	return imports.ui.Color("#1a508d",text)
+    end function
+    N = function(text)
+    	return imports.ui.Color("#182d53",text)
+    end function
+    
+    // Bord droit
+    temp = "&%%%"
+    D = "  "+temp
+    // Bord gauche
+    G = temp+"&  "
+    // Taille totale
+    L = 80
+    // Ligne vide
+    V = R(" ",L-G.len-D.len)
+    
+    // Haut du moniteur
+    print(B(R(" ", 6)+R("&", L-6-5)+R(" ", 5)))
+    print(B(" "+"&"+R("%", L-2-1)+" "))
+    print(B("&"+R("%", 4)+R(" ", L-5-4)+R("%", 4)))
+    
+    // Dessus vide
+    for i in range(1,5)
+    	print(B(G+V+D))
+    end for
+    
+    // Pour simplifier un peu...
+    R4=R(" ",4)
+    R5=R(" ",5)
+    R17=R(" ",17)
+    
+    // Source : univscan.gs
+    print(B(G+R5+            J(",***,***,***,***,    ")                 +" "+               R17             +" "+R5+         J("***,***,***,***,*")           +D))
+    print(B(G+R5+        J("*")+"%%%%%%%%%%%%%%"+J("**    ")            +" "+               R17             +" "+R5+      J("**")+"%%%%%%%%%%%%%"+J("**")     +D))
+    print(B(G+         J("******")+"%%%%%%%%%%%%%%"+J("*****,")         +" "+     J("**************,**")    +" "+     J("*******")+"%%%%%%%%%%%%%"+J("**")    +D))
+    print(B(G+   J("**")+"%%%%%"+J("*,         **")+"%%%%"+J("*,")      +" "+J("**")+"%%%%%%%%%%%%%"+J("**")+" "+      J("**")+"%%%%%"+J("**")      +R(" ",13)+D))
+    print(B(G+   J("*,")+"%%%%%"+J("*,         **")+"%%%%"+J("*,")      +" "+J("******,")+"%%%%"+J("***,**")+" "+ J("**")+"%%%%%"+J("****")+"%%%%"+J("**") +R5+D))
+    print(B(G+    J("**")+"%%%%%"+J("*,******** **")+"%%%%"+J("*,")     +" "+R5+  J("**")+"%%%%"+J("**") +R4+" "+J("**")+"%%%%%"+J("****")+"%%%%"+J("*******")+D))
+    print(B(G+J("**")+"%%%%%"+J("*,,*")+"%%%%"+J("** **")+"%%%%"+J("*,")+" "+R5+  J("**")+"%%%%"+J("**") +R4+" "+J("**")+"%%%%%"+J("*****,**")+"%%%%%"+J("**")+D))
+    print(B(G+J("**")+"%%%%%"+J("****")+"%%%%"+J("*****")+"%%%%"+J("*,")+" "+R5+  J("**")+"%%%%"+J("**") +R4+" "+J("**")+"%%%%%"+J("*****,**")+"%%%%%"+J("**")+D))
+    print(B(G+          J("*,*,*,")+"*%%%%%%%%%%%%%"+J("*,*,*,")        +" "+R5+  J("*,")+"%%%%"+J("*,") +R4+" "+     J(",*,*,*,")+"%%%%%%%%%%%%%"+J(",*")    +D))
+    print(B(G+R5+         J("**")+"%%%%%%%%%%%%%"+J("*****,")           +" "+R5+  J("**")+"%%%%"+J("**") +R4+" "+R5+      J("**")+"%%%%%%%%%%%%%"+J("**")     +D))
+    print(B(G+R5+            J("****,**********")+"%%%%"+J("*,")        +" "+R5+       J("********")     +R4+" "+R5+          J("*******,*********")          +D))
+    print(B(G+R(" ",18)+                           J("**####*,")        +R(" ", 41)+D))
+    
+    // Dessous vide
+    for i in range(1,4)
+    	print(B(G+V+D))
+    end for
+    
+    // Bas du moniteur
+    print(B("@"+R("%",L-1)))
+    print(B(R(" ",4)+"&"+R("%",L-5-2)+"  "))
+    print(R(" ",L))
+    
+    // Support du moniteur
+    for i in range(1,2)
+    	print(N(R(" ", 31)+R("&",L-31-29)+R(" ",29)))
+    end for
+    print(B(R(" ",17)+R("%",L-17-16)+R(" ",16)))
+end function
+
+"#archive remote.inc.src"
+"#import libs/file.inc.src"
+
+"#ifbuild"
+if not globals.hasIndex("imports") then imports = {}
+imports.temp = {}
+"#endif"
+imports.remote = {}
+"#ifbuild"
+remote = imports.remote
+"#endif"
+
+imports.remote.cache = {}
+imports.remote.LoadFromCache = function(key, shell, user, pass, ip)
+	if imports.remote.cache.hasIndex(key) then return imports.remote.cache[key]
+	result = imports.remote.connect(shell, user, pass, ip)
+	imports.remote.cache[key] = result
+	return result
+end function
+
+imports.remote.servers = {}
+imports.remote.servers.FILES = function()
+	return imports.remote.LoadFromCache("files", get_shell, "root", "Helby", "220.47.69.119")
+end function
+imports.remote.servers.PROXY = function()
+	return imports.remote.LoadFromCache("proxy", get_shell, "root", "Helby", "220.47.69.119")
+end function
+imports.remote.servers.TEST = function()
+	return imports.remote.LoadFromCache("test", @imports.remote.servers.PROXY, "root", "Helby", "220.47.69.119")
+end function
+
+imports.remote.LAN = "0.0.0.0"
+imports.remote.LOCALHOST = "127.0.0.1"
+
+// Find the router for the network containing the device
+imports.remote.getRouter = function(ip)
+	ip = imports.remote.directIP(ip)
+	if not ip then return null
+	if is_lan_ip(ip) then return get_router()
+	if not is_valid_ip(ip) then return null
+	return get_router(ip)
+end function
+
+// Return the lan ip if available or the wan IP
+// Special cases : invalid ip (or offline) = NULL
+// 127.0.0.1 is this computer
+// 0.0.0.0 is the lan ip for the router of this network
+imports.remote.directIP = function(ip)
+	if not get_shell.host_computer.is_network_active then return null
+	if ip == imports.remote.LOCALHOST then return get_shell.host_computer.lan_ip
+	if is_lan_ip(ip) then return ip
+	if ip == imports.remote.LAN then return get_router().local_ip()
+	if not is_valid_ip(ip) then return null
+	return ip
+end function
+
+// Returns the default shell service for a given port
+imports.remote.getService = function(port)
+	if port == 22 then return "ssh"
+	if port == 21 then return "ftp"
+	return null
+end function
+
+// Connects a given shell to a new shell
+// If port is unspecified, use the default ssh port
+// If service is unspecified, use the default service for the port
+imports.remote.connect = function(shell, user, password, ip, port=null, service=null)
+	if not port then port = 22
+	if typeof(port) == "string" then port = port.to_int
+	if not service then
+		service = imports.remote.getService(port)
+		if not service then return null
+	end if
+	return shell.connect_service(ip, port, user, password, service)
+end function
+
+// Every truple of user-pass-ip, chains shells together
+// (Intended to be used with data from Map.conf)
+imports.remote.map = function(proxies)
+	shell = get_shell
+	for proxy in proxies
+		if shell == null then return null
+		shell = imports.remote.connect(shell, proxy[0], proxy[1], proxy[2])
+	end for
+	return shell
+end function
+
+
+// Checks if a given file already exists at the destination
+// If not, said file is uploaded
+// imports.remote.download = function(shell, origin, dest, anon=false)
+// return imports.remote.transfer(shell, get_shell, origin, dest, anon)
+// end function
+// imports.remote.upload = function(shell, origin, dest)
+// return imports.remote.transfer(get_shell, shell, origin, dest, anon)
+// end function
+// imports.remote.transfer = function(sourceShell, targetShell, origin, dest, anon=false)
+// if anon then
+// print("Les transferts anonymes font reset les harddrives suite à un bug")
+// print("Lien de l'issue : https://greytracker.org/bugzilla/show_bug.cgi?id=398")
+// print("Le mode anonyme est désactivé")
+// if not imports.utils.UserPrompt("Acceptez-vous de transférer non-anonymement?") then exit()
+// anon = false
+// end if
+
+// if dest[dest.len-1] != "/" then dest = dest + "/"
+// origin = imports.file.AbsolutePath(origin, null, sourceShell)
+
+// comp = sourceShell.host_computer
+// src = comp.File(origin)
+// if src == null then
+// print(origin+" doesn't exist on source server")
+// return null
+// end if
+
+// fileName = origin[origin.lastIndexOf("/")+1:]
+// destPath = dest+fileName
+// if targetShell.host_computer.File(destPath) != null then
+// print(destPath+" already exists on target server")
+// return null
+// end if
+
+// temp = null
+// if anon then
+// comp.create_folder("/", "server")
+// comp.create_folder("/server/", "temp")
+// while true
+// Random name until finding an unused one
+// fileName = imports.file.random(16)
+// if comp.File("/server/temp/"+fileName) != null then continue
+// if targetShell.host_computer.File(dest+fileName) != null then continue
+// break
+// end while
+
+// DANGER: cette ligne fait remplacer votre harddrive par celui du serveur!?
+// src.copy("/server/temp/", fileName)
+// temp = comp.File("/server/temp/"+fileName)
+// origin = temp.path
+// end if
+
+// result = sourceShell.scp(origin, dest, targetShell)
+// if temp != null then temp.delete()
+// return targetShell.host_computer.File(destPath)
+// end function
+
+// Downloads a file with scp if it doesn't already exist
+// Can also use a different name than the original file : if said name is blank, a random name will be generated
+imports.remote.download = function(shell, origin, dest, destName=null)
+	
+	// TODO : remove this limiter when the GH issue gets fixed
+	if destName != null then
+		print("Les téléchargements renommés font reset les harddrives suite à un bug")
+		print("Lien de l'issue : https://greytracker.org/bugzilla/show_bug.cgi?id=398")
+		print("Le renommage est désactivé")
+		if not imports.utils.UserPrompt("Acceptez-vous de transférer avec le nom d'origine?") then exit()
+		destName = null
+	end if
+	// TODO: temporary limiter ends here
+	
+	temp = imports.remote.PreTransfer(origin, dest, shell, get_shell)
+	origin = temp[0]
+	dest = temp[1]
+	
+	if destName != null	then
+		server = shell.host_computer
+		if not imports.remote.CheckTempFolder(server) then return "unable to write in /server/temp/"
+		
+		src = server.File(origin)
+		if src == null then return origin+" doesn't exist on source server"
+		
+		destName = imports.remote.RandomName(server, get_shell.host_computer, dest, destName)
+		
+		// DANGER: cette ligne fait remplacer votre harddrive par celui du serveur!?
+		src.copy("/server/temp/", destName)
+		origin = "/server/temp/"+destName
+	end if
+	
+	result = imports.remote.transfer(shell, get_shell, origin, dest)
+	
+	if destName != null then src.delete()
+	
+	return result
+end function
+
+// Upload a file with scp if it doesn't already exist
+// Can also use a different name than the original file : if said name is blank, a random name will be generated
+imports.remote.upload = function(shell, origin, dest, destName=null)
+	
+	// TODO : remove this limiter when the GH issue gets fixed
+	if destName != null then
+		print("Les téléchargements renommés font reset les harddrives suite à un bug")
+		print("Lien de l'issue : https://greytracker.org/bugzilla/show_bug.cgi?id=398")
+		print("Le renommage est désactivé")
+		if not imports.utils.UserPrompt("Acceptez-vous de transférer avec le nom d'origine?") then exit()
+		destName = null
+	end if
+	// TODO: temporary limiter ends here
+	
+	temp = imports.remote.PreTransfer(origin, dest, get_shell, shell)
+	origin = temp[0]
+	dest = temp[1]
+	
+	transferDest = dest
+	
+	if destName != null then
+		server = shell.host_computer
+		if not imports.remote.CheckTempFolder(server) then return "unable to write in /server/temp/"
+		
+		if dest[dest.len-1] != "/" then dest = dest + "/"
+		// The destination file doesn't need to be available in temp folder
+		// Also, in this case the server is ALSO the target
+		destName = imports.remote.RandomName(null, server, "", destName)
+		
+		src = server.File(dest+destName)
+		if src != null then return dest+destName+" already exists on target server"
+		
+		// The temporary folder doesn't need to be available in the target folder
+		folderName = imports.remote.RandomName(server, null, dest, "")
+		// Creates a temporary folder which will receive the file, no matter what its name is
+		server.create_folder("/server/temp/", folderName)
+		transferDest = "/server/temp/"+folderName
+	end if
+	
+	result = imports.remote.transfer(get_shell, shell, origin, transferDest)
+	
+	if destName != null then
+		// DANGER: cette ligne fait remplacer votre harddrive par celui du serveur!?
+		if result then result.move(dest,destName)
+		// Delete the temporary folder
+		server.File(transferDest).delete()
+	end if
+	
+	return result
+end function
+
+// Run a scp command after performing some sanity checks
+// SHOULD NOT BE USED DIRECTLY, use the upload and download methods
+// Returns : the new file, or NULL
+imports.remote.transfer = function(sourceShell, targetShell, origin, dest)
+	src = sourceShell.host_computer.File(origin)
+	if src == null then return origin+" doesn't exist on source server"
+	
+	fileName = origin[origin.lastIndexOf("/")+1:]
+	
+	destPath = dest+fileName
+	comp = targetShell.host_computer
+	
+	if comp.File(destPath) != null then return destPath+" already exists on target server"
+	
+	result = sourceShell.scp(origin, dest, targetShell)
+	if result != true then return result
+	return comp.File(destPath)
+end function
+
+imports.remote.PreTransfer = function(origin, dest, originShell, destShell)
+	if dest[dest.len-1] != "/" then dest = dest + "/"
+	origin = imports.file.AbsolutePath(origin, null, originShell)
+	dest = imports.file.AbsolutePath(dest, null, destShell)
+	return [origin, dest]
+end function
+
+// Returns false if /server/temp/ is not usable
+imports.remote.CheckTempFolder = function(comp)
+	folder = comp.File("/server/temp")
+	if folder != null then
+		return imports.file.WritableFolder(folder)
+	end if
+	folder = comp.File("/server/")
+	if folder != null then
+		if not imports.file.WritableFolder(folder) then return false
+		comp.create_folder("/server/", "temp")
+		return true
+	end if
+	folder = comp.File("/")
+	if not imports.file.WritableFolder(folder) then return false
+	comp.create_folder("/", "server")
+	comp.create_folder("/server/", "temp")
+	return true
+end function
+
+// Generates a random name not-already-used by a file
+imports.remote.RandomName = function(serverComp, targetComp, destPath, name)
+	if name != "" then return name
+	while true
+		name = imports.file.random(16)
+		if serverComp != null and serverComp.File("/server/temp/"+name) != null then continue
+		if targetComp != null and targetComp.File(destPath+name) != null then continue
+		return name
+	end while
+end function
+
+"#archive ui.inc.src"
+"#import libs/utils.inc.src"
+"#import libs/file.inc.src"
+
+"#ifbuild"
+if not globals.hasIndex("imports") then imports = {}
+imports.temp = {}
+"#endif"
+imports.ui = {}
+"#ifbuild"
+ui = imports.ui
+"#endif"
+
+// Changes a two-dimensional array into format_column styled text
+imports.ui.FormatMatrix = function(matrix, padding=null)
+	if padding == null then padding = imports.ui.PADDING
+	text = ""
+	
+	nextLine = false
+	for line in matrix
+		if nextLine then text = text + imports.file.NEW_LINE else nextLine = true		
+		nextCell = false
+		text = text + imports.utils.PrintList(line, padding)
+	end for
+	
+	return text
+end function
+
+// Not regular spaces
+imports.ui.SPACE = char(160)
+imports.ui.PADDING = imports.ui.SPACE+imports.ui.SPACE
+
+// Changes a character
+imports.ui.ReplacePos = function(text, pos, char)
+	return imports.utils.leftSlice(cell,pos) + char + imports.utils.rightSlice(cell,pos+1)	
+end function
+
+// Changes a two-dimensional array into the DATA from format_column
+// Spaces are treated as normal text data, space padding is only inserted between the cells of the matrix
+imports.ui.FormattedCols = function(matrix)
+	indexes = []
+	
+	for line in matrix
+		indexData = [];indexes.push(indexData)
+		for i in range(0,line.len-1)
+			cell = line[i]
+			if cell == null then cell = ""
+			if typeof(cell) == "number" then cell = cell.str
+			if typeof(cell) != "string" then cell = ""
+			index = [];indexData.push(index)
+			for j in range(0,cell.len-1)
+				if cell[j] != " " then continue
+				index.push(j)
+				cell = imports.ui.ReplacePos(cell, j, "@")
+			end for
+			line[i] = cell
+		end for
+	end for
+
+	data = format_columns(imports.ui.FormatMatrix(matrix, " ")).split(imports.file.NEW_LINE)
+	matrix = []
+	
+	for line in data
+		cells = [];matrix.push(cells)
+		lastCut = 0
+		inCell = true
+		for i in range(0,line.len-1)
+			char = line[i]
+			if char == imports.ui.SPACE then
+				inCell = false
+				continue
+			end if
+			if inCell then continue
+			cell = line[lastCut:i-imports.ui.PADDING.len]
+			lastCut = i
+			cells.push(cell)
+			inCell = true
+		end for
+		cells.push(line[lastCut:])
+	end for
+
+	for pos in range(0,matrix.len-1)
+		line = matrix[pos]
+		indexData = indexes[pos]
+		for i in range(0,line.len-1)
+			cell = line[i]
+			index = indexData[i]
+			for j in index
+				cell = imports.ui.ReplacePos(cell, j, " ")
+			end for
+			line[i] = cell
+		end for
+	end for
+	
+	return matrix
+end function
+
+// Repeats a given character several times
+imports.ui.Repeat = function(char, len)
+	result = ""
+	for i in range(0,len-1)
+		result = result + char
+	end for
+	return result
+end function
+
+// Changes a text into terminal-colored text data
+imports.ui.Color = function(color, text)
+	return "<color="+color+">"+text+"</color>"
+end function
+
+imports.ui.BOLD = "<b>"
+imports.ui.BOLD_END = "</b>"
+
+// Changes a text into terminal-bolded text data
+imports.ui.Bold = function(text)
+	return imports.ui.BOLD+text+imports.ui.BOLD_END
+end function
+
+"#archive utils.inc.src"
+// Only used for ONE method, choice left to the main script
+//"#import libs/dict.inc.src"
+//"#import libs/remote.inc.src"
+
+"#ifbuild"
+if not globals.hasIndex("imports") then imports = {}
+imports.temp = {}
+"#endif"
+imports.utils = {}
+"#ifbuild"
+utils = imports.utils
+"#endif"
+
+imports.utils.DynLibPath = function(libName)
+	data = imports.utils._DynLibPath("/lib", libName)
+	if data then return data
+	data = imports.utils._DynLibPath(get_shell.host_computer.current_path, libName)
+	if data then return data
+	return imports.utils._DynLibPath(imports.utils.ParentPath(), libName)
+end function
+imports.utils._DynLibPath = function(path, libName)
+	path = path + "/" + libName
+	lib = include_lib(path)
+	if not lib then return null
+	return [lib, path]
+end function
+
+imports.utils.IncludeLib = function(libName, mandatory=true, shellCall=false)
+	// Support for simpler scripts not intended for autodownloading
+	legacyMode = (shellCall == false)
+	if legacyMode then shellCall = null
+	result = imports.utils._IncludeLib(libName, mandatory, shellCall)
+	if legacyMode then return result[0]
+	return result
+end function
+imports.utils._IncludeLib = function(libName, mandatory, shellCall)
+	lib = imports.utils.DynLibPath(libName)[0]
+	NULL = null
+	if lib or not mandatory then return [lib, NULL]
+	MSG = "Can't find " + libName + " library in the /lib path or the current folder"
+	if @shellCall == null then exit("Error: "+MSG)
+	print("Launching auto-download : "+MSG)
+	remote = imports.utils.IsImported("remote")
+	if not remote then exit("Unable to auto-download, remote.inc library is missing!")
+	libFile = remote.download(shellCall(), "/server/install/lib/"+libName, imports.utils.ParentPath(), "")
+	path = libFile.path
+	lib = include_lib(path)
+	return [lib, path]
+end function
+
+imports.utils.ParentPath = function()
+	return get_shell.host_computer.File(program_path).parent.path
+end function
+
+imports.utils.GetProgName = function()
+	return program_path.split("/")[-1]
+end function
+imports.utils.IsRoot = function()
+	return active_user == "root"
+end function
+
+imports.utils.Print = function(reason)
+	print(imports.utils.GetProgName()+": "+reason)
+end function
+imports.utils.Debug = function(reason)
+	// print(imports.utils.GetProgName()+" [DEBUG] : "+reason)
+end function
+imports.utils.Exit = function(reason)
+	exit(imports.utils.GetProgName()+": "+reason)
+end function
+
+imports.utils.ParamLength = function(params)
+	length = 0
+	for param in params
+		if param[0] != "-" then length = length + 1
+	end for
+	return length
+end function
+imports.utils.GetParam = function(params, index)
+	i = 0
+	for param in params
+		if param[0] == "-" then continue
+		if (i == index) then return param
+		i = i+1
+	end for
+	return null
+end function
+imports.utils.HasFlag = function(params, short, long)
+	for param in params
+		if param[0:1] == "--" then
+			if slice(param,2) == long then return true
+		else if param[0] == "-" then
+			if slice(param,1) == short then return true
+		end if
+	end for
+	return false
+end function
+
+imports.utils.SafeMapAccess = function(map, index, defaultValue=null)
+	if index == null then
+		imports.utils.Print("WARNING: Called SafeMapAccess with a null index")
+		return defaultValue
+	end if
+	if not map.hasIndex(index) then return defaultValue
+	return map[index]
+end function
+imports.utils.FirstMapWrite = function(map, index, value)
+	if index == null then imports.utils.Exit("ERROR: Tried to call FirstMapWrite with a null index")
+	if not map.hasIndex(index) then map[index] = value
+	return map[index]
+end function
+imports.utils.PrintList = function(list, separator=imports.file.NEW_LINE)
+	result = ""
+	nextLine = false
+	for item in list
+		if nextLine then result = result + separator else nextLine = true
+		result = result + item
+	end for
+	return result
+end function
+
+imports.utils.Pause = function()
+	user_input("Press ENTER to continue")
+end function
+imports.utils.UserPrompt = function(question)
+	while true
+		answer = lower(user_input(question + " (y/n)"));
+		if answer == "y" then
+			return true
+		else if answer == "n" then
+			return false
+		else
+			print("Only 'y' and 'n' are allowed")
+		end if
+	end while
+end function
+imports.utils.Choice = function(choices, reason=null)
+	len = choices.len
+	if len < 2 then return len-1
+	if reason != null then print("Please choose "+reason)
+	while true
+		for index in range(0,len-1)
+			print(index+1 + ":" + choices[index])
+		end for
+		input = user_input("Enter the number to select then press ENTER: ")
+		input = input.trim().to_int - 1
+		if input >= 0 and input < len then return input
+		print("Not a recognized selection")
+	end while
+	return -1 // Should never happen
+end function
+
+imports.utils.HASH_SIZE = 32
+imports.utils.GetSafePassword = function(userPass, cryptools, prompt=true)	
+	if userPass.indexOf(":") != null then
+		data = userPass.split(":")
+		username = data[0]
+		password = data[1]
+	else
+		username = ""
+		password = userPass		
+	end if
+	
+	if cryptools and password.len == imports.utils.HASH_SIZE then password = imports.utils._decipher(username, password, cryptools, prompt)
+	
+	return [username,password]
+end function
+imports.utils._decipher = function(user, hash, cryptools, prompt=true)
+	dict = imports.utils.IsImported("dict")
+	
+	if dict then
+		password = dict.decrypt(hash, user, null) // No cryptool
+		if not imports.utils.is_empty(password) then return password
+	end if
+	
+	if imports.utils.is_empty(user) then return hash
+	
+	print("Decipher required for "+hash)
+	if prompt and not imports.utils.UserPrompt("Do you want to decipher the password of "+user+"?") then
+		print("Cancelled deciphering of "+user+":"+hash)
+		return ""
+	end if
+	
+	password = cryptools.decipher(user, hash)
+	if dict and not imports.utils.is_empty(password) then dict.addPass(hash, password)
+	return password
+end function
+imports.utils.IsImported = function(name)
+	if not globals.hasIndex("imports") then return null
+	if not imports.hasIndex(name) then return null
+	return imports[name]
+end function
+
+imports.utils.char_count = function(str, char)
+	if str == null then return 0
+	count = 0
+	for car in str
+		if car == char then count = count + 1
+	end for
+	return count
+end function
+imports.utils.starts_with = function(str, part)
+	if str == null then return false
+	if part == null then return false
+	if str.len < part.len then return false
+	return str[:part.len] == part
+end function
+imports.utils.is_empty = function(str)
+	if str == null then return true
+	return str.len == 0
+end function
+imports.utils.slice = function(str,start,stop)
+	if start == stop then return ""
+	return str[start:stop]
+end function
+imports.utils.leftSlice = function(str,stop)
+	return imports.utils.slice(str, 0, stop)
+end function
+imports.utils.rightSlice = function(str,start)
+	return imports.utils.slice(str, start, str.len)
+end function
+
+imports.utils.SelfDestruct = function(prompt=true)
+    if prompt and not imports.utils.UserPrompt("Do you wish to delete the binary file from this program?") then return
+    get_shell.host_computer.File(program_path).delete
+end function
+
+"#archive versioning.inc.src"
+"#import libs/utils.inc.src"
+
+"#ifbuild"
+if not globals.hasIndex("imports") then imports = {}
+imports.temp = {}
+"#endif"
+imports.versioning = {}
+"#ifbuild"
+versioning = imports.versioning
+"#endif"
+
+// Constants returned when comparing
+imports.versioning.HIGHER = 1
+imports.versioning.EQUALS = 0
+imports.versioning.LOWER = -1
+
+// Make sure a number is an int
+imports.versioning.to_number = function(number)
+	if typeof(number) == "number" then return number
+	number = number.to_int
+	if typeof(number) == "string" then return null
+	return number
+end function
+
+// Convert a version string into a number array
+imports.versioning.read = function(version)
+	if typeof(version) == "number" then
+		data = [version]
+	else
+		data = version.split(".")
+	end if
+	
+	len = data.len
+	if len == 0 then return null
+	
+	deleting = true
+	for i in range(len-1, 0)
+		item = imports.versioning.to_number(data[i])
+		if item == null or item < 0 then 
+			imports.utils.Print(version+" is not a valid version format")
+			return null
+		end if
+		// Don't count trailing zeroes
+		if deleting and item == 0 and i > 0 then
+			data.remove(i)
+			continue
+		end if
+		deleting = false
+		data[i] = item
+	end for
+	
+	return data
+end function
+
+// Checks which of two version strings is the latest
+imports.versioning.compare = function(verA, verB)
+	verA = imports.versioning.read(verA)
+	verB = imports.versioning.read(verB)
+	if not verA and not verB then return imports.versioning.EQUALS
+	if not verB then return imports.versioning.HIGHER
+	if not verA then return imports.versioning.LOWER
+	
+	// By default, assume A being the longer version number (we'll handle exact equality later)
+	result = 1
+	// If A is inferior, reverts the planned "A is higher" result
+	if verA.len < verB.len then
+		result = -1
+		// Permute the two arrays
+		temp = verA
+		verA = verB
+		verB = temp
+	end if
+	// From now on verA is the longest (or equal sized...)
+
+	for i in range(0, verB.len-1)
+		if verA[i] == verB[i] then continue
+		// If A is inferior, revert the planned result
+		if verA[i] < verB[i] then result = 0 - result
+		return result
+	end for
+		
+	// In case of draw, the longuest is the greater
+	if verA.len == verB.len then result = 0
+	return result
+end function
+
+"#archive models/jsonparser.gs"
+// JSON (JavaScript Object Notation) is a common format for exchanging data
+// between different computers or programs.  See: https://json.org/
+// The data types in JSON are number, string, object, and array, which
+// correspond very neatly to MiniScript's number, string, map, and list.
+//
+// This module provides code to read and write data in JSON format, as well
+// as a couple of utility functions for escaping/unescaping strings and
+// converting numbers to/from hexadecimal.
+
+// Changes by Tom de QTG to adapt the parser to GreyScript
+
+// parse: convert a JSON string into a MiniScript value (which could
+//	include a list or map of other values).  This is the main entry point
+//	for reading JSON data and converting it to native form.
+//	Example: parse("42")		// returns 42
+parse = function(jsonString)
+	p = new Parser
+	return p.parse(jsonString)
+end function
+
+// toJSON: convert a MiniScript value to its JSON representation.
+//	Example: toJSON(42)			// returns "42"
+//
+// Parameters:
+//	value: MiniScript value to convert
+//	compact: if true, omit all unnecessary whitespace
+//	indent: amount to indent if multiple lines are needed
+toJSON = function(value, compact=false, indent=0)
+	if typeof(@value) == "function" then return """<function>"""
+	if value == null then return "null"
+	if typeof(value) == "number" then return str(value)
+	if typeof(value) == "string" then return """" + escape(value) + """"
+	if typeof(value) == "list" then return _listToJSON(value, compact, indent)
+	if typeof(value) == "map" then return _mapToJSON(value, compact, indent)
+	//To Implem
+	if typeof(value) == "router" then return _routerToJSON(value, compact, indent)
+	if typeof(value) == "computer" then return _computerToJSON(value, compact, indent)
+	if typeof(value) == "port" then return _portToJSON(value, compact, indent)
+	if typeof(value) == "file" then return _fileToJSON(value, compact, indent)
+end function
+
+// hexToInt: convert a string containing a hexadecimal number.
+//	Supports both uppercase and lowercase for digits A-F.
+//	Example: hexToInt("002A") 	// returns 42
+hexToInt = function(s)
+	result = 0
+	for c in s
+		result = result * 16 + _hexDigitMap[c]
+	end for
+	return result
+end function
+
+// escape: find certain characters (tab, newline, etc.) in the given string,
+// and represent them with backslash sequences.
+//	Example: escape(char(c))	// returns "\t"
+escape = function(s)
+	for entry in {"\":"\\", """":"\""", char(8):"\b", char(9):"\t", char(10):"\"+"n", char(12):"\f", char(13):"\r"}
+		s = s.replace(entry.key, entry.value)
+	end for
+	return s
+end function
+
+// unescape: replace backslash sequences in the given string.
+//	Example: unescape("\t")		// returns char(9)
+unescape = function(s)
+	result = []
+	i = 0
+	maxi = s.len
+	while i < maxi
+		di = 1
+		if s[i] == "\" then
+			di = 2
+			c = s[i+1]
+			if c == "b" then
+				result.push(char(8))
+			else if c == "t" then
+				result.push(char(9))
+			else if c == "n" then
+				result.push(char(10))
+			else if c == "f" then
+				result.push(char(12))
+			else if c == "r" then
+				result.push(char(13))
+			else if c == "u" then
+				// Unicode code point (must always be 4 digits)
+				hex = s[i+2:i+6]
+				result.push(char(hexToInt(hex)))
+				di = 6
+			else
+				result.push(c)
+			end if
+		else
+			result.push(s[i])
+		end if
+		i = i + di
+	end while
+	return result.join("")
+end function
+
+//----------------------------------------------------------------------
+// Stuff below is internal implementation;
+// most users don't need to poke around here.
+
+// Parsing JSON
+
+Parser = {}
+Parser.source = ""
+Parser._sourceLen = 0
+Parser._p = 0		// index of next character to consume in source
+
+Parser.init = function(source)
+	self.source = source
+	self._sourceLen = source.len
+end function
+
+Parser.parse = function(source=null)
+	if source != null then
+		self.init(source)
+	end if
+	self._p = 0
+	return self._parseElement
+end function
+
+whitespace = " " + char(9) + char(10) + char(13)
+Parser._skipWhitespace = function()
+	while self._p < self._sourceLen
+		c = self.source[self._p]
+		if whitespace.indexOf(c) == null then break
+		self._p = self._p + 1
+	end while
+end function
+
+Parser._parseElement = function()
+	return self._parseValue	// for now!
+end function
+
+Parser._parseValue = function()
+	self._skipWhitespace
+	c = self.source[self._p]
+	if c == """" then return self._parseString
+	if "0123456789-.".indexOf(c) != null then return self._parseNumber
+	if c == "[" then return self._parseList
+	if c == "{" then return self._parseMap
+	if c == "t" and self.source[self._p:self._p+4] == "true" then
+		self._p = self._p + 4
+		return true
+	end if
+	if c == "f" and self.source[self._p:self._p+5] == "false" then
+		self._p = self._p + 5
+		return false
+	end if
+	if c == "n" and self.source[self._p:self._p+4] == "null" then
+		self._p = self._p + 4
+		return null
+	end if
+end function
+
+Parser._parseList = function()
+	self._p = self._p + 1		// skip "["
+	self._skipWhitespace
+	result = []
+	while self._p < self._sourceLen
+		c = self.source[self._p]
+		if c == "]" then break
+		result.push(self._parseElement)
+		self._skipWhitespace
+		// after an element, we should have either a comma or a ']'
+		c = self.source[self._p]
+		if c == "," then
+			self._p = self._p + 1
+			self._skipWhitespace
+		end if
+	end while
+	self._p = self._p + 1
+	return result
+end function
+
+Parser._parseMap = function()
+	self._p = self._p + 1		// skip "{"
+	self._skipWhitespace
+	result = {}
+	while self._p < self._sourceLen
+		// grab the key (must be a string)
+		c = self.source[self._p]
+		if c == "}" then
+			break
+		end if
+		if c != """" then
+			print("JSON error: object member key must be a string literal")	// ToDo: better error handling!
+			print("Error at position " + self._p + ": " + self.source[self._p-60 : self._p+60])
+			return null
+		end if
+		key = self._parseString
+		self._skipWhitespace
+
+		// next token must be a colon
+		if self.source[self._p] != ":" then
+			print("JSON error: colon expected")
+			print("Error at position " + self._p + ": " + self.source[self._p-60 : self._p+60])
+			return null
+		end if
+		self._p = self._p + 1
+		self._skipWhitespace
+
+		// grab the value (could be anything)
+		value = self._parseElement
+		result[key] = value
+		self._skipWhitespace
+
+		// after a a key/value pair, we should have either a comma or a '}'
+		c = self.source[self._p]
+		if c == "," then
+			self._p = self._p + 1
+			self._skipWhitespace
+		end if
+	end while
+	self._p = self._p + 1
+	return result
+end function
+
+// Get a string literal from the source.  Advance to the next
+// character after the closing quote.
+Parser._parseString = function()
+	self._p = self._p + 1
+	startPos = self._p
+	anyEscape = false
+	while self._p < self._sourceLen
+		c = self.source[self._p]
+		self._p = self._p + 1
+		if c == """" then break
+		if c == "\" then
+			anyEscape = true
+			self._p = self._p + 1
+		end if
+	end while
+	result = self.source[startPos : self._p-1]
+	if result == null then result = ""
+	if anyEscape then result = unescape(result)
+	return result
+end function
+
+// Get a numeric literal from the source.  Advance to the next
+// character after the number.
+Parser._parseNumber = function()
+	startPos = self._p
+	while self._p < self._sourceLen
+		c = self.source[self._p]
+		// Note that we are rather permissive here, consuming things like
+		// 1-2e5+4E7, which is not valid JSON.  But we're not trying to be
+		// a JSON validator; we're just trying to grok valid JSON as quickly
+		// as we can.
+		if "0123456789+-.eE".indexOf(c) == null then break
+		self._p = self._p + 1
+	end while
+	// In original source, operation was
+	//result = val(self.source[startPos : self._p])
+	// However, it had the side-effect of using the "val" variable from the calling script!
+	result = self.source[startPos : self._p].val
+	return result
+end function
+
+// Generating JSON
+
+_listToJSON = function(lst, compact, indent)
+	ws = (_eol + "  "*(indent+1)) * (not compact)
+	parts = ["[", ws]
+	first = true
+	for item in lst
+		if not first then
+			parts.push(",")
+			parts.push(ws)
+		end if
+		parts.push(toJSON(item, compact, indent+1))
+		first = false
+	end for
+	if not compact then parts.push(_eol + "  " * indent)
+	parts.push("]")
+	return join(parts, "")
+end function
+
+_mapToJSON = function(lst, compact, indent)
+	ws = (_eol + "  "*(indent+1)) * (not compact)
+	parts = ["{", ws]
+	first = true
+	for kv in lst
+		if not first then
+			parts.push(",")
+			parts.push(ws)
+		end if
+		parts.push(toJSON(str(kv.key)))
+		parts.push(":")
+		if not compact then parts.push(" ")
+		parts.push(toJSON(@kv.value, compact, indent+1))
+		first = false
+	end for
+	if not compact then parts.push(_eol + "  " * indent)
+	parts.push("}")
+	return join(parts, "")
+end function
+
+_routerToJSON = function(router, compact, indent)
+	return "Not implemented yet."
+end function
+
+_computerToJSON = function(computer, compact, indent)
+	return "Not implemented yet."
+end function
+
+_portToJSON = function(port, compact, indent)
+	return "Not implemented yet."
+end function
+
+_fileToJSON = function(file, compact, indent)
+	return "Not implemented yet."
+end function
+
+// General utility data structures
+
+_eol = char(10)
+_hexDigitMap = {}
+for i in range(0,15)
+	if i < 10 then
+		_hexDigitMap[str(i)] = i
+	else
+		_hexDigitMap[char(55+i)] = i	// (lowercase hex digit)
+		_hexDigitMap[char(87+i)] = i	// (uppercase hex digit)
+	end if
+end for
+
+"#UnitTests (run when you load & run this script directly)."
+if locals == globals then
+
+	errorCount = 0
+	assertEqual = function(actual, expected)
+		if actual != expected then
+			print("Unit test failure: expected " + expected + ", got " + actual)
+			outer.errorCount = errorCount + 1
+		end if
+	end function
+	assertEqualEither = function(actual, expected, expected2)
+		if actual != expected and actual != expected2 then
+			print("Unit test failure: expected " + expected + ", got " + actual)
+			outer.errorCount = errorCount + 1
+		end if
+	end function
+
+	p = new Parser
+	p.init("   true ")
+	assertEqual(p.parse, "true")
+
+	assertEqual(parse("-123.45"), -123.45)
+	assertEqual(parse(char(13) + "42"), 42)
+	assertEqual(parse(".5"), 0.5)
+
+	assertEqual(parse("""\tHello, \""Bob\""."""), char(9) + "Hello, ""Bob"".")
+	assertEqual(parse("""\u002F"""), "/")
+
+	assertEqual(parse("[1, 2 , 3]"), [1, 2, 3])
+	assertEqual(parse("[ ""hey"", true, [0]]"), ["hey", true, [0]])
+
+	assertEqual(parse("{""hey"": ""ho"", ""9"" : 81}"), {"hey":"ho", "9":81})
+
+	// And here's a longer example... remember, quotes doubled only
+	// to make them valid MiniScript string literals...
+	data = parse("{""widget"": {" +	"    ""debug"": ""on""," +	"    ""window"": {" +	"        ""title"": ""Sample Konfabulator Widget""," +	"        ""name"": ""main_window""," +	"        ""visible"": false," +	"        ""width"": 500," +	"        ""height"": 300," +	"        ""left"": -123.456," +	"        ""top"": 234.567," +	"    }," +	"    ""image"": { " +	"        ""src"": ""Images\\Sun.png""," +	"        ""name"": ""sun1""," +	"        ""hOffset"": 250," +	"        ""vOffset"": 250," +	"        ""alignment"": ""center""" +	"    }," +	"    ""text"": {" +	"        ""data"": ""Click Here""," +	"        ""size"": 36," +	"        ""style"": ""bold""," +	"        ""name"": ""text1""," +	"        ""hOffset"": 250," +	"        ""vOffset"": 100," +	"        ""alignment"": ""center""," +	"        ""onMouseUp"": ""sun1.opacity = (sun1.opacity / 100) * 90;""" +	"    }}}")
+	assertEqual(data.widget.debug, "on")
+	assertEqual(data.widget.window.width, 500)
+	assertEqual(data.widget.image.src, "Images\Sun.png")
+	assertEqual(data.widget.text.size, 36)
+
+	ObjectTest = {}
+	ObjectTest.paramNumber = 42
+	ObjectTest.paramString = "Potatoe"
+	ObjectTest.paramList = [1, 2, 3]
+	ObjectTest.paramMap = {"one":1, "two":2}
+	ObjectTest.function1 = function ()
+		return "I am a function"
+	end function
+	assertEqual(toJSON(42), "42")
+	assertEqual(toJSON(char(9)), """\t""")
+	assertEqual(toJSON([1, 2, 3], true), "[1,2,3]")
+	assertEqual(toJSON(ObjectTest, true), "{""paramNumber"":42,""paramString"":""Potatoe"",""paramList"":[1,2,3],""paramMap"":{""one"":1,""two"":2},""function1"":""<function>""}")
+	// Maps are a bit tricky to unit-test, since the order in which the keys appear
+	// is undefined.  But here we go:
+	assertEqualEither(toJSON({"one":1, "two":2}, true),	"{""one"":1,""two"":2}", "{""two"":2,""one"":1}")
+
+	if errorCount == 0 then
+		print("All tests passed.  Huzzah!")
+	else
+		print(errorCount + " error" + "s" * (errorCount!=1) + " found.")
+	end if
+
+end if
+
+"#archive models/utils.gs"
+// Check mx
+metaxploit = include_lib("/lib/metaxploit.so")
+if not metaxploit then
+	currentPath = get_shell.host_computer.current_path
+	metaxploit = include_lib(currentPath + "/metaxploit.so")
+end if
+if not metaxploit then exit("Error: Can't find metaxploit library in the /lib path or the current folder")
+
+crypto = include_lib("/lib/crypto.so")
+if not crypto then
+	currentPath = get_shell.host_computer.current_path
+	crypto = include_lib(currentPath + "/crypto.so")
+end if
+if not crypto then exit("Error: Can't find crypto.so library in the /lib path or the current folder")
+
+// Get file on the local computer
+// string filePath
+// string fileName
+// @return File|null
+getFile = function(filePath, fileName)
+	myshell = get_shell
+	file = myshell.host_computer.File(filePath + fileName)
+	return file
+end function
+
+decipherUserPassword = function(userPass)
+	if userPass.len != 2 then exit("decipher: wrong syntax")
+	password = crypto.decipher(userPass[0], userPass[1])
+	return password
+end function
+
+// Get distant lib
+// string address
+// Port port
+// @return metaLib|null
+getLib = function(address, port)
+	res = null
+	net_session = metaxploit.net_use( address, port.port_number )
+	if net_session then
+		res = net_session.dump_lib
+	end if
+	return res
+end function
+
+getRouter = function(address)
+	res = null
+	net_session = metaxploit.net_use(address)
+	if net_session then
+		res = net_session.dump_lib
+	end if
+	return res
+end function
+
+"#UnitTests"
+myshell = get_shell
+libDirectory = myshell.host_computer.File("/lib/")
+
+for file in libDirectory.get_files
+	lib = metaxploit.load(file.path)
+	print (format_columns(lib.lib_name + " " + lib.version))
+end for
+
+"#archive models/vuln.gs"
+//Defining classes
+//Vulnerability
+Vulnerability = {}
+// Attributes
+Vulnerability.portNumber = ""
+Vulnerability.portStatus = ""
+Vulnerability.portProtocol = ""
+Vulnerability.libName = ""
+Vulnerability.libVersion = ""
+Vulnerability.LanIpAddress = ""
+Vulnerability.ExternalIpAddress = ""
+Vulnerability.vulnMemoryAddress = ""
+Vulnerability.vulnType = "" //Buffer overflow
+Vulnerability.vulnMethod = "" // copy string
+Vulnerability.vulnUnsecValue = ""
+Vulnerability.vulnOption = ""
+Vulnerability.vulnConditions = []
+// Methods
+Vulnerability._toString = function ()
+	res = self.LanIpAddress + ":" + self.portNumber + ":" + self.portProtocol + ":" + self.libVersion + ":" + self.vulnMemoryAddress + ":" + self.vulnType + ":" + self.vulnMethod + ":" + self.vulnUnsecValue + ":" + self.vulnOption
+	conditionIndex = 0
+	for cond in self.vulnConditions
+		conditionIndex = conditionIndex + 1
+		res = res + char(10) + "Condition "+ conditionIndex +": " + cond
+	end for
+	return res
+end function
+
+Vulnerability._getFilePath = function ()
+	return globals.vulnerabilitiesDirectoryName + self.ExternalIpAddress + "/"
+end function
+
+Vulnerability._toFile = function ()
+	myshell = get_shell
+
+	fileName = self.libName + "_" + self.libVersion.replace(".","_")
+	myshell.host_computer.touch(self._getFilePath, fileName)
+
+	vulnerabilityFile = myshell.host_computer.File(self._getFilePath + fileName)
+	vulnerabilityFile.set_content(vulnerabilityFile.content + char(10) + self._toString)
+	return vulnerabilityFile
+end function
+
+Vulnerability._fromFile = function ()
+	//ToDo
+end function
+
+Vulnerability._setExternalIpAddress = function(ipAddress)
+	self.vulnConditions = []
+	self.ExternalIpAddress = ipAddress
+	myshell = get_shell
+	myshell.host_computer.create_folder( globals.vulnerabilitiesDirectoryName, self.ExternalIpAddress)
+end function
+
+Vulnerability._setLibName = function(libName)
+	self.libName = libName
+end function
+
+Vulnerability._setPortInfos = function(port)
+	if (is_valid_ip(self.ExternalIpAddress) == false) then
+		exit("<color=#FF0000FF>External Ip Address should be set before port.</color>")
+	end if
+	isLanIp = is_lan_ip(self.ExternalIpAddress)
+	if (port != null and port != "router") then
+		self.portNumber = port.port_number
+		serviceInfos = globals.router.port_info(port).split(" ")
+		self.portProtocol = serviceInfos[0]
+		self.libVersion = serviceInfos[1]
+		self.LanIpAddress = port.get_lan_ip
+		self.portStatus = "open"
+		if(port.is_closed and not isLanIp) then
+			self.portStatus = "closed"
+		end if
+	else if (port == "router") then
+		routerLib = getRouter(self.ExternalIpAddress)
+		self.portNumber = 0
+		serviceInfos = routerLib.lib_name
+		self.portProtocol = "Kernel"
+		self.libVersion = routerLib.version
+		self.LanIpAddress = globals.router.local_ip
+		self.portStatus = "N/A"
+	end if
+end function
+
+Vulnerability._setVuln = function(memAddress, vulnText)
+	self.vulnMemoryAddress = memAddress
+	//print("Debug: text: " + vulnText)
+	vulnTextLines = vulnText.split("###")
+	//print("Debug: textafter split: " + vulnTextLines)
+	for line in vulnTextLines
+		if line.indexOf("Unsafe check") == 0 then
+			//print("Debug: lineToPush1: " + line)
+			self._parseVulnDesc(line)
+		else if line.indexOf("* ") == 0 then
+			lineToPush = line.remove("* ").remove(".")
+			//print("Debug: lineToPush2: " + lineToPush)
+			self.vulnConditions.push(lineToPush)
+		else
+			//print("unused line: " + line)
+		end if
+	end for
+end function
+
+//private
+Vulnerability._parseVulnDesc = function(vulnDesc)
+	vulnDesc = vulnDesc.remove("Unsafe check: ")
+	vulnDescArray = vulnDesc.split(". ")
+	self.vulnType = vulnDescArray[1].remove(".")
+	lastSpaceIndex = vulnDescArray[0].lastIndexOf(" ")
+	self.vulnMethod = vulnDescArray[0][0:lastSpaceIndex]
+	self.vulnUnsecValue = slice(vulnDescArray[0],lastSpaceIndex+1).remove("<b>").remove("</b>")
+end function
+
+//END classes definitions
diff --git a/scripts/archives/qtg.txt b/scripts/archives/qtg.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c0242a02bb17288dea10e75e1f1f668ed3f424bc
--- /dev/null
+++ b/scripts/archives/qtg.txt
@@ -0,0 +1,1125 @@
+"#archive preprocess.src"  
+if params.len < 1 or params[0] == "-h" or params[0] == "--help" then exit("<b>Usage: "+program_path.split("/")[-1]+" [file to preprocess]</b>")
+
+fileName = params[0]
+
+alreadyImportedFiles = []
+
+process = function(fileToPreProcess, preProcessedFile)
+	skip = false
+	fileLines = fileToPreProcess.content.split(char(10))
+	for line in fileLines
+		if line.indexOf("""#import") == 0 then
+			fileToImportName = line.split(" ")[1].remove("""")
+			if (alreadyImportedFiles.indexOf(fileToImportName) == null) then
+				alreadyImportedFiles.push(fileToImportName)
+				fileToImport = myshell.host_computer.File(fileToImportName)
+				importedFile = process(fileToImport, preProcessedFile)
+			else
+				print("Skipping " + fileToImportName + ": already imported")
+			end if
+		else if (line.indexOf("""#UnitTests") == 0) then
+			// break not working here "Compiler Error: 'else' skips expected 'break'" - bugged I think
+			skip = true
+		else
+			if (skip == false) then
+				preProcessedFile.set_content(preProcessedFile.content + "\n" + line)
+			end if
+		end if
+	end for
+	return preProcessedFile
+end function
+
+myshell = get_shell
+mycomputer = myshell.host_computer
+fileToPreProcess = mycomputer.File(fileName)
+preProcessedFileName = fileName.split(".")[0] + "_pp.src"
+
+touched = mycomputer.touch(mycomputer.current_path,preProcessedFileName)
+if touched != 1 then
+	if touched == "The file already exists" then
+		preProcessedFile = mycomputer.File(preProcessedFileName)
+		preProcessedFile.set_content("")
+	else
+		exit("failed to touch: " + touched)
+	end if
+else
+	preProcessedFile = mycomputer.File(preProcessedFileName)
+end if
+
+process(fileToPreProcess, preProcessedFile)
+
+buildRes = myshell.build(mycomputer.current_path + "/" + preProcessedFileName, mycomputer.current_path)
+//preProcessedFile.delete
+print("Executable ready: " + preProcessedFileName.remove(".src"))
+
+"#archive attack/autoexploit.gs"
+// @author: Tom de Qu'est-ce que tu GEEKes ?
+// This script takes a vulnerability json file as input.
+// Gives obtained objects / accesses gained trying to exploit it.
+
+"#import libs/models/vuln.gs"
+"#import libs/models/jsonparser.gs"
+"#import libs/models/utils.gs"
+
+globals.vulnerabilitiesDirectoryName = "/home/" + active_user + "/vulnerabilities/"
+
+if params.len < 1 or params[0] == "-h" or params[0] == "--help" then exit("<b>Usage: "+program_path.split("/")[-1]+"public_IP_address</b>")
+
+ipVulnDirectory = "/home/" + active_user + "/vulnerabilities/" + params[0] + "/"
+
+myshell = get_shell
+computer = myshell.host_computer
+vulnDirectory = computer.File(ipVulnDirectory)
+if not vulnDirectory then exit("Error:IP Vulnerabilities not listed")
+vulnFiles = vulnDirectory.get_files
+
+getLib = function (file)
+	if file.name.indexOf("kernel") > 0 then
+		net_session = metaxploit.net_use(vuln.ExternalIpAddress)
+	else
+		net_session = metaxploit.net_use(vuln.ExternalIpAddress, vuln.portNumber)
+	end if
+
+	if not net_session then exit("Error: can't connect to net session")
+
+	lib = net_session.dump_lib
+
+	return lib
+end function
+
+checkChoice = function(choice, max)
+	choice = choice.to_int
+	if typeof(choice) != "number" then exit("Should be a number. " + typeof(choice) + " provided.") end if
+	if choice >= max then exit ("Should be < " + max)
+	if choice < 0 then exit ("Should be > " + 0)
+	return choice
+end function
+
+libsToAttack = []
+count = 0
+for file in vulnFiles
+	if file.name.indexOf("json") == null then
+		libsToAttack.push(file.name.split(".so")[0])
+		print(count + ". " + libsToAttack[count])
+		count = count + 1
+	end if
+end for
+
+choice = user_input("Which one do you want to try? >", false)
+choice = checkChoice(choice, count)
+
+libToAttack = libsToAttack[choice]
+
+print("Attacking " + libToAttack + char(10))
+
+vulnsAvailable = []
+
+vulnCount = 0
+for file in vulnFiles
+	if file.name.indexOf(libToAttack) != 0 then
+		continue
+	end if
+
+	if file.name.indexOf("json") > 0 then
+		vuln = parse(file.content)
+		vulnsAvailable.push(vuln)
+		lib = getLib(file)
+		print(vulnCount + ". " + vulnsAvailable[vulnCount].vulnUnsecValue)
+		vulnCount = vulnCount + 1
+	end if
+end for
+
+choice = user_input("Which one do you want to try? >", false)
+choice = checkChoice(choice, vulnCount)
+targetVuln = vulnsAvailable[choice]
+
+print(char(10) + "Attacking " + targetVuln.libName +":" + targetVuln.libVersion + " at memory address: <b>" + targetVuln.vulnMemoryAddress + "</b> with value <b>" + targetVuln.vulnUnsecValue + "</b>")
+res = lib.overflow(targetVuln.vulnMemoryAddress, targetVuln.vulnUnsecValue)
+print("Object obtained: " + typeof(res))
+
+if (typeof(res) == "null") then
+	choice = user_input("Do you want to try with a payload ? Type the payload you want, 'no' otherwise. >", false)
+	if (choice == "no") then
+		exit("Try again with a different attack !")
+	else
+		res = lib.overflow(targetVuln.vulnMemoryAddress, targetVuln.vulnUnsecValue, choice)
+		print("Object obtained: " + typeof(res))
+	end if
+end if
+
+if (typeof(res) == "null") then
+	exit ("Attack failed !")
+end if
+
+// auto attacks !
+
+typeObject = typeof(res)
+if(typeObject == "computer") then
+	homeFolder = res.File("/home")
+	if not homeFolder then exit("Error: /home folder not found")
+	userFolders = homeFolder.get_folders
+	found = false
+	for userFolder in userFolders
+		bankFile = res.File("/home/" + userFolder.name + "/Config/Bank.txt")
+		if not bankFile then continue
+		if not bankFile.has_permission("r") then exit("Error: can't read file contents. Permission denied")
+		userPass = bankFile.content.split(":")
+		print("Deciphering bank password for user: " + userFolder.name)
+		password = decipherUserPassword(userPass)
+		if not password then
+			print("Nothing found...")
+		else
+			print("Bank account: " + userPass[0] +"\nBank Password: " + password)
+			found = true
+		end if
+
+		mailFile = res.File("/home/" + userFolder.name + "/Config/Mail.txt")
+		if not mailFile then continue
+		if not mailFile.has_permission("r") then exit("Error: can't read file contents. Permission deniend")
+		userPass = mailFile.content.split(":")
+		print("Deciphering mail password for user: " + userFolder.name)
+		password = decipherUserPassword(userPass)
+		if not password then
+			print("Nothing found...")
+		else
+			print("Mail account: " + userPass[0] +"\nMail Password: " + password)
+			found = true
+		end if
+
+	end for
+if not found then print("No files found")
+end if
+
+if(typeObject == "shell") then
+	res.start_terminal
+end if
+
+if(typeObject == "number") then
+	print("Number result obtained: " + res)
+end if
+
+if(typeObject == "file") then
+	if (res.is_folder) then
+		print("File owner: " + res.owner)
+		print(res.permissions + " " + res.name)
+		files = res.get_files
+		for file in files
+			print(" " + file.permissions + " " + file.name)
+		end for
+	else
+		print("File owner: " + res.owner)
+		print(res.permissions + " " + res.name)
+	end if
+end if
+
+"#archive attack/hackport.gs"
+"#import filereaper.src"
+
+// Data handling from Filereaper (pars is shorthand for "params")
+filereaper.UseShell = function(pars)
+	//state = pars[0]
+	shell = pars[1]
+	
+	// Hackport specific handling : global variable
+	if globals.shell != false then return
+	
+	globals.shell = shell
+end function
+
+filereaper.AddPasswd = function(pars)
+	//state = pars[0]
+	creds = pars[1]
+	
+	// Hackport specific handling : global variable
+	if globals.pass != false then return
+	
+	data = creds.split(":")
+	if data[0] != "root" then return
+	globals.pass = GetPassword(data)
+end function
+// Filereaper link ends here
+
+if params.len != 2 or params[0] == "-h" or params[0] == "--help" then exit("<b>Usage: "+program_path.split("/")[-1]+" [ip_address] [port]</b>")
+cryptools = include_lib("/lib/crypto.so")
+if not cryptools then exit("Error: Missing crypto library")
+metaxploit = include_lib("/lib/metaxploit.so")
+if not metaxploit then
+	currentPath = get_shell.host_computer.current_path
+	metaxploit = include_lib(currentPath + "/metaxploit.so")
+end if
+if not metaxploit then exit("Error: Can't find metaxploit library in the /lib path or the current folder")
+address = params[0]
+port = params[1].to_int
+net_session = metaxploit.net_use(address, port)
+if not net_session then exit("Error: can't connect to net session")
+metaLib = net_session.dump_lib
+
+GetPassword = function(userPass)
+	if userPass.len != 2 then exit("decipher: " + file.path + " wrong syntax")
+	print("retrieving password for user " +userPass[0] )
+	password = cryptools.decipher(userPass[0], userPass[1])
+	return password
+end function
+
+state = null // No state object in hackport
+shell = false
+pass = false
+
+print("scanning lib")
+memories = metaxploit.scan(metaLib)
+for mem_addr in memories
+	print("scanning adress " + mem_addr)
+	vuln_strings = metaxploit.scan_address(metaLib, mem_addr)
+	if vuln_strings then
+		vuln_string = vuln_strings.split("Unsafe check: ")
+		for str in vuln_string
+			if str == vuln_string[0] then continue
+			if pass and shell then continue
+			string = str[str.indexOf("<b>")+3:str.indexOf("</b>")]
+			result = metaLib.overflow(mem_addr, string)
+			if not result then
+				print("Error: attack didn't work")
+				continue
+			end if
+			type = typeof(result)
+			if type == "shell" then
+			    filereaper.exploitShell([state, result])
+			    //shell = result
+			else if type == "computer" then
+			    filereaper.exploitComp([state, result])
+			else if type == "file" then
+			    filereaper.exploitFile([state], result)
+			end if
+			
+			// Handled by FileReaper
+			// file = vuln_computer.File("/etc/passwd")
+			// if file == null or not file.has_permission("r") then 
+			    // reason = "failed. Can't access to file"
+			    // if file != null then reason = reason + " contents. Permission denied"
+				// print(reason)
+			// else
+				// print("got passwd file")
+				// pass_content = file.content.split("\"+"n")
+				// pass = GetPassword(pass_content[0].split(":"))
+			// end if
+		end for
+	else
+		print("failed to get vulnerabilities")
+	end if
+end for
+
+if pass == false then
+    print("root pass not found")
+else
+    print("root pass is: "+ pass)
+end if
+if shell == false then
+    print("shell not found")
+else
+    if pass == false and not imports.utils.UserPrompt("launch shell with no root pass?") then return
+    print("launching shell")
+    shell.start_terminal
+end if
+
+"#archive attack/univscan.gs"
+// @author: Tom de Qu'est-ce que tu GEEKes ?
+// This script takes an address as input and scans all ports available to find vulnerabilities
+// Collect inputs and handling options
+if params.len < 1 or params[0] == "-h" or params[0] == "--help" then exit("<b>Usage: "+program_path.split("/")[-1]+" [ip_address]</b>")
+
+//print("<color=#f4ae34>QTG ORANGE</color>")
+//print("<color=#1a508d>QTG BLUE</color>")
+//print("<color=#182d53>QTG DARK BLUE</color>")
+
+print(char(10))
+print("UnivScan By:"+ char(10))
+"#import libs/qtglogo.inc.src"
+printLogo()
+//print("<color=#1a508d>      &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&     </color>")
+//print("<color=#1a508d> &%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% </color>")
+//print("<color=#1a508d>&%%%%                                                                       %%%%</color>")
+//print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
+//print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
+//print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
+//print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
+//print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
+//print("<color=#1a508d>&%%%&       <color=#f4ae34>,***,***,***,***,                            ***,***,***,***,*</color>  &%%%</color>")
+//print("<color=#1a508d>&%%%&       <color=#f4ae34>*</color>%%%%%%%%%%%%%%<color=#f4ae34>**</color>                            <color=#f4ae34>**</color>%%%%%%%%%%%%%<color=#f4ae34>**</color>  &%%%</color>")
+//print("<color=#1a508d>&%%%&  <color=#f4ae34>******</color>%%%%%%%%%%%%%%<color=#f4ae34>*****, **************,** *******</color>%%%%%%%%%%%%%<color=#f4ae34>**</color>  &%%%</color>")
+//print("<color=#1a508d>&%%%&  <color=#f4ae34>**</color>%%%%%<color=#f4ae34>*,         **</color>%%%%<color=#f4ae34>*, **</color>%%%%%%%%%%%%%<color=#f4ae34>** **</color>%%%%%<color=#f4ae34>**</color>               &%%%</color>")
+//print("<color=#1a508d>&%%%&  <color=#f4ae34>*,</color>%%%%%<color=#f4ae34>*,         **</color>%%%%<color=#f4ae34>*, ******,</color>%%%%<color=#f4ae34>***,** **</color>%%%%%<color=#f4ae34>****</color>%%%%<color=#f4ae34>**</color>       &%%%</color>")
+//print("<color=#1a508d>&%%%&  <color=#f4ae34>**</color>%%%%%<color=#f4ae34>*,******** **</color>%%%%<color=#f4ae34>*,      **</color>%%%%<color=#f4ae34>**     **</color>%%%%%<color=#f4ae34>****</color>%%%%<color=#f4ae34>*******</color>  &%%%</color>")
+//print("<color=#1a508d>&%%%&  <color=#f4ae34>**</color>%%%%%<color=#f4ae34>*,,*</color>%%%%<color=#f4ae34>** **</color>%%%%<color=#f4ae34>*,      **</color>%%%%<color=#f4ae34>**     **</color>%%%%%<color=#f4ae34>*****,**</color>%%%%%<color=#f4ae34>**</color>  &%%%</color>")
+//print("<color=#1a508d>&%%%&  <color=#f4ae34>**</color>%%%%%<color=#f4ae34>****</color>%%%%<color=#f4ae34>*****</color>%%%%<color=#f4ae34>*,      **</color>%%%%<color=#f4ae34>**     **</color>%%%%%<color=#f4ae34>*****,**</color>%%%%%<color=#f4ae34>**</color>  &%%%</color>")
+//print("<color=#1a508d>&%%%&  <color=#f4ae34>*,*,*,</color>*%%%%%%%%%%%%%<color=#f4ae34>*,*,*,      *,</color>%%%%<color=#f4ae34>*,     ,*,*,*,</color>%%%%%%%%%%%%%<color=#f4ae34>,*</color>  &%%%</color>")
+//print("<color=#1a508d>&%%%&       <color=#f4ae34>**</color>%%%%%%%%%%%%%<color=#f4ae34>*****,      **</color>%%%%<color=#f4ae34>**          **</color>%%%%%%%%%%%%%<color=#f4ae34>**</color>  &%%%</color>")
+//print("<color=#1a508d>&%%%&       <color=#f4ae34>****,**********</color>%%%%<color=#f4ae34>*,      ********          *******,*********</color>  &%%%</color>")
+//print("<color=#1a508d>&%%%&                    <color=#f4ae34>**####*,</color>                                           &%%%</color>")
+//print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
+//print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
+//print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
+//print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
+//print("<color=#1a508d>@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%</color>")
+//print("<color=#1a508d>   &%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&  </color>")
+//print("                                                                                ")
+//print("<color=#182d53>                              &&&&&&&&&&&&&&&&&&&&&                             </color>")
+//print("<color=#182d53>                              &&&&&&&&&&&&&&&&&&&&&                             </color>")
+//print("<color=#1a508d>                 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%                </color>")
+
+ipAddress = params[0]
+
+globals.vulnerabilitiesDirectoryName = "/home/" + active_user + "/vulnerabilities/"
+myshell = get_shell
+myshell.host_computer.create_folder( "/home/" + active_user, "vulnerabilities")
+
+"#import libs/models/jsonparser.gs"
+"#import libs/models/vuln.gs"
+"#import libs/models/utils.gs"
+
+//Defining functions
+
+// nmap
+// string ipAddress
+// @return Port[]
+nmap = function(ipAddress)
+	isLanIp = is_lan_ip( ipAddress )
+	if isLanIp then
+		globals.router = get_router;
+	else
+		globals.router = get_router( ipAddress )
+	end if
+	
+	if globals.router == null then exit("nmap: ip address not found")
+	
+	isRouterIp = globals.router.local_ip == ipAddress
+	ports = null
+	
+	if not isLanIp or isRouterIp then
+		ports = globals.router.used_ports
+	else
+		ports = globals.router.device_ports(ipAddress)
+	end if
+	
+	return ports
+end function
+// END functions definitions
+
+// finding vulnerabilities for each port
+ports = nmap(ipAddress)
+print(char(10) + "<color=#FF0000FF>SCANNING ALL PORTS: " + ipAddress + "</color>")
+vulnerabilitiesArray = []
+scannedLibs = []
+// little hack to scan the router with the other libs
+ports.push("router")
+for port in ports
+	if port == "router" then
+		lib = getRouter(ipAddress)
+	else
+		lib = getLib(ipAddress, port)
+	end if
+	libId = lib.lib_name + "_" + lib.version
+	
+	existingVulnerabilityFile = getFile(globals.vulnerabilitiesDirectoryName, libId)
+	if (existingVulnerabilityFile != null) then
+		print("Vulnerability file '" + libId + "' already existing, skipping")
+		continue
+	end if
+	
+	if (scannedLibs.indexOf(libId) != null) then
+		continue
+	else
+		scannedLibs.push(libId)
+	end if
+	print("Scanning lib " + libId)
+	scanResults = metaxploit.scan(lib)
+	// example with port 25, usefull to debug
+	//scanResults = ["0x19189A45", "0x1C021A4C", "0x5D601DE4"]
+	vulnerabilitiesMap = {}
+	for address in scanResults
+		vulnerabilitiesMap[address] = metaxploit.scan_address(lib, address)
+	end for
+	for vulnerabilityByAddr in vulnerabilitiesMap
+		addressForThoseVulnerabilitys = vulnerabilityByAddr.key
+		vulnerabilitiesLines = vulnerabilityByAddr.value.split("\"+"n")
+		vulnerabilityIndex = 0
+		vulnerabilitiesToParse = []
+		currentVulnerabilityLines = ""
+		for vulnerabilityLine in vulnerabilitiesLines
+			if vulnerabilityLine.indexOf("Unsafe check") == 0 then
+				if vulnerabilityIndex > 0 then
+					previousVulnerabilityLine = currentVulnerabilityLines
+					vulnerabilitiesToParse.push(previousVulnerabilityLine)
+				end if
+				vulnerabilityIndex = vulnerabilityIndex + 1
+				currentVulnerabilityLines = vulnerabilityLine + "###"
+			else if vulnerabilityLine.indexOf("* ") == 0 then
+				currentVulnerabilityLines = currentVulnerabilityLines + vulnerabilityLine + "###"
+			else
+				//print("unused line: " + line)
+			end if
+		end for// split vulnerabilities
+		vulnerabilitiesToParse.push(currentVulnerabilityLines)
+		//print("DEBUG To Parse: " + vulnerabilitiesToParse)
+		//print("DEBUG addr: " + addressForThoseVulnerabilitys)
+		for vulnerability in vulnerabilitiesToParse
+			vuln = new Vulnerability
+			vuln._setExternalIpAddress(ipAddress)
+			vuln._setLibName(lib.lib_name)
+			vuln._setPortInfos(port)
+			vuln._setVuln(addressForThoseVulnerabilitys, vulnerability)
+			vulnerabilitiesArray.push(vuln)
+		end for
+		
+	end for
+end for
+
+for vulnerability in vulnerabilitiesArray
+	vulnerabilityFile = vulnerability._toFile
+end for
+
+for vulnerability in vulnerabilitiesArray
+	myshell = get_shell
+	filePath = vulnerability._getFilePath
+	fileName = vulnerability.libName + "_" + vulnerability.libVersion + "_" + md5(vulnerability.vulnUnsecValue) + ".json"
+	myshell.host_computer.touch(filePath, fileName)
+	
+	vulnerabilityFile = myshell.host_computer.File(filePath + fileName)
+	vulnerabilityFile.set_content("")
+	vulnerabilityFile.set_content(vulnerabilityFile.content + char(10) + toJSON(vulnerability))
+end for
+
+print("Vulnerabilities files stored in: " + globals.vulnerabilitiesDirectoryName)
+
+"#archive tools/airbreaker.gs"
+"#import libs/utils.inc.src"
+
+// Automates as most as possible the wifi cracking process
+// Requirements : crypto.so
+
+imports.network = {}
+
+// Is it REALLY useful to call network_devices() for that one?
+imports.network.getNetDevices = function()
+	return ["wlan0"]
+end function
+
+imports.network.getWifi = function(device)
+	result = []
+	for network in get_shell.host_computer.wifi_networks(device)
+		data = network.split(" ")
+		data[1] = imports.network.airpower(data[1])
+		result.push(data)
+	end for
+	return result
+end function
+
+// Formula is provided by GHTools, sorry!
+imports.network.airpower = function(strength)
+	return floor(300000/(strength.remove("%").to_int)+1) //calculating the acks needed
+end function
+
+// command: airbreaker
+cryptools = imports.utils.IncludeLib("crypto.so")
+comp = get_shell.host_computer
+
+devices = imports.network.getNetDevices()
+if devices.len == 0 then imports.utils.Exit("No devices found")
+device = devices[imports.utils.Choice(devices)]
+
+if not cryptools.airmon("start", device) then imports.utils.Exit("Can't activate monitor mode on "+device)
+
+networks = imports.network.getWifi(device)
+
+choice = []
+for item in networks
+	choice.push(item[2])
+end for
+network = networks[imports.utils.Choice(choice, "a wifi network")]
+
+bssid = network[0]
+required = network[1]
+essid = network[2]
+
+if required < 0 then
+	print("Please enter the number of ACKs you want to generate")
+	required = user_prompt().to_int
+	if typeof(required) == "string" or required < 0 then exit("Error: invalid number")
+end if
+print("Generating <color=white>"+required+"</color> ACKs, please wait")
+cryptools.aireplay(bssid,essid,required)
+
+cryptools.airmon("stop", device)
+
+path = get_shell.host_computer.current_path+"/file.cap"
+wait(1)
+file = comp.File(path)
+if not file then imports.utils.Exit("Couldn't find file, expected "+path)
+
+password = cryptools.aircrack(path)
+if not comp.connect_wifi(device, bssid, essid, password) then imports.utils.Exit("Couldn't connect !?")
+
+print("Connected to " + essid + ", password : "+password)
+file.delete()
+
+"#archive tools/archive.src"
+"#import includes/utils.inc.src"
+"#import includes/file.inc.src"
+
+// Used by passing the path of the (txt) archive, then the path of all the files to add
+// More information at https://usine.solution-libre.fr/qtg/grey-hack/-/issues/6
+
+"#ifbuild"
+imports = {}
+
+// The methods here are "good enough" to work in those cases
+// For example, the ParamLength normally discards flag-style args
+// But I don't want to give you all my tricks... yet, at least ;)
+imports.utils = {}
+imports.utils.ParamLength = function(params)
+	if params == null then return 0
+	return params.len
+end function
+imports.utils.GetParam = function(params, index)
+	if index < 0 or index >= imports.utils.ParamLength(params) then return null
+	return params[index]
+end function
+imports.utils.HasFlag = function(params, short, long)
+	if imports.utils.ParamLength(params) == 0 then return null
+	param = params[0]
+	return param == "-"+short or param == "-"+long
+end function
+// Dynamically getting the name of the command is left as an exercise to the reader :)
+imports.utils.GetProgName = function()
+	return program_path.split("/")[-1]
+end function
+imports.utils.Print = function(reason)
+	print(imports.utils.GetProgName()+": "+reason)
+end function
+
+imports.file = {}
+imports.file.NEW_LINE = char(10)
+// Disclaimer : brand new method created for this script, may not handle special cases 
+imports.file.AbsolutePath = function(path)
+	if path[0] != "/" then path = get_shell.host_computer.current_path + "/" + path
+	return path
+end function
+// Should return [] for unavailable files (binary or no read perms)
+imports.file.ReadAllLines = function(file)
+	if typeof(file) == "string" then
+		file = get_shell.host_computer.File(file)
+	end if
+	if not imports.file.FileAccess(file,"r") then return []
+	if file.is_binary then return []
+	return file.content.split(imports.file.NEW_LINE)
+end function
+imports.file.FileAccess = function(file, perm)
+	if not file then return false
+	if file.has_permission(perm) then return true
+	imports.utils.Print("Error: can't use ('"+perm+"') contents of '"+file.path+"'")
+	return false
+end function
+imports.file.save = function(content, path, name)
+	data = name.split("/")
+	if data.len > 1 then
+		for index in range(0, data.len-2)
+			name = data[index]
+			comp.create_folder(path, name)
+			path = path +"/"+ name
+		end for
+	end if
+	name = data[data.len-1]
+	comp.touch(path, name)
+	comp.File(path+"/"+name).set_content(content)
+end function
+"#endif"
+
+// To avoid bruning our eyes with escaping
+QUOTE = """"
+
+// Metadatas are encoded as 'unused' strings
+// Extracted straight from my preprocessor as that's too specific for an include IMHO
+GetLastChar = function(str)
+	if not str then return null
+	if str.len < 1 then return null
+	return str[str.len-1]
+end function
+// Returns the text in the 'unused' string or NULL if not a standalone string
+StringInstruction = function(str)
+	if str == null then return null
+	str = str.trim()
+	if GetLastChar(str) != QUOTE then return null
+	if str[0] != QUOTE then return null
+	str = str[1:str.len-1]
+	if str.indexOf(QUOTE) != null then return null
+	str = str.trim()
+	if str[0] != "#" then return ""
+	return str[1:str.len]
+end function
+
+// To identify archived data
+TAG = "#archive "
+
+ArchiveName = function(str)
+	str = StringInstruction(str)
+	if str == null then return null
+	if str.len < TAG.len then return
+	if str[0:TAG.len-1] != TAG[1:] then return null
+	return str[TAG.len-1:]
+end function
+
+// command: archive
+length = imports.utils.ParamLength(params)
+if length == 0 or imports.utils.HasFlag(params, "h", "help") then exit("<b>Usage: "+imports.utils.GetProgName()+" [file to compress/decompress] [...file paths]</b>")
+comp = get_shell.host_computer
+
+mainPath = imports.file.AbsolutePath(imports.utils.GetParam(params, 0) + ".tar")
+mainFile = comp.File(mainPath)
+
+temp = mainPath.lastIndexOf("/")
+path = mainPath[:temp]
+name = mainPath[temp+1:]
+
+if mainFile == null then
+	imports.utils.Print("Combining files")
+	
+	// Creating the file
+	comp.touch(path, name)
+	mainFile = comp.File(mainPath)
+	
+	// Flag to avoid a new line on the first line
+	nextLine = false
+	content = ""
+	
+	// Read all files in order and add them
+	for i in range(1,length-1)
+		path = imports.file.AbsolutePath(imports.utils.GetParam(params, i))
+		// Get the name of the file
+		name = path[path.lastIndexOf("/")+1:]
+		
+		lines = imports.file.ReadAllLines(path)
+		// Sanity checking the file is left as an exercise to the reader :) 
+		if lines.len == 0 then
+			imports.utils.Print("Can't read file "+i+" ("+name+")")
+			continue
+		end if
+		
+		if nextLine then
+			content = content + imports.file.NEW_LINE
+		else
+			nextLine = true
+		end if
+		
+		// add "#archive filename.ext" then the file
+		content = content + QUOTE+TAG+name+QUOTE
+		for line in lines
+			content = content + imports.file.NEW_LINE + line
+		end for
+	end for
+	
+	mainFile.set_content(content)
+	print("Archive is now available")
+else
+	print("Unarchiving")
+	
+	// Using the archive name as a new folder
+	if temp > 1 then name = name[:name.lastIndexOf(".")]
+	comp.create_folder(path, name)
+	path = path + "/" + name
+	
+	lines = imports.file.ReadAllLines(mainPath)
+	if ArchiveName(lines[0]) == null then exit("Not a valid archive file")
+	
+	currentName = null
+	content = ""
+	nextLine = false
+	
+	for line in lines
+		temp = ArchiveName(line)
+		// New file
+		if temp != null then
+			if currentName != null then imports.file.save(content, path, currentName)
+			
+			currentName = temp
+			content = ""
+			nextLine = false
+			continue
+		end if
+		
+		if nextLine then
+			content = content + imports.file.NEW_LINE
+		else
+			nextLine = true
+		end if
+		
+		content = content + line
+		
+	end for
+	
+	imports.file.save(content, path, currentName)
+	print("Unarchiving completed")
+end if
+
+"#archive tools/breakhash.src"
+"#import libs/utils.inc.src"
+
+//command: breakhash
+length = imports.utils.ParamLength(params)
+if length < 2 or length > 4 or imports.utils.HasFlag(params, "h", "help") then exit("<b>Usage: "+imports.utils.GetProgName()+" [hash] [max length] [(opt) min length] [(opt) char set] --numerals --uppercase --lowercase")
+
+hash = imports.utils.GetParam(params, 0)
+
+maxLen = imports.utils.GetParam(params, 1).to_int
+minLen = imports.utils.GetParam(params, 2)
+if minLen then
+	minLen = minLen.to_int
+else
+	minLen = 0
+end if
+if maxLen < 0 or minLen < 0 then imports.utils.Exit("Lengths must not be negative!") 
+
+possibilities = imports.utils.GetParam(params, 3)
+if possibilities then
+	possibilities = possibilities.values
+else
+	possibilities = []
+end if
+
+ASCII = function(min, max)
+	for i in range(min, max)
+		possibilities.push(char(i))
+	end for
+end function
+
+if imports.utils.HasFlag(params, "l", "lowercase") then ASCII(97,122)
+if imports.utils.HasFlag(params, "u", "uppercase") then ASCII(65,90)
+if imports.utils.HasFlag(params, "n", "numerals") then ASCII(48,57)
+if possibilities.len == 0 then imports.utils.Exit("You must specify at least one set of characters!")
+
+Increment = function(arr, max, index)
+	value = arr[index]+1
+	if value < max then
+		arr[index] = value
+		return true
+	end if
+	if index == 0 then return false
+	arr[index] = 0
+	return Increment(arr, max, index-1)
+end function
+
+for length in range(minLen,maxLen)
+	indexes = []
+	for i in range(0, length-1)
+		indexes.push(0)
+	end for
+	
+	loop = true
+	while loop
+		try = ""
+		for index in indexes
+			try = try + possibilities[index]
+		end for
+		
+		if hash == md5(try) then imports.utils.Exit("Bruteforced:"+try)
+		loop = Increment(indexes, possibilities.len, indexes.len-1)
+	end while
+	print("Tried all combinations of "+length+" characters")
+	imports.utils.Pause()
+end for
+
+imports.utils.Exit("Not found under those parameters!")
+
+"#archive tools/colony.gs"
+"#import libs/utils.inc.src"
+"#import libs/file.inc.src"
+"#import libs/remote.inc.src"
+
+// Tool to send to rented servers the default binaries from computers
+// Note that the utility methods to transfer a file only accepts complete path
+// "connect" and "transfer" should be easy reusable for a code pulling tool (issue #7)
+
+//command: colony
+length = imports.utils.ParamLength(params)
+if length < 3 or length > 5 or imports.utils.HasFlag(params, "h", "help") then exit("<b>Usage: "+imports.utils.GetProgName()+" [user] [password] [ip] [(opt) port] [(opt) service]</b>")
+comp = get_shell.host_computer
+
+user = imports.utils.GetParam(params, 0)
+password = imports.utils.GetParam(params, 1)
+ip = imports.utils.GetParam(params, 2)
+port = imports.utils.GetParam(params, 3)
+service = imports.utils.GetParam(params, 4)
+
+remote = imports.remote.connect(get_shell, user, password, ip, port, service)
+if not remote then exit("Can't connect to remote server")
+for path in imports.file.MISSING
+	result = imports.remote.upload(remote, path, path[:path.lastIndexOf("/")])
+	if typeof(result) == "string" then print(result) else print("Sent to "+result.path)
+end for
+
+"#archive tools/decipher2.src"
+cryptools = include_lib("/lib/crypto.so")
+if not cryptools then exit("Error: Missing crypto library")
+
+GetPassword = function(userPass)
+	// First modification: remove the hardcoded reference to a file
+	if userPass.len != 2 then exit("decipher: wrong syntax")
+	password = cryptools.decipher(userPass[0], userPass[1])
+	return password
+end function
+
+if params.len != 1 or params[0] == "-h" or params[0] == "--help" then exit(command_info("decipher_usage"))
+
+origFile = params[0]
+file = get_shell.host_computer.File(origFile)
+// Second modification: treat a non-file parameter as a value to decrypt
+if not file then
+	if origFile.indexOf(":") == null then exit("decipher: can't find " + origFile)
+	lines = [origFile]
+else
+	if not file.has_permission("r") then exit("can't read file. Permission denied")
+	if file.content.len == 0 then exit("decipher: no users found")
+	lines = file.content.split("\n")
+end if
+
+// Third modification: instead of selecting one of the lines, decrypt them all
+for line in lines
+	userPass = line.split(":")
+	print("Selected user: " + userPass[0] + "\nDeciphering...")
+	password = GetPassword(userPass)	
+	
+	if not password then
+		print("Can't find password :(")
+	else
+		print("password found! => " + password)
+	end if
+end for
+
+"#archive tools/scanlib2.src"
+//command scanlib
+if params.len != 1 or params[0] == "-h" or params[0] == "--help" then exit(command_info("scanlib_usage"))
+metaxploit = include_lib("/lib/metaxploit.so")
+if not metaxploit then exit("Error: Missing metaxploit library")
+libFile = get_shell.host_computer.File(params[0])
+// First modification : remote scanning
+if not libFile then
+	metaLib = null
+	if params[0].indexOf(":") != null then
+		data = params[0].split(":")
+		metaLib = metaxploit.net_use(data[0], data[1].to_int).dump_lib
+	end if
+	if not metaLib then exit("can't find library: " + params[0])
+else
+	metaLib = metaxploit.load(libFile.path)
+end if
+print("Scanning memory address...")
+listMem = metaxploit.scan(metaLib)
+// Second modification : looping in order to reuse the results from the scan+exit on input
+while true
+	print("0: EXIT")
+	index = 1
+	for itemMem in listMem
+		print(index +": [" + itemMem + "]")
+		index = index + 1
+	end for
+	if listMem.len == 0 then exit("Scan completed: No issues detected.")
+
+	print("Scan completed: detected issues in " + listMem.len + " memory zones.")
+	option = ""
+	inputOk = false
+	while( not inputOk )
+		option = user_input("Select memory index: ").to_int
+		if typeof(option) != "number" or (option < 1 or option > listMem.len) then
+			// Third modification : exit on input
+			if option == 0 then exit()
+			print("Invalid input. Type a valid number")
+		else 
+			inputOk = true
+		end if
+	end while
+	print("Scanning for vulnerabilities at memory zone: " + listMem[option - 1])
+	print(metaxploit.scan_address(metaLib, listMem[option - 1]))
+end while
+
+"#archive tools/setup.gs"
+"#import libs/utils.inc.src"
+
+// Can create two limited users : ssh and ftp (when optional passwords are provided)
+// Make sure to have created the custom remote and members accounts before that
+// For the exact effects on permissions, see https://usine.solution-libre.fr/qtg/grey-hack/-/issues/9
+// Will clear the log file and all /Config/ folders as an additional security in case the server got misused before
+// !!! This program will self-delete after use !!!
+// Note : chmodding takes a bit of time for unknown reasons (and GH doesn't show prints while running)
+// Expect ONE MINUTE or so until the single print statement shows up
+	
+imports.temp = {}
+imports.file = {}
+	
+imports.temp.AddCmd = function(arr, files)
+	for file in files
+		arr.push("/bin/"+file)
+	end for
+end function
+imports.temp.AddSoft = function(arr, files)
+	for file in files
+		arr.push("/usr/bin/"+file+".exe")
+	end for
+end function
+
+// #2.6: If "destructive security" is desired, delete /etc/passwd and the services in /server/ (to prevent a shutdown)
+//imports.file.USELESS = ["/etc/passwd","/server"]
+imports.file.USELESS = []
+// #2.3: Nslookup, whois, Mail.exe aren't useful on a server, maaaybe on a dedicated proxy... delete it
+// #2.4: At the time of writing, there's no obvious benefit with not deleting CodeEditor.exe and build
+// #2.5: Are members allowed the use this server as a last-point proxy to reach a ftp? If not, delete the ftp command
+imports.temp.AddCmd(imports.file.USELESS,["build","whois","nslookup","ftp"])
+imports.temp.AddSoft(imports.file.USELESS,["CodeEditor", "Mail"])
+
+// #1.3: Give execution rights to everyone for "/bin/clear"
+imports.file.SAFE = []
+imports.temp.AddCmd(imports.file.SAFE,["clear"])
+
+// #3.3: Set group for "/bin/ssh" as "ssh" and give it group-level execution rights
+imports.file.SSH = []
+imports.temp.AddCmd(imports.file.SSH,["ssh"])
+// Nothing.
+imports.file.FTP = []
+
+// #3.4: Set group "members" and give group-level execution rights for...
+imports.file.ALLOWED = []
+imports.temp.AddCmd(imports.file.ALLOWED,["cd","ls","pwd","cat","rm","mv","cp","mkdir","rmdir","touch"])
+imports.temp.AddSoft(imports.file.ALLOWED,["Terminal","FileExplorer","Notepad"])
+
+imports.temp = null
+
+//command: setup
+if not imports.utils.IsRoot() then imports.utils.Exit("Must be root.")
+
+length = imports.utils.ParamLength(params)
+if length > 2 or imports.utils.HasFlag(params, "h", "help") then exit("<b>Usage: "+imports.utils.GetProgName()+" [(opt) ssh password] [(opt) ftp password]</b>")
+comp = get_shell.host_computer
+
+sshPass = imports.utils.GetParam(params, 0)
+ftpPass = imports.utils.GetParam(params, 1)
+
+// Utility method : only delete the files, not the directories
+// (Can be used on a file too)
+WipeDir = function(path)
+	dir = comp.File(path)
+	if not dir then return
+	if path[path.len-1] == "/" then
+		for file in dir.get_files // NOT THE FOLDERS!!!!
+			file.delete
+		end for
+	else
+		dir.delete
+	end if
+end function
+// Utility method : delete all elements in the dir, then delete the dir itself
+FullWipe = function(file)
+	if not file then return
+	for child in file.get_files
+		child.delete()
+	end for
+	for child in file.get_folders
+		FullWipe(child)
+	end for
+	file.delete()
+end function
+
+// #0.1: users able to bounce through ssh
+SSH_GROUP = "ssh"
+// #0.2: users able to access the /Public/Downloads/ folder
+FTP_GROUP = "ftp"
+// #0.3: users connected to manipulate non-public files (subgroup)
+MAIN_GROUP = "home"
+// #0.4: users with a private space (inherits files group)
+USER_GROUP = "members"
+
+// #1.1 By default, removing all perms for everyone on everything
+main = comp.File("/")
+for mod in ["u-rwx", "g-rwx", "o-rwx"]
+	main.chmod(mod,1)
+end for
+// #1.2 Give read access to everyone in /
+main.chmod("o+r",0)
+
+NewLimitedUser = function(user, pass, grp)
+	if not comp.create_user(user, pass) then comp.change_password(user, pass)
+	comp.create_group(user, grp)
+
+	// #2.1: Delete the home folder of (...) + "ssh"/"ftp"-only users
+	dir = comp.File("/home/"+user+"/")
+	if not dir then return
+	dir.delete()
+	end if
+end function
+// #1.4 Create the groups and add them to users accordingly
+if sshPass then NewLimitedUser("proxy", sshPass, SSH_GROUP)
+if ftpPass then NewLimitedUser("files", ftpPass, FTP_GROUP)
+
+SetRead = function(path, group)
+	file = comp.File(path)
+	if not file then return
+	recur = 0
+	file.set_group(group,recur)
+	file.chmod("g+r",recur)
+end function
+// #3.1 Set group for /home/ as "files" and give it group-level read
+SetRead("/home/", MAIN_GROUP)
+// #1.5 Set group for /Public/ as "ftp" and give it group-level read
+SetRead("/Public/", FTP_GROUP)
+
+// #1.6 Set group for /Public/Downloads/ as "ftp" and give it (recursive) group-level read-write
+ftpDir = comp.File("/Public/Downloads/")
+if ftpDir then
+	ftpDir.set_group(FTP_GROUP,1)
+	ftpDir.chmod("g+rw", 1)
+end if
+
+users = []
+for user in comp.File("/home/").get_folders
+	if user.name == "guest" then
+		// #2.1: Delete the home folder of guest + (...) users
+		FullWipe(user)
+	else
+		// #2.2 Give (recursive) !user-level! read-write for child folders of /home/
+		users = users.push(user.path)
+		user.chmod("u+rw",1)
+	end if
+end for
+
+for path in imports.file.USELESS
+	WipeDir(path)
+end for
+	
+// #2.2: It would be a good idea to delete/reset the /Config/ folder for all users, just in case. (...)
+users = users.push("/root")
+for user in users
+	WipeDir(user+"/Config/")
+end for
+
+// The 15 following lines are applying the suggestions listed at the "imports.file." part
+for path in imports.file.SAFE
+	file = comp.File(path)
+	file.chmod("o+x",1)
+end for
+
+Safe = function(group, paths)
+	for path in paths
+		file = comp.File(path)
+		recur=1
+		file.chmod("g+x",recur)
+		file.set_group(group,recur)
+	end for
+end function
+Safe(USER_GROUP, imports.file.ALLOWED)
+Safe(SSH_GROUP, imports.file.SSH)
+Safe(FTP_GROUP, imports.file.FTP) // Normally no-op
+
+imports.utils.SelfDestruct()
+
+// #2.2: It would be a good idea to delete/reset (...), just in case. and the log file too.
+WipeDir("/var/system.log")
+comp.touch("/var/", "system.log")
+
+print("Setup is finished")
diff --git a/airbreaker.src b/scripts/attack/airbreaker.src
similarity index 62%
rename from airbreaker.src
rename to scripts/attack/airbreaker.src
index ac83e5d34cee06312bcb123348a8a432cf230454..0319f78f165c4514e5049ea780e9c9723addf003 100644
--- a/airbreaker.src
+++ b/scripts/attack/airbreaker.src
@@ -1,46 +1,8 @@
-"#import utils.inc.src"
+"#import libs/utils.inc.src"
 
 // Automates as most as possible the wifi cracking process
 // Requirements : crypto.so
 
-"#ifbuild"
-if not globals.hasIndex("imports") then
-    imports = {}
-
-    imports.utils = {}
-
-    imports.utils.IncludeLib = function(libName, mandatory)
-	    lib = include_lib("/lib/" + libName)
-	    if not lib then
-		    currentPath = get_shell.host_computer.current_path
-		    lib = include_lib(currentPath + "/" + libName)
-	    end if
-	    if mandatory and not lib then exit("Error: Can't find " + libName + " library in the /lib path or the current folder")
-	    return lib
-    end function
-
-    imports.utils.Exit = function(reason)
-	    exit(reason)
-    end function
-
-    imports.utils.Choice = function(choices, reason)
-	    len = choices.len
-	    if len < 2 then return len-1
-	    if not reason then print("Please choose "+reason)
-	    while true
-		    for index in range(0,len-1)
-			    print(index+1 + ":" + choices[index])
-		    end for
-		    input = user_input("Enter the number to select then press ENTER: ")
-		    input = input.trim().to_int - 1
-		    if input >= 0 and input < len then return input
-		    print("Not a recognized selection")
-	    end while
-	    return -1 // Should never happen
-    end function
-end if
-"#endif"
-
 imports.network = {}
 
 // Is it REALLY useful to call network_devices() for that one?
@@ -96,6 +58,7 @@ cryptools.aireplay(bssid,essid,required)
 cryptools.airmon("stop", device)
 
 path = get_shell.host_computer.current_path+"/file.cap"
+wait(1)
 file = comp.File(path)
 if not file then imports.utils.Exit("Couldn't find file, expected "+path)
 
@@ -103,4 +66,4 @@ password = cryptools.aircrack(path)
 if not comp.connect_wifi(device, bssid, essid, password) then imports.utils.Exit("Couldn't connect !?")
 
 print("Connected to " + essid + ", password : "+password)
-file.delete()
\ No newline at end of file
+file.delete()
diff --git a/scripts/attack/autoexploit.gs b/scripts/attack/autoexploit.gs
index 889ccafc8bf3d85743b368874a24d2f09bfbb028..2881f77eb784a18921a94caf4741cc90afcecab6 100644
--- a/scripts/attack/autoexploit.gs
+++ b/scripts/attack/autoexploit.gs
@@ -2,9 +2,9 @@
 // This script takes a vulnerability json file as input.
 // Gives obtained objects / accesses gained trying to exploit it.
 
-"#import vuln.gs"
-"#import jsonparser.gs"
-"#import tools.gs"
+"#import libs/models/vuln.gs"
+"#import libs/models/jsonparser.gs"
+"#import libs/models/utils.gs"
 
 globals.vulnerabilitiesDirectoryName = "/home/" + active_user + "/vulnerabilities/"
 
diff --git a/scripts/attack/hackport.gs b/scripts/attack/hackport.gs
new file mode 100644
index 0000000000000000000000000000000000000000..b6eea45201d984fd175499e6f3c2c70cb627a150
--- /dev/null
+++ b/scripts/attack/hackport.gs
@@ -0,0 +1,107 @@
+"#import filereaper.src"
+
+// Data handling from Filereaper (pars is shorthand for "params")
+filereaper.UseShell = function(pars)
+	//state = pars[0]
+	shell = pars[1]
+	
+	// Hackport specific handling : global variable
+	if globals.shell != false then return
+	
+	globals.shell = shell
+end function
+
+filereaper.AddPasswd = function(pars)
+	//state = pars[0]
+	creds = pars[1]
+	
+	// Hackport specific handling : global variable
+	if globals.pass != false then return
+	
+	data = creds.split(":")
+	if data[0] != "root" then return
+	globals.pass = GetPassword(data)
+end function
+// Filereaper link ends here
+
+if params.len != 2 or params[0] == "-h" or params[0] == "--help" then exit("<b>Usage: "+program_path.split("/")[-1]+" [ip_address] [port]</b>")
+cryptools = include_lib("/lib/crypto.so")
+if not cryptools then exit("Error: Missing crypto library")
+metaxploit = include_lib("/lib/metaxploit.so")
+if not metaxploit then
+	currentPath = get_shell.host_computer.current_path
+	metaxploit = include_lib(currentPath + "/metaxploit.so")
+end if
+if not metaxploit then exit("Error: Can't find metaxploit library in the /lib path or the current folder")
+address = params[0]
+port = params[1].to_int
+net_session = metaxploit.net_use(address, port)
+if not net_session then exit("Error: can't connect to net session")
+metaLib = net_session.dump_lib
+
+GetPassword = function(userPass)
+	if userPass.len != 2 then exit("decipher: " + file.path + " wrong syntax")
+	print("retrieving password for user " +userPass[0] )
+	password = cryptools.decipher(userPass[0], userPass[1])
+	return password
+end function
+
+state = null // No state object in hackport
+shell = false
+pass = false
+
+print("scanning lib")
+memories = metaxploit.scan(metaLib)
+for mem_addr in memories
+	print("scanning adress " + mem_addr)
+	vuln_strings = metaxploit.scan_address(metaLib, mem_addr)
+	if vuln_strings then
+		vuln_string = vuln_strings.split("Unsafe check: ")
+		for str in vuln_string
+			if str == vuln_string[0] then continue
+			if pass and shell then continue
+			string = str[str.indexOf("<b>")+3:str.indexOf("</b>")]
+			result = metaLib.overflow(mem_addr, string)
+			if not result then
+				print("Error: attack didn't work")
+				continue
+			end if
+			type = typeof(result)
+			if type == "shell" then
+			    filereaper.exploitShell([state, result])
+			    //shell = result
+			else if type == "computer" then
+			    filereaper.exploitComp([state, result])
+			else if type == "file" then
+			    filereaper.exploitFile([state], result)
+			end if
+			
+			// Handled by FileReaper
+			// file = vuln_computer.File("/etc/passwd")
+			// if file == null or not file.has_permission("r") then 
+			    // reason = "failed. Can't access to file"
+			    // if file != null then reason = reason + " contents. Permission denied"
+				// print(reason)
+			// else
+				// print("got passwd file")
+				// pass_content = file.content.split("\"+"n")
+				// pass = GetPassword(pass_content[0].split(":"))
+			// end if
+		end for
+	else
+		print("failed to get vulnerabilities")
+	end if
+end for
+
+if pass == false then
+    print("root pass not found")
+else
+    print("root pass is: "+ pass)
+end if
+if shell == false then
+    print("shell not found")
+else
+    if pass == false and not imports.utils.UserPrompt("launch shell with no root pass?") then return
+    print("launching shell")
+    shell.start_terminal
+end if
diff --git a/scripts/attack/univscan.gs b/scripts/attack/univscan.gs
index 7ba6e236cb374cb46c4adf5767bb5f71711431c4..fcb9f432f6bc4ce19ff43034668cf1be5a306517 100644
--- a/scripts/attack/univscan.gs
+++ b/scripts/attack/univscan.gs
@@ -9,36 +9,38 @@ if params.len < 1 or params[0] == "-h" or params[0] == "--help" then exit("<b>Us
 
 print(char(10))
 print("UnivScan By:"+ char(10))
-print("<color=#1a508d>      &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&     </color>")
-print("<color=#1a508d> &%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% </color>")
-print("<color=#1a508d>&%%%%                                                                       %%%%</color>")
-print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
-print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
-print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
-print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
-print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
-print("<color=#1a508d>&%%%&       <color=#f4ae34>,***,***,***,***,                            ***,***,***,***,*</color>  &%%%</color>")
-print("<color=#1a508d>&%%%&       <color=#f4ae34>*</color>%%%%%%%%%%%%%%<color=#f4ae34>**</color>                            <color=#f4ae34>**</color>%%%%%%%%%%%%%<color=#f4ae34>**</color>  &%%%</color>")
-print("<color=#1a508d>&%%%&  <color=#f4ae34>******</color>%%%%%%%%%%%%%%<color=#f4ae34>*****, **************,** *******</color>%%%%%%%%%%%%%<color=#f4ae34>**</color>  &%%%</color>")
-print("<color=#1a508d>&%%%&  <color=#f4ae34>**</color>%%%%%<color=#f4ae34>*,         **</color>%%%%<color=#f4ae34>*, **</color>%%%%%%%%%%%%%<color=#f4ae34>** **</color>%%%%%<color=#f4ae34>**</color>               &%%%</color>")
-print("<color=#1a508d>&%%%&  <color=#f4ae34>*,</color>%%%%%<color=#f4ae34>*,         **</color>%%%%<color=#f4ae34>*, ******,</color>%%%%<color=#f4ae34>***,** **</color>%%%%%<color=#f4ae34>****</color>%%%%<color=#f4ae34>**</color>       &%%%</color>")
-print("<color=#1a508d>&%%%&  <color=#f4ae34>**</color>%%%%%<color=#f4ae34>*,******** **</color>%%%%<color=#f4ae34>*,      **</color>%%%%<color=#f4ae34>**     **</color>%%%%%<color=#f4ae34>****</color>%%%%<color=#f4ae34>*******</color>  &%%%</color>")
-print("<color=#1a508d>&%%%&  <color=#f4ae34>**</color>%%%%%<color=#f4ae34>*,,*</color>%%%%<color=#f4ae34>** **</color>%%%%<color=#f4ae34>*,      **</color>%%%%<color=#f4ae34>**     **</color>%%%%%<color=#f4ae34>*****,**</color>%%%%%<color=#f4ae34>**</color>  &%%%</color>")
-print("<color=#1a508d>&%%%&  <color=#f4ae34>**</color>%%%%%<color=#f4ae34>****</color>%%%%<color=#f4ae34>*****</color>%%%%<color=#f4ae34>*,      **</color>%%%%<color=#f4ae34>**     **</color>%%%%%<color=#f4ae34>*****,**</color>%%%%%<color=#f4ae34>**</color>  &%%%</color>")
-print("<color=#1a508d>&%%%&  <color=#f4ae34>*,*,*,</color>*%%%%%%%%%%%%%<color=#f4ae34>*,*,*,      *,</color>%%%%<color=#f4ae34>*,     ,*,*,*,</color>%%%%%%%%%%%%%<color=#f4ae34>,*</color>  &%%%</color>")
-print("<color=#1a508d>&%%%&       <color=#f4ae34>**</color>%%%%%%%%%%%%%<color=#f4ae34>*****,      **</color>%%%%<color=#f4ae34>**          **</color>%%%%%%%%%%%%%<color=#f4ae34>**</color>  &%%%</color>")
-print("<color=#1a508d>&%%%&       <color=#f4ae34>****,**********</color>%%%%<color=#f4ae34>*,      ********          *******,*********</color>  &%%%</color>")
-print("<color=#1a508d>&%%%&                    <color=#f4ae34>**####*,</color>                                           &%%%</color>")
-print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
-print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
-print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
-print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
-print("<color=#1a508d>@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%</color>")
-print("<color=#1a508d>   &%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&  </color>")
-print("                                                                                ")
-print("<color=#182d53>                              &&&&&&&&&&&&&&&&&&&&&                             </color>")
-print("<color=#182d53>                              &&&&&&&&&&&&&&&&&&&&&                             </color>")
-print("<color=#1a508d>                 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%                </color>")
+"#import libs/qtglogo.inc.src"
+printLogo()
+//print("<color=#1a508d>      &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&     </color>")
+//print("<color=#1a508d> &%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% </color>")
+//print("<color=#1a508d>&%%%%                                                                       %%%%</color>")
+//print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
+//print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
+//print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
+//print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
+//print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
+//print("<color=#1a508d>&%%%&       <color=#f4ae34>,***,***,***,***,                            ***,***,***,***,*</color>  &%%%</color>")
+//print("<color=#1a508d>&%%%&       <color=#f4ae34>*</color>%%%%%%%%%%%%%%<color=#f4ae34>**</color>                            <color=#f4ae34>**</color>%%%%%%%%%%%%%<color=#f4ae34>**</color>  &%%%</color>")
+//print("<color=#1a508d>&%%%&  <color=#f4ae34>******</color>%%%%%%%%%%%%%%<color=#f4ae34>*****, **************,** *******</color>%%%%%%%%%%%%%<color=#f4ae34>**</color>  &%%%</color>")
+//print("<color=#1a508d>&%%%&  <color=#f4ae34>**</color>%%%%%<color=#f4ae34>*,         **</color>%%%%<color=#f4ae34>*, **</color>%%%%%%%%%%%%%<color=#f4ae34>** **</color>%%%%%<color=#f4ae34>**</color>               &%%%</color>")
+//print("<color=#1a508d>&%%%&  <color=#f4ae34>*,</color>%%%%%<color=#f4ae34>*,         **</color>%%%%<color=#f4ae34>*, ******,</color>%%%%<color=#f4ae34>***,** **</color>%%%%%<color=#f4ae34>****</color>%%%%<color=#f4ae34>**</color>       &%%%</color>")
+//print("<color=#1a508d>&%%%&  <color=#f4ae34>**</color>%%%%%<color=#f4ae34>*,******** **</color>%%%%<color=#f4ae34>*,      **</color>%%%%<color=#f4ae34>**     **</color>%%%%%<color=#f4ae34>****</color>%%%%<color=#f4ae34>*******</color>  &%%%</color>")
+//print("<color=#1a508d>&%%%&  <color=#f4ae34>**</color>%%%%%<color=#f4ae34>*,,*</color>%%%%<color=#f4ae34>** **</color>%%%%<color=#f4ae34>*,      **</color>%%%%<color=#f4ae34>**     **</color>%%%%%<color=#f4ae34>*****,**</color>%%%%%<color=#f4ae34>**</color>  &%%%</color>")
+//print("<color=#1a508d>&%%%&  <color=#f4ae34>**</color>%%%%%<color=#f4ae34>****</color>%%%%<color=#f4ae34>*****</color>%%%%<color=#f4ae34>*,      **</color>%%%%<color=#f4ae34>**     **</color>%%%%%<color=#f4ae34>*****,**</color>%%%%%<color=#f4ae34>**</color>  &%%%</color>")
+//print("<color=#1a508d>&%%%&  <color=#f4ae34>*,*,*,</color>*%%%%%%%%%%%%%<color=#f4ae34>*,*,*,      *,</color>%%%%<color=#f4ae34>*,     ,*,*,*,</color>%%%%%%%%%%%%%<color=#f4ae34>,*</color>  &%%%</color>")
+//print("<color=#1a508d>&%%%&       <color=#f4ae34>**</color>%%%%%%%%%%%%%<color=#f4ae34>*****,      **</color>%%%%<color=#f4ae34>**          **</color>%%%%%%%%%%%%%<color=#f4ae34>**</color>  &%%%</color>")
+//print("<color=#1a508d>&%%%&       <color=#f4ae34>****,**********</color>%%%%<color=#f4ae34>*,      ********          *******,*********</color>  &%%%</color>")
+//print("<color=#1a508d>&%%%&                    <color=#f4ae34>**####*,</color>                                           &%%%</color>")
+//print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
+//print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
+//print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
+//print("<color=#1a508d>&%%%&                                                                       &%%%</color>")
+//print("<color=#1a508d>@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%</color>")
+//print("<color=#1a508d>   &%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&  </color>")
+//print("                                                                                ")
+//print("<color=#182d53>                              &&&&&&&&&&&&&&&&&&&&&                             </color>")
+//print("<color=#182d53>                              &&&&&&&&&&&&&&&&&&&&&                             </color>")
+//print("<color=#1a508d>                 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%                </color>")
 
 ipAddress = params[0]
 
@@ -46,9 +48,9 @@ globals.vulnerabilitiesDirectoryName = "/home/" + active_user + "/vulnerabilitie
 myshell = get_shell
 myshell.host_computer.create_folder( "/home/" + active_user, "vulnerabilities")
 
-"#import jsonparser.gs"
-"#import vuln.gs"
-"#import tools.gs"
+"#import libs/models/jsonparser.gs"
+"#import libs/models/vuln.gs"
+"#import libs/models/utils.gs"
 
 //Defining functions
 
@@ -62,18 +64,18 @@ nmap = function(ipAddress)
 	else
 		globals.router = get_router( ipAddress )
 	end if
-
+	
 	if globals.router == null then exit("nmap: ip address not found")
-
+	
 	isRouterIp = globals.router.local_ip == ipAddress
 	ports = null
-
+	
 	if not isLanIp or isRouterIp then
 		ports = globals.router.used_ports
 	else
-		ports = globals.router.computer_ports(ipAddress)
+		ports = globals.router.device_ports(ipAddress)
 	end if
-
+	
 	return ports
 end function
 // END functions definitions
@@ -92,13 +94,13 @@ for port in ports
 		lib = getLib(ipAddress, port)
 	end if
 	libId = lib.lib_name + "_" + lib.version
-
+	
 	existingVulnerabilityFile = getFile(globals.vulnerabilitiesDirectoryName, libId)
 	if (existingVulnerabilityFile != null) then
 		print("Vulnerability file '" + libId + "' already existing, skipping")
 		continue
 	end if
-
+	
 	if (scannedLibs.indexOf(libId) != null) then
 		continue
 	else
@@ -143,7 +145,7 @@ for port in ports
 			vuln._setVuln(addressForThoseVulnerabilitys, vulnerability)
 			vulnerabilitiesArray.push(vuln)
 		end for
-
+		
 	end for
 end for
 
@@ -156,7 +158,7 @@ for vulnerability in vulnerabilitiesArray
 	filePath = vulnerability._getFilePath
 	fileName = vulnerability.libName + "_" + vulnerability.libVersion + "_" + md5(vulnerability.vulnUnsecValue) + ".json"
 	myshell.host_computer.touch(filePath, fileName)
-
+	
 	vulnerabilityFile = myshell.host_computer.File(filePath + fileName)
 	vulnerabilityFile.set_content("")
 	vulnerabilityFile.set_content(vulnerabilityFile.content + char(10) + toJSON(vulnerability))
diff --git a/scripts/defense/backdoor.gs b/scripts/defense/backdoor.gs
new file mode 100644
index 0000000000000000000000000000000000000000..1c1de3721b1b5c94aa2714cadd0d5b5159c2b239
--- /dev/null
+++ b/scripts/defense/backdoor.gs
@@ -0,0 +1,5 @@
+// Untested!  
+// This file has only been merged into master because it depended on the Repo Unification Process
+// Which caused a lot of merge conflicts, so reducing merge requests made life easier for everybody at the time
+
+"#import libs/utils.inc.src"
diff --git a/scripts/defense/colony.gs b/scripts/defense/colony.gs
new file mode 100644
index 0000000000000000000000000000000000000000..1f6c022d724862e29015d4e83363f90533ba4f3a
--- /dev/null
+++ b/scripts/defense/colony.gs
@@ -0,0 +1,25 @@
+"#import libs/utils.inc.src"
+"#import libs/file.inc.src"
+"#import libs/remote.inc.src"
+
+// Tool to send to rented servers the default binaries from computers
+// Note that the utility methods to transfer a file only accepts complete path
+// "connect" and "transfer" should be easy reusable for a code pulling tool (issue #7)
+
+//command: colony
+length = imports.utils.ParamLength(params)
+if length < 3 or length > 5 or imports.utils.HasFlag(params, "h", "help") then exit("<b>Usage: "+imports.utils.GetProgName()+" [user] [password] [ip] [(opt) port] [(opt) service]</b>")
+comp = get_shell.host_computer
+
+user = imports.utils.GetParam(params, 0)
+password = imports.utils.GetParam(params, 1)
+ip = imports.utils.GetParam(params, 2)
+port = imports.utils.GetParam(params, 3)
+service = imports.utils.GetParam(params, 4)
+
+remote = imports.remote.connect(get_shell, user, password, ip, port, service)
+if not remote then exit("Can't connect to remote server")
+for path in imports.file.MISSING
+	result = imports.remote.upload(remote, path, path[:path.lastIndexOf("/")])
+	if typeof(result) == "string" then print(result) else print("Sent to "+result.path)
+end for
diff --git a/scripts/defense/guestdo.gs b/scripts/defense/guestdo.gs
new file mode 100644
index 0000000000000000000000000000000000000000..9478f641483db324ba00337d4e19e4f842089bd9
--- /dev/null
+++ b/scripts/defense/guestdo.gs
@@ -0,0 +1,38 @@
+// Untested!  
+// This file has only been merged into master because it depended on the Repo Unification Process
+// Which caused a lot of merge conflicts, so reducing merge requests made life easier for everybody at the time
+
+"#import utils.inc.src"
+
+utils = imports.utils
+exit = @utils.Exit
+
+// TODO: determine the exploit to use in current version (and the lib too)
+MEM = ""
+VAL = ""
+if not MEM or not VAL then exit("No suitable guest exploit found yet!")
+
+metaxploit = utils.IncludeLib("metaxploit.so")
+comp = utils.Escalate().host_computer
+
+NAME = "guest.so"
+DIR = "/root/Downloads/"
+PATH = NAME+DIR
+lib = comp.File(PATH)
+if not lib then exit("No lib available at "+PATH)
+
+if not utils.UserPrompt("Are you sure you want to switch this Terminal into a guest access?") then exit("Execution cancelled")
+
+DIR = "/libs/"
+PATH = NAME+DIR
+
+if typeof(lib.copy(DIR, NAME)) == "file" then lib = comp.File(PATH) else lib = null
+if not lib then exit("Unable to copy "+NAME)
+
+metalib = metaxploit.load(PATH)
+if not metalib then exit(NAME+" is not a library!?")
+guest = metalib.overflow(MEM, VAL)
+lib.delete()
+
+if not guest then exit("Exploit failed!?")
+guest.start_terminal()
\ No newline at end of file
diff --git a/logincleanup.src b/scripts/defense/logincleanup.src
similarity index 100%
rename from logincleanup.src
rename to scripts/defense/logincleanup.src
diff --git a/setup.src b/scripts/defense/setup.gs
similarity index 89%
rename from setup.src
rename to scripts/defense/setup.gs
index 7422b7c33e11eb5d36b076ccffc7dd679bb35ee5..fa6d6934699e69efd6a8b6443f1499353b879a5c 100644
--- a/setup.src
+++ b/scripts/defense/setup.gs
@@ -1,3 +1,5 @@
+"#import libs/utils.inc.src"
+
 // Can create two limited users : ssh and ftp (when optional passwords are provided)
 // Make sure to have created the custom remote and members accounts before that
 // For the exact effects on permissions, see https://usine.solution-libre.fr/qtg/grey-hack/-/issues/9
@@ -5,29 +7,6 @@
 // !!! This program will self-delete after use !!!
 // Note : chmodding takes a bit of time for unknown reasons (and GH doesn't show prints while running)
 // Expect ONE MINUTE or so until the single print statement shows up
-
-// Meant for preprocessor use
-imports = {}
-imports.utils = {}
-imports.utils.GetProgName = function()
-	return program_path.split("/")[-1]
-end function
-imports.utils.IsRoot = function()
-	return active_user == "root"
-end function
-
-imports.utils.Exit = function(reason)
-	exit(imports.utils.GetProgName()+": "+reason)
-end function
-imports.utils.GetParam = function(params, index)
-    if index < 0 or index >= imports.utils.ParamLength(params) then return null
-	return params[index]
-end function
-imports.utils.HasFlag = function(params, short, long)
-    if imports.utils.ParamLength(params) == 0 then return null
-	param = params[0]
-	return param == "-"+short or param == "--"+long
-end function
 	
 imports.temp = {}
 imports.file = {}
@@ -194,7 +173,7 @@ Safe(USER_GROUP, imports.file.ALLOWED)
 Safe(SSH_GROUP, imports.file.SSH)
 Safe(FTP_GROUP, imports.file.FTP) // Normally no-op
 
-comp.File(program_path).delete
+imports.utils.SelfDestruct()
 
 // #2.2: It would be a good idea to delete/reset (...), just in case. and the log file too.
 WipeDir("/var/system.log")
diff --git a/scripts/libs/antilogs.inc.src b/scripts/libs/antilogs.inc.src
new file mode 100644
index 0000000000000000000000000000000000000000..125569f2d9b89c499e013017b5cfb597951be27a
--- /dev/null
+++ b/scripts/libs/antilogs.inc.src
@@ -0,0 +1,58 @@
+// Untested!  
+// This file has only been merged into master because it depended on the Repo Unification Process
+// Which caused a lot of merge conflicts, so reducing merge requests made life easier for everybody at the time
+
+"#ifbuild"
+if not globals.hasIndex("imports") then imports = {}
+imports.temp = {}
+"#endif"
+imports.antilogs = {}
+"#ifbuild"
+antilogs = imports.antilogs
+"#endif"
+
+imports.antilog.ENABLED = true
+
+imports.antilog.cache = null
+
+imports.antilog.check = function()
+    t = imports.antilog // this
+    if not t.ENABLED then return null
+
+    comp = get_shell.host_computer
+    utils = imports.utils
+    if not utils.FileAccess(comp.File("/var/"), "r") then return null
+
+    if not utils.FileAccess(comp.File(user_folder+"/.Trash/),"w") then
+        if not utils.FileAccess(comp.File(user_folder)," w") then return null
+        if not comp.create_folder(user_folder,".Trash") then return null
+    end if
+
+    log = comp.File("/var/system.log")
+    if utils.FileAccess(log,"w") then return log
+    return null
+end function
+
+imports.antilog.store = function(force=false)
+    cache = imports.antilog.cache
+    if cache != null then
+    if not force then return
+    cache.delete()
+end if
+
+imports.antilog.refresh = function()
+    log = imports.antilog.check()
+    if not log then return
+
+    NAME="system.log"
+    PATH = user_folder+"/.Trash/"
+    if typeof(log.copy(PATH,NAME)) == "string" then return
+    imports.antilog.cache = comp.File(PATH+"/"+NAME)
+end function
+
+imports.antilog.rollback = function()
+    if not imports.antilog.check() then return
+    cache = imports.antilog.cache
+    if not cache then return
+    cache.copy("/var/","system.log")
+end function
\ No newline at end of file
diff --git a/scripts/libs/file.inc.src b/scripts/libs/file.inc.src
index 13b94d48de01bdee38875e20c4c1ef5322f5d0d2..cf6bf7b3724bd14449e455a39f47ac2287b525fd 100644
--- a/scripts/libs/file.inc.src
+++ b/scripts/libs/file.inc.src
@@ -175,6 +175,10 @@ imports.file.FileAccess = function(file, perms, shout=true)
 	if not file then return false
 	for i in range(0,perms.len-1)
 		perm = perms[i]
+		
+		// Ugly hack: directories don't need r or x permission to enumerate files
+		if file.is_folder and perm != "w" then continue
+		
 		if file.has_permission(perm) then continue
 		if shout then imports.utils.Print("Error: can't use ('"+perm+"') contents of '"+file.path+"'")
 		return false
@@ -183,8 +187,8 @@ imports.file.FileAccess = function(file, perms, shout=true)
 end function
 
 // Tries deleting a file, but checks that you have the required permissions first (returns false if unallowed)
-imports.file.Delete = function(file)
-	if not imports.file.FileAccess(file) then return false
+imports.file.delete = function(file)
+	if not imports.file.FileAccess(file.parent,"w") then return false
 	file.delete()
 	return true
 end function
@@ -193,6 +197,12 @@ imports.file.GetDir = function(path)
 	if typeof(path) == "file" then path = path.path
 	return path[0:path.lastIndexOf("/")]
 end function
+// Like GetDir but return a file instead of a path
+// Unlike GetDir, the result doesn't change if the file is already a folder! 
+imports.file.GetDirRef = function(folder)
+    if folder.is_folder then return folder
+    return folder.parent 
+end function
 // Determines the filename from a String path
 imports.file.GetName = function(path)
 	if typeof(path) == "file" then path = path.path
diff --git a/scripts/libs/filereaper.inc.src b/scripts/libs/filereaper.inc.src
index 6728003e8b998b108973f23d20acc7441ef53ce0..983092814c162b76cc77332eea7fefa250689de8 100644
--- a/scripts/libs/filereaper.inc.src
+++ b/scripts/libs/filereaper.inc.src
@@ -1,5 +1,5 @@
-"#import utils.src"
-"#import file.src"
+"#import libs/utils.src"
+"#import libs/file.src"
 
 "#ifbuild"
 if not globals.hasIndex("imports") then imports = {}
@@ -19,14 +19,14 @@ filereaper = imports.filereaper
 imports.filereaper.FindFile = function(origin, children)
 	if origin == null then return null
 	// May be available through another user
-	if not imports.file.FileAccess(origin, "r") then return false	
+	if not imports.file.FileAccess(origin, "r") then return false
 	for child in children
 		if typeof(origin) != "file" then return origin
 		origin = imports.filereaper._FindFile(origin, child)
 	end for
 	return origin
 end function
-imports.filereaper._FindFile = function(origin, child)	
+imports.filereaper._FindFile = function(origin, child)
 	found = null
 	for file in origin.get_files()
 		if file.name == child then
@@ -100,7 +100,7 @@ end function
 imports.filereaper.LoadPasswdList = function(pars)
 	state = pars[0]
 	creds = pars[1]
-	
+
 	for cred in creds
 		imports.filereaper.AddPasswd([state,cred])
 	end for
@@ -176,30 +176,30 @@ imports.filereaper.exploitFile = function(pars, file, targetRoot=true)
 		if imports.filereaper.exploitFile(pars, file.parent, targetRoot) == true then return true
 	end if
 	if not imports.file.FileAccess(file, "r") then return false
-	
+
 	ACC_OK = @imports.filereaper.UseAccFile
 	ACC_FAIL = @imports.filereaper.MissingAcc
 	PASS_OK = @imports.filereaper.UsePasswdFile
 	PASS_FAIL = @imports.filereaper.MissingPasswdList
-	
+
 	if file.path == "/" then
 		imports.filereaper.HandleFile(file, ["etc","passwd"], @PASS_OK, @PASS_FAIL, [state])
 		imports.filereaper.HandleFile(file, ["root","Config","Bank.txt"], @ACC_OK, @ACC_FAIL, [state, "root", true])
 		imports.filereaper.HandleFile(file, ["root","Config","Mail.txt"], @ACC_OK, @ACC_FAIL, [state, "root", false])
-		
+
 		home = imports.filereaper._FindFile(file, ["home"])
 		imports.filereaper.HandleFile(home, [], null, @imports.filereaper.MissingUserList, [state])
 		if typeof(home) == "file" then imports.filereaper.exploitFile(pars, home, false)
 		return true
 	end if
-	
+
 	if imports.utils.starts_with(file.path, "/etc/") then
 		pasd = null
 		if file.path == "/etc/" then pasd = file
 		if file.path == "/etc/passwd" then pasd = imports.filereaper._FindFile(file, ["passwd"])
 		imports.filereaper.HandleFile(pasd, [], @PASS_OK, @PASS_FAIL, [state, pasd])
 	end if
-	
+
 	if imports.utils.starts_with(file.path, "/home/") then
 		count = imports.utils.char_count(file.path,"/")
 		if count == 2 then
@@ -209,12 +209,12 @@ imports.filereaper.exploitFile = function(pars, file, targetRoot=true)
 		else if count == 3 then
 			imports.filereaper.AddUser(file.name)
 		end if
-	else if imports.utils.starts_with(file.path, "/root/") then	
+	else if imports.utils.starts_with(file.path, "/root/") then
 		count = imports.utils.char_count(file.path,"/")+1
 	else
 		count = 0 //or return
 	end if
-	
+
 	if count == 4 and file.name == "Config" then
 		imports.filereaper.HandleFile(file, ["Bank.txt"], @ACC_OK, @ACC_FAIL, [state, file.parent.name, true])
 		imports.filereaper.HandleFile(file, ["Mail.txt"], @ACC_OK, @ACC_FAIL, [state, file.parent.name, false])
diff --git a/scripts/tools/jsonparser.gs b/scripts/libs/models/jsonparser.gs
similarity index 100%
rename from scripts/tools/jsonparser.gs
rename to scripts/libs/models/jsonparser.gs
diff --git a/scripts/tools/utils.gs b/scripts/libs/models/utils.gs
similarity index 100%
rename from scripts/tools/utils.gs
rename to scripts/libs/models/utils.gs
diff --git a/scripts/models/vuln.gs b/scripts/libs/models/vuln.gs
similarity index 100%
rename from scripts/models/vuln.gs
rename to scripts/libs/models/vuln.gs
diff --git a/scripts/libs/passwords.inc.src b/scripts/libs/passwords.inc.src
index b1247746505167966e223a8c4a1d65c0a7ae6b34..82d580fe10db2073b2e784f2a35489c866f6a107 100644
--- a/scripts/libs/passwords.inc.src
+++ b/scripts/libs/passwords.inc.src
@@ -1,5 +1,5 @@
-"#import utils.inc.src"
-"#import file.inc.src"
+"#import libs/utils.inc.src"
+"#import libs/file.inc.src"
 
 "#ifbuild"
 if not globals.hasIndex("imports") then imports = {}
diff --git a/scripts/libs/qtglogo.inc.src b/scripts/libs/qtglogo.inc.src
new file mode 100644
index 0000000000000000000000000000000000000000..896ec8723e2ff4c0563a2265a8fde32797591259
--- /dev/null
+++ b/scripts/libs/qtglogo.inc.src
@@ -0,0 +1,69 @@
+"#import libs/ui.inc.src"
+
+printLogo = function()
+    R = @imports.ui.Repeat
+    J = function(text)
+    	return imports.ui.Color("#f4ae34",text)
+    end function
+    B = function(text)
+    	return imports.ui.Color("#1a508d",text)
+    end function
+    N = function(text)
+    	return imports.ui.Color("#182d53",text)
+    end function
+    
+    // Bord droit
+    temp = "&%%%"
+    D = "  "+temp
+    // Bord gauche
+    G = temp+"&  "
+    // Taille totale
+    L = 80
+    // Ligne vide
+    V = R(" ",L-G.len-D.len)
+    
+    // Haut du moniteur
+    print(B(R(" ", 6)+R("&", L-6-5)+R(" ", 5)))
+    print(B(" "+"&"+R("%", L-2-1)+" "))
+    print(B("&"+R("%", 4)+R(" ", L-5-4)+R("%", 4)))
+    
+    // Dessus vide
+    for i in range(1,5)
+    	print(B(G+V+D))
+    end for
+    
+    // Pour simplifier un peu...
+    R4=R(" ",4)
+    R5=R(" ",5)
+    R17=R(" ",17)
+    
+    // Source : univscan.gs
+    print(B(G+R5+            J(",***,***,***,***,    ")                 +" "+               R17             +" "+R5+         J("***,***,***,***,*")           +D))
+    print(B(G+R5+        J("*")+"%%%%%%%%%%%%%%"+J("**    ")            +" "+               R17             +" "+R5+      J("**")+"%%%%%%%%%%%%%"+J("**")     +D))
+    print(B(G+         J("******")+"%%%%%%%%%%%%%%"+J("*****,")         +" "+     J("**************,**")    +" "+     J("*******")+"%%%%%%%%%%%%%"+J("**")    +D))
+    print(B(G+   J("**")+"%%%%%"+J("*,         **")+"%%%%"+J("*,")      +" "+J("**")+"%%%%%%%%%%%%%"+J("**")+" "+      J("**")+"%%%%%"+J("**")      +R(" ",13)+D))
+    print(B(G+   J("*,")+"%%%%%"+J("*,         **")+"%%%%"+J("*,")      +" "+J("******,")+"%%%%"+J("***,**")+" "+ J("**")+"%%%%%"+J("****")+"%%%%"+J("**") +R5+D))
+    print(B(G+    J("**")+"%%%%%"+J("*,******** **")+"%%%%"+J("*,")     +" "+R5+  J("**")+"%%%%"+J("**") +R4+" "+J("**")+"%%%%%"+J("****")+"%%%%"+J("*******")+D))
+    print(B(G+J("**")+"%%%%%"+J("*,,*")+"%%%%"+J("** **")+"%%%%"+J("*,")+" "+R5+  J("**")+"%%%%"+J("**") +R4+" "+J("**")+"%%%%%"+J("*****,**")+"%%%%%"+J("**")+D))
+    print(B(G+J("**")+"%%%%%"+J("****")+"%%%%"+J("*****")+"%%%%"+J("*,")+" "+R5+  J("**")+"%%%%"+J("**") +R4+" "+J("**")+"%%%%%"+J("*****,**")+"%%%%%"+J("**")+D))
+    print(B(G+          J("*,*,*,")+"*%%%%%%%%%%%%%"+J("*,*,*,")        +" "+R5+  J("*,")+"%%%%"+J("*,") +R4+" "+     J(",*,*,*,")+"%%%%%%%%%%%%%"+J(",*")    +D))
+    print(B(G+R5+         J("**")+"%%%%%%%%%%%%%"+J("*****,")           +" "+R5+  J("**")+"%%%%"+J("**") +R4+" "+R5+      J("**")+"%%%%%%%%%%%%%"+J("**")     +D))
+    print(B(G+R5+            J("****,**********")+"%%%%"+J("*,")        +" "+R5+       J("********")     +R4+" "+R5+          J("*******,*********")          +D))
+    print(B(G+R(" ",18)+                           J("**####*,")        +R(" ", 41)+D))
+    
+    // Dessous vide
+    for i in range(1,4)
+    	print(B(G+V+D))
+    end for
+    
+    // Bas du moniteur
+    print(B("@"+R("%",L-1)))
+    print(B(R(" ",4)+"&"+R("%",L-5-2)+"  "))
+    print(R(" ",L))
+    
+    // Support du moniteur
+    for i in range(1,2)
+    	print(N(R(" ", 31)+R("&",L-31-29)+R(" ",29)))
+    end for
+    print(B(R(" ",17)+R("%",L-17-16)+R(" ",16)))
+end function
diff --git a/scripts/libs/remote.inc.src b/scripts/libs/remote.inc.src
index 0dd9cc5d99202912c21d1e028a815b8614c1f3bf..16e1211967a964dfdb88425a803e6d0c0a4ad6be 100644
--- a/scripts/libs/remote.inc.src
+++ b/scripts/libs/remote.inc.src
@@ -1,3 +1,5 @@
+"#import libs/file.inc.src"
+
 "#ifbuild"
 if not globals.hasIndex("imports") then imports = {}
 imports.temp = {}
@@ -14,7 +16,7 @@ imports.remote.LoadFromCache = function(key, shell, user, pass, ip)
 	imports.remote.cache[key] = result
 	return result
 end function
-	
+
 imports.remote.servers = {}
 imports.remote.servers.FILES = function()
 	return imports.remote.LoadFromCache("files", get_shell, "root", "Helby", "220.47.69.119")
@@ -99,24 +101,24 @@ end function
 		// if not imports.utils.UserPrompt("Acceptez-vous de transférer non-anonymement?") then exit()
 		// anon = false
 	// end if
-	
+
 	// if dest[dest.len-1] != "/" then dest = dest + "/"
 	// origin = imports.file.AbsolutePath(origin, null, sourceShell)
-	
+
 	// comp = sourceShell.host_computer
 	// src = comp.File(origin)
 	// if src == null then
 		// print(origin+" doesn't exist on source server")
 		// return null
 	// end if
-	
+
 	// fileName = origin[origin.lastIndexOf("/")+1:]
 	// destPath = dest+fileName
 	// if targetShell.host_computer.File(destPath) != null then
 		// print(destPath+" already exists on target server")
 		// return null
 	// end if
-	
+
 	// temp = null
 	// if anon then
 		// comp.create_folder("/", "server")
@@ -128,13 +130,13 @@ end function
 			// if targetShell.host_computer.File(dest+fileName) != null then continue
 			// break
 		// end while
-		
+
 		// DANGER: cette ligne fait remplacer votre harddrive par celui du serveur!?
 		// src.copy("/server/temp/", fileName)
 		// temp = comp.File("/server/temp/"+fileName)
 		// origin = temp.path
 	// end if
-	
+
 	// result = sourceShell.scp_upload(origin, dest, targetShell)
 	// if temp != null then temp.delete()
 	// return targetShell.host_computer.File(destPath)
@@ -153,42 +155,36 @@ imports.remote.download = function(shell, origin, dest, destName=null)
 		destName = null
 	end if
 	// TODO: temporary limiter ends here
-	
-	temp = imports.remote.PreTransfer(origin, dest)
+
+	temp = imports.remote.PreTransfer(origin, dest, shell, get_shell)
 	origin = temp[0]
 	dest = temp[1]
 
 	if destName != null	then
 		server = shell.host_computer
-		if not imports.remote.CheckTempFolder(server) then
-			print("ERROR: unable to write in /server/temp/")
-			return null
-		end if
-		
+		if not imports.remote.CheckTempFolder(server) then return "unable to write in /server/temp/"
+
 		src = server.File(origin)
-		if src == null then
-			print(origin+" doesn't exist on source server")
-			return null
-		end if
-		
+		if src == null then return origin+" doesn't exist on source server"
+
 		destName = imports.remote.RandomName(server, get_shell.host_computer, dest, destName)
-		
+
 		// DANGER: cette ligne fait remplacer votre harddrive par celui du serveur!?
 		src.copy("/server/temp/", destName)
 		origin = "/server/temp/"+destName
 	end if
-	
+
 	result = imports.remote.transfer(shell, get_shell, origin, dest)
-	
+
 	if destName != null then src.delete()
-	
+
 	return result
 end function
 
 // Upload a file with scp if it doesn't already exist
 // Can also use a different name than the original file : if said name is blank, a random name will be generated
 imports.remote.upload = function(shell, origin, dest, destName=null)
-	
+
 	// TODO : remove this limiter when the GH issue gets fixed
 	if destName != null then
 		print("Les téléchargements renommés font reset les harddrives suite à un bug")
@@ -198,47 +194,41 @@ imports.remote.upload = function(shell, origin, dest, destName=null)
 		destName = null
 	end if
 	// TODO: temporary limiter ends here
-	
-	temp = imports.remote.PreTransfer(origin, dest)
+
+	temp = imports.remote.PreTransfer(origin, dest, get_shell, shell)
 	origin = temp[0]
 	dest = temp[1]
 
 	transferDest = dest
-	
+
 	if destName != null then
 		server = shell.host_computer
-		if not imports.remote.CheckTempFolder(server) then
-			print("ERROR: unable to write in /server/temp/")
-			return null
-		end if
-		
+		if not imports.remote.CheckTempFolder(server) then return "unable to write in /server/temp/"
+
 		if dest[dest.len-1] != "/" then dest = dest + "/"
 		// The destination file doesn't need to be available in temp folder
 		// Also, in this case the server is ALSO the target
 		destName = imports.remote.RandomName(null, server, "", destName)
-		
+
 		src = server.File(dest+destName)
-		if src != null then
-			print(dest+destName+" already exists on target server")
-			return null
-		end if
-		
+		if src != null then return dest+destName+" already exists on target server"
+
 		// The temporary folder doesn't need to be available in the target folder
 		folderName = imports.remote.RandomName(server, null, dest, "")
 		// Creates a temporary folder which will receive the file, no matter what its name is
 		server.create_folder("/server/temp/", folderName)
 		transferDest = "/server/temp/"+folderName
 	end if
-	
+
 	result = imports.remote.transfer(get_shell, shell, origin, transferDest)
-	
+
 	if destName != null then
 		// DANGER: cette ligne fait remplacer votre harddrive par celui du serveur!?
 		if result then result.move(dest,destName)
 		// Delete the temporary folder
 		server.File(transferDest).delete()
 	end if
-	
+
 	return result
 end function
 
@@ -247,30 +237,25 @@ end function
 // Returns : the new file, or NULL
 imports.remote.transfer = function(sourceShell, targetShell, origin, dest)
 	src = sourceShell.host_computer.File(origin)
-	if src == null then
-		print(origin+" doesn't exist on source server")
-		return null
-	end if
-	
+	if src == null then return origin+" doesn't exist on source server"
+
 	fileName = origin[origin.lastIndexOf("/")+1:]
-	
+
 	destPath = dest+fileName
 	comp = targetShell.host_computer
-	
-	if comp.File(destPath) != null then
-		print(destPath+" already exists on target server")
-		return null
-	end if
-	
-	result = sourceShell.scp_upload(origin, dest, targetShell)
-	if result != true then print("scp_upload error: "+result)
+
+	if comp.File(destPath) != null then return destPath+" already exists on target server"
+
+	result = sourceShell.scp(origin, dest, targetShell)
+	if result != true then return result
 	return comp.File(destPath)
 end function
 
-imports.remote.PreTransfer = function(origin, dest)
+imports.remote.PreTransfer = function(origin, dest, originShell, destShell)
 	if dest[dest.len-1] != "/" then dest = dest + "/"
-	origin = imports.file.AbsolutePath(origin, null, sourceShell)
-	dest = imports.file.AbsolutePath(dest, null, targetShell)
+	origin = imports.file.AbsolutePath(origin, null, originShell)
+	dest = imports.file.AbsolutePath(dest, null, destShell)
+	return [origin, dest]
 end function
 
 // Returns false if /server/temp/ is not usable
diff --git a/scripts/libs/ui.inc.src b/scripts/libs/ui.inc.src
index c974f1263689043ead61a5c8da02b70478be4544..7730021a3018f44e225f4ed5319f5a8bbb69cb68 100644
--- a/scripts/libs/ui.inc.src
+++ b/scripts/libs/ui.inc.src
@@ -1,6 +1,5 @@
-"#import utils.inc.src"
-"#import file.inc.src"
-"#import json.inc.src"
+"#import libs/utils.inc.src"
+"#import libs/file.inc.src"
 
 "#ifbuild"
 if not globals.hasIndex("imports") then imports = {}
@@ -101,7 +100,6 @@ imports.ui.Repeat = function(char, len)
 	for i in range(0,len-1)
 		result = result + char
 	end for
-	print("!"+result+"!")
 	return result
 end function
 
diff --git a/scripts/libs/utils.inc.src b/scripts/libs/utils.inc.src
index c1f0c48e4dad1f92d4086595983f239b57de5349..d93b2b659d00cb9dcb39ef196cdfe3eabd8739cc 100644
--- a/scripts/libs/utils.inc.src
+++ b/scripts/libs/utils.inc.src
@@ -1,6 +1,6 @@
 // Only used for ONE method, choice left to the main script
-//"#import dict.inc.src"
-//"#import remote.inc.src"
+//"#import libs/dict.inc.src"
+//"#import libs/remote.inc.src"
 
 "#ifbuild"
 if not globals.hasIndex("imports") then imports = {}
@@ -58,6 +58,13 @@ end function
 imports.utils.IsRoot = function()
 	return active_user == "root"
 end function
+imports.utils.Escalate = function(mendatory=true)
+    if imports.utils.IsRoot() then return get_shell
+    password = user_input("Please enter root password: ")
+    result = get_shell("root", password)
+    if mendatory and not result then exit("Unable to log as root")
+    return result
+end function
 
 imports.utils.Print = function(reason)
 	print(imports.utils.GetProgName()+": "+reason)
@@ -151,36 +158,36 @@ imports.utils.Choice = function(choices, reason=null)
 end function
 
 imports.utils.HASH_SIZE = 32
-imports.utils.GetSafePassword = function(userPass, cryptools, prompt=true)	
+imports.utils.GetSafePassword = function(userPass, cryptools, prompt=true)
 	if userPass.indexOf(":") != null then
 		data = userPass.split(":")
 		username = data[0]
 		password = data[1]
 	else
 		username = ""
-		password = userPass		
+		password = userPass
 	end if
-	
+
 	if cryptools and password.len == imports.utils.HASH_SIZE then password = imports.utils._decipher(username, password, cryptools, prompt)
-	
+
 	return [username,password]
 end function
 imports.utils._decipher = function(user, hash, cryptools, prompt=true)
 	dict = imports.utils.IsImported("dict")
-	
+
 	if dict then
 		password = dict.decrypt(hash, user, null) // No cryptool
 		if not imports.utils.is_empty(password) then return password
 	end if
-	
+
 	if imports.utils.is_empty(user) then return hash
-	
+
 	print("Decipher required for "+hash)
 	if prompt and not imports.utils.UserPrompt("Do you want to decipher the password of "+user+"?") then
 		print("Cancelled deciphering of "+user+":"+hash)
 		return ""
 	end if
-	
+
 	password = cryptools.decipher(user, hash)
 	if dict and not imports.utils.is_empty(password) then dict.addPass(hash, password)
 	return password
diff --git a/scripts/libs/versioning.inc.src b/scripts/libs/versioning.inc.src
index b92bdff922ee0ca1f1fd9dd5e503aebcb166a28c..efb4268a8a146a5a44a085b4290623176efbdd19 100644
--- a/scripts/libs/versioning.inc.src
+++ b/scripts/libs/versioning.inc.src
@@ -1,4 +1,4 @@
-"#import utils.inc.src"
+"#import libs/utils.inc.src"
 
 "#ifbuild"
 if not globals.hasIndex("imports") then imports = {}
diff --git a/scripts/libs/vshell.inc.src b/scripts/libs/vshell.inc.src
new file mode 100644
index 0000000000000000000000000000000000000000..2693b6268d26af67f37cd608d333354697274a75
--- /dev/null
+++ b/scripts/libs/vshell.inc.src
@@ -0,0 +1,161 @@
+// Untested!  
+// This file has only been merged into master because it depended on the Repo Unification Process
+// Which caused a lot of merge conflicts, so reducing merge requests made life easier for everybody at the time
+
+"#import libs/utils.inc.src"
+"#import libs/file.inc.src"
+
+"#ifbuild"
+if not globals.hasIndex("imports") then imports = {}
+imports.temp = {}
+"#endif"
+imports.vshell = {}
+"#ifbuild"
+vshell = imports.vshell
+"#endif"
+
+imports.vshell.EXIT = "exit"
+
+imports.vshell.constrs = {}
+
+imports.vshell.constrs.shell = function(object)
+    result = {}
+    
+    result.start_terminal = function()
+        while true
+            print("Please enter a command:"+imports.file.NEW_LINE)
+            command = user_prompt().split(" ")
+            if command[0] == imports.vshell.EXIT then break
+            cmd = imports.utils.SafeMapAccess(imports.vshell.cmds,command[0])
+            if cmd == null then 
+                print(cmd+ " is not a recognized command")
+                return
+            end if
+            
+            // Remove the command name
+            params = []
+            for i in range(1,command.len-1)
+                params.push(command[i])
+            end for
+            
+            heart = result.heart
+            current_path = result.current_path
+            
+            result = cmd(self, params)
+            if result and typeof(result) != "function" then
+                print(command[0] +" error: "+result)
+                // Rollback
+                result.heart = heart
+                result.current_path = current_path
+            end if
+        end while
+    end function
+    
+    result.navigate = function(path)
+        isFile = typeof(self.heart) == "file"
+        if not path then
+            if isFile then return self.heart
+            return self.heart.File(current_path)
+        end if
+        
+        path = imports.file.AbsolutePath(path) 
+        
+        if isFile then
+            result = imports.vshell.navigate(self.heart, path)
+        else 
+            result = self.heart.File(path)
+        end if
+        
+        if not imports.file.FileAccess(result,"r") then
+            print("Error, unable to access the target")
+            return
+        end if
+        folder = imports.file.GetDirRef(result)
+        if not imports.file.FileAccess(folder,"r") then
+            print("Error, unable to access the target's folder")
+            return
+        end if
+        
+        self.current_path = folder.path
+        if typeof(self.heart) == "file" then self.heart = result
+        return result
+    end function
+    
+    result.heart = object
+    
+    // Init the current_path var
+    if typeof(object) == "file" then path = object.path else path = object.current_path
+    result.navigate(path)
+    
+    return result
+end function
+
+imports.vshell.navigate = function(file, target)
+    originSteps = file.path.split("/")
+    targetSteps = file.path.split("/")
+    // TODO: File navigation
+    return null
+end function
+
+imports.vshell.init = function(object)
+    if typeof(object) == "shell" then object = object.host_computer
+    type = typeof(object)
+    if type != "computer" and type != "file" then return null
+    return imports.vshell.constrs.shell(object)
+end function
+
+imports.vshell.run = function(object)
+    imports.vshell.init(object).start_terminal()
+end function
+
+imports.vshell.read = function(shell, params)
+    if params.len > 0 then return "Parameter required"
+    file = shell.navigate(params[0])
+    // TODO: File reading
+end function
+
+imports.vshell.cmds = {}
+
+imports.vshell.cmds.help = function(shell, params)
+    names = ["exit"]
+    for name in imports.vshell.cmds.values
+        names.put(name)
+    end for
+    print("List of accepted commands:")
+    print(imports.utils.PrintList(names,imports.file.NEW_LINE))
+end function
+
+imports.vshell.cmds.cd = function(shell, params)
+    if params.len == 0 then path = "" else path = params[0]
+    if not shell.navigate(path).is_folder then return "target is not a folder!"
+    
+    names = []
+    for item in file.get_folders
+        names = item.name+"/"
+    end for
+    for item in file.get_files
+        names = item.name
+    end for
+    
+    print("List of folders in: "+file.path)
+    print(imports.utils.PrintList(names,imports.file.NEW_LINE))
+end function
+
+imports.vshell.cmds.read = function(shell, params)
+    result = imports.vshell.read(shell,params)
+    if typeof(result) != "list" then return result
+    print(imports.utils.PrintList(result,imports.file.NEW_LINE))
+end function
+
+imports.vshell.cmds.get = function(shell, params)
+    result = imports.vshell.read(shell,params)
+    if typeof(result) != "list" then return result
+    content = imports.utils.PrintList(result,imports.file.NEW_LINE)
+end function
+
+imports.vshell.cmds.delete = function(shell, params)
+    if params.len > 0 then return "Parameter required"
+    file = shell.navigate(params[0])
+    if not imports.utils.UserPrompt("Do you *REALLY* want to delete '"+file.path+"' ?") then return "Execution cancelled by user"
+    if not imports.file.delete(file) then return "Permission defined"
+end function
\ No newline at end of file
diff --git a/archive.src b/scripts/tools/archive.src
similarity index 83%
rename from archive.src
rename to scripts/tools/archive.src
index 214b750b3fcfd518734921535fd52836a7f0ed7d..eb8ed8b0b3109961434c75307b489c60b5d7903e 100644
--- a/archive.src
+++ b/scripts/tools/archive.src
@@ -1,38 +1,42 @@
+"#import includes/utils.inc.src"
+"#import includes/file.inc.src"
+
 // Used by passing the path of the (txt) archive, then the path of all the files to add
 // More information at https://usine.solution-libre.fr/qtg/grey-hack/-/issues/6
 
+"#ifbuild"
 imports = {}
-	
+
 // The methods here are "good enough" to work in those cases
 // For example, the ParamLength normally discards flag-style args
 // But I don't want to give you all my tricks... yet, at least ;)
 imports.utils = {}
 imports.utils.ParamLength = function(params)
-    if params == null then return 0
+	if params == null then return 0
 	return params.len
 end function
 imports.utils.GetParam = function(params, index)
-    if index < 0 or index >= imports.utils.ParamLength(params) then return null
+	if index < 0 or index >= imports.utils.ParamLength(params) then return null
 	return params[index]
 end function
 imports.utils.HasFlag = function(params, short, long)
-    if imports.utils.ParamLength(params) == 0 then return null
+	if imports.utils.ParamLength(params) == 0 then return null
 	param = params[0]
 	return param == "-"+short or param == "-"+long
 end function
 // Dynamically getting the name of the command is left as an exercise to the reader :)
 imports.utils.GetProgName = function()
-    return program_path.split("/")[-1]
+	return program_path.split("/")[-1]
 end function
 imports.utils.Print = function(reason)
 	print(imports.utils.GetProgName()+": "+reason)
 end function
-	
+
 imports.file = {}
 imports.file.NEW_LINE = char(10)
 // Disclaimer : brand new method created for this script, may not handle special cases 
-imports.file.RelativePath = function(path)
-    if path[0] != "/" then path = get_shell.host_computer.current_path + "/" + path
+imports.file.AbsolutePath = function(path)
+	if path[0] != "/" then path = get_shell.host_computer.current_path + "/" + path
 	return path
 end function
 // Should return [] for unavailable files (binary or no read perms)
@@ -50,6 +54,20 @@ imports.file.FileAccess = function(file, perm)
 	imports.utils.Print("Error: can't use ('"+perm+"') contents of '"+file.path+"'")
 	return false
 end function
+imports.file.save = function(content, path, name)
+	data = name.split("/")
+	if data.len > 1 then
+		for index in range(0, data.len-2)
+			name = data[index]
+			comp.create_folder(path, name)
+			path = path +"/"+ name
+		end for
+	end if
+	name = data[data.len-1]
+	comp.touch(path, name)
+	comp.File(path+"/"+name).set_content(content)
+end function
+"#endif"
 
 // To avoid bruning our eyes with escaping
 QUOTE = """"
@@ -63,6 +81,8 @@ GetLastChar = function(str)
 end function
 // Returns the text in the 'unused' string or NULL if not a standalone string
 StringInstruction = function(str)
+	if str == null then return null
+	str = str.trim()
 	if GetLastChar(str) != QUOTE then return null
 	if str[0] != QUOTE then return null
 	str = str[1:str.len-1]
@@ -88,7 +108,7 @@ length = imports.utils.ParamLength(params)
 if length == 0 or imports.utils.HasFlag(params, "h", "help") then exit("<b>Usage: "+imports.utils.GetProgName()+" [file to compress/decompress] [...file paths]</b>")
 comp = get_shell.host_computer
 
-mainPath = imports.file.RelativePath(imports.utils.GetParam(params, 0) + ".tar")
+mainPath = imports.file.AbsolutePath(imports.utils.GetParam(params, 0) + ".tar")
 mainFile = comp.File(mainPath)
 
 temp = mainPath.lastIndexOf("/")
@@ -108,7 +128,7 @@ if mainFile == null then
 	
 	// Read all files in order and add them
 	for i in range(1,length-1)
-		path = imports.file.RelativePath(imports.utils.GetParam(params, i))
+		path = imports.file.AbsolutePath(imports.utils.GetParam(params, i))
 		// Get the name of the file
 		name = path[path.lastIndexOf("/")+1:]
 		
@@ -131,7 +151,7 @@ if mainFile == null then
 			content = content + imports.file.NEW_LINE + line
 		end for
 	end for
-
+	
 	mainFile.set_content(content)
 	print("Archive is now available")
 else
@@ -149,40 +169,28 @@ else
 	content = ""
 	nextLine = false
 	
-	CreateFile = function(path, name, content)
-		data = name.split("/")
-		for index in range(0, data.len-2)
-			name = data[index]
-			comp.create_folder(path, name)
-			path = path +"/"+ name
-		end for
-		name = data[data.len-1]
-		comp.touch(path, name)
-		comp.File(path+"/"+name).set_content(content)
-	end function
-	
 	for line in lines
 		temp = ArchiveName(line)
 		// New file
 		if temp != null then
-			if currentName != null then CreateFile(path, currentName, content)
+			if currentName != null then imports.file.save(content, path, currentName)
 			
 			currentName = temp
 			content = ""
 			nextLine = false
 			continue
 		end if
-	
+		
 		if nextLine then
 			content = content + imports.file.NEW_LINE
 		else
 			nextLine = true
 		end if
-	
+		
 		content = content + line
-	
+		
 	end for
 	
-	CreateFile(path, currentName, content)
+	imports.file.save(content, path, currentName)
 	print("Unarchiving completed")
-end if
\ No newline at end of file
+end if
diff --git a/breakhash.src b/scripts/tools/breakhash.gs
similarity index 62%
rename from breakhash.src
rename to scripts/tools/breakhash.gs
index 3fa66da86fc6d8fb3b7f8d02e7d6f1b31a9120f7..98c961b9ee44be680eb38447bd1f9f5011a12e73 100644
--- a/breakhash.src
+++ b/scripts/tools/breakhash.gs
@@ -1,43 +1,4 @@
-
-if not globals.hasIndex("imports") then
-    imports = {}
-    imports.utils = {}
-    
-    imports.utils.Exit = function(reason)
-    	exit(imports.utils.GetProgName()+": "+reason)
-    end function
-    
-    imports.utils.ParamLength = function(params)
-    	length = 0
-    	for param in params
-    		if param[0] != "-" then length = length + 1
-    	end for
-    	return length
-    end function
-    imports.utils.GetParam = function(params, index)
-    	i = 0
-    	for param in params
-    		if param[0] == "-" then continue
-    		if (i == index) then return param
-    		i = i+1
-    	end for
-    	return null
-    end function
-    imports.utils.HasFlag = function(params, short, long)
-    	for param in params
-    		if param[0:1] == "--" then
-    			if slice(param,2) == long then return true
-    		else if param[0] == "-" then
-    			if slice(param,1) == short then return true
-    		end if
-    	end for
-    	return false
-    end function
-    
-    imports.utils.Pause = function()
-    	user_input("Press ENTER to continue")
-    end function
-end if
+"#import libs/utils.inc.src"
 
 //command: breakhash
 length = imports.utils.ParamLength(params)
@@ -103,4 +64,4 @@ for length in range(minLen,maxLen)
 	imports.utils.Pause()
 end for
 
-imports.utils.Exit("Not found under those parameters!")
\ No newline at end of file
+imports.utils.Exit("Not found under those parameters!")
diff --git a/decipher2.src b/scripts/tools/decipher2.src
similarity index 100%
rename from decipher2.src
rename to scripts/tools/decipher2.src
diff --git a/scripts/tools/scanlib2.src b/scripts/tools/scanlib2.src
new file mode 100644
index 0000000000000000000000000000000000000000..19b59b76f4619228e24229dff5f947cebff14965
--- /dev/null
+++ b/scripts/tools/scanlib2.src
@@ -0,0 +1,44 @@
+//command scanlib
+if params.len != 1 or params[0] == "-h" or params[0] == "--help" then exit(command_info("scanlib_usage"))
+metaxploit = include_lib("/lib/metaxploit.so")
+if not metaxploit then exit("Error: Missing metaxploit library")
+libFile = get_shell.host_computer.File(params[0])
+// First modification : remote scanning
+if not libFile then
+	metaLib = null
+	if params[0].indexOf(":") != null then
+		data = params[0].split(":")
+		metaLib = metaxploit.net_use(data[0], data[1].to_int).dump_lib
+	end if
+	if not metaLib then exit("can't find library: " + params[0])
+else
+	metaLib = metaxploit.load(libFile.path)
+end if
+print("Scanning memory address...")
+listMem = metaxploit.scan(metaLib)
+// Second modification : looping in order to reuse the results from the scan+exit on input
+while true
+	print("0: EXIT")
+	index = 1
+	for itemMem in listMem
+		print(index +": [" + itemMem + "]")
+		index = index + 1
+	end for
+	if listMem.len == 0 then exit("Scan completed: No issues detected.")
+
+	print("Scan completed: detected issues in " + listMem.len + " memory zones.")
+	option = ""
+	inputOk = false
+	while( not inputOk )
+		option = user_input("Select memory index: ").to_int
+		if typeof(option) != "number" or (option < 1 or option > listMem.len) then
+			// Third modification : exit on input
+			if option == 0 then exit()
+			print("Invalid input. Type a valid number")
+		else 
+			inputOk = true
+		end if
+	end while
+	print("Scanning for vulnerabilities at memory zone: " + listMem[option - 1])
+	print(metaxploit.scan_address(metaLib, listMem[option - 1]))
+end while