Przeglądaj źródła

Added web root agi excution feature and removed scheudler

Toby Chui 2 lat temu
rodzic
commit
d18e547df2

+ 5 - 2
mod/agi/agi.go

@@ -341,7 +341,7 @@ func (g *Gateway) ExecuteAGIScript(scriptContent string, scriptFile string, scri
 	Execute AGI script with given user information
 	Pass in http.Request pointer to enable serverless GET / POST request
 */
-func (g *Gateway) ExecuteAGIScriptAsUser(scriptFile string, targetUser *user.User, r *http.Request) (string, error) {
+func (g *Gateway) ExecuteAGIScriptAsUser(fsh *filesystem.FileSystemHandler, scriptFile string, targetUser *user.User, r *http.Request) (string, error) {
 	//Create a new vm for this request
 	vm := otto.New()
 	//Inject standard libs into the vm
@@ -380,7 +380,7 @@ func (g *Gateway) ExecuteAGIScriptAsUser(scriptFile string, targetUser *user.Use
 	}()
 
 	//Try to read the script content
-	scriptContent, err := ioutil.ReadFile(scriptFile)
+	scriptContent, err := fsh.FileSystemAbstraction.ReadFile(scriptFile)
 	if err != nil {
 		return "", err
 	}
@@ -397,6 +397,9 @@ func (g *Gateway) ExecuteAGIScriptAsUser(scriptFile string, targetUser *user.Use
 	}
 
 	valueString, err := value.ToString()
+	if err != nil {
+		return "", err
+	}
 	return valueString, nil
 }
 

+ 2 - 2
mod/agi/external.agi.go

@@ -54,7 +54,7 @@ func (g *Gateway) ExtAPIHandler(w http.ResponseWriter, r *http.Request) {
 		common.SendErrorResponse(w, "Invalid username")
 		return
 	}
-	_, realPath, err := virtualPathToRealPath(pathFromDb, userInfo)
+	fsh, realPath, err := virtualPathToRealPath(pathFromDb, userInfo)
 	if err != nil {
 		common.SendErrorResponse(w, "Invalid filepath")
 		return
@@ -63,7 +63,7 @@ func (g *Gateway) ExtAPIHandler(w http.ResponseWriter, r *http.Request) {
 	// execute!
 	start := time.Now()
 	//g.ExecuteAGIScript(scriptContent, "", "", w, r, userInfo)
-	result, err := g.ExecuteAGIScriptAsUser(realPath, userInfo, r)
+	result, err := g.ExecuteAGIScriptAsUser(fsh, realPath, userInfo, r)
 	duration := time.Since(start)
 
 	if err != nil {

+ 402 - 385
mod/agi/systemFunc.go

@@ -1,385 +1,402 @@
-package agi
-
-import (
-	"encoding/csv"
-	"encoding/json"
-	"errors"
-	"io/ioutil"
-	"log"
-	"os/exec"
-	"path/filepath"
-	"strings"
-	"time"
-
-	"github.com/robertkrimen/otto"
-)
-
-//Inject aroz online custom functions into the virtual machine
-func (g *Gateway) injectStandardLibs(vm *otto.Otto, scriptFile string, scriptScope string) {
-	//Define system core modules and definations
-	sysdb := g.Option.UserHandler.GetDatabase()
-
-	//Define VM global variables
-	vm.Set("BUILD_VERSION", g.Option.BuildVersion)
-	vm.Set("INTERNAL_VERSION", g.Option.InternalVersion)
-	vm.Set("LOADED_MODULES", g.Option.LoadedModule)
-	vm.Set("LOADED_STORAGES", g.Option.UserHandler.GetStoragePool())
-	vm.Set("HTTP_RESP", "")
-	vm.Set("HTTP_HEADER", "text/plain")
-
-	//Response related
-	vm.Set("sendResp", func(call otto.FunctionCall) otto.Value {
-		argString, _ := call.Argument(0).ToString()
-		vm.Set("HTTP_RESP", argString)
-		return otto.Value{}
-	})
-
-	vm.Set("sendOK", func(call otto.FunctionCall) otto.Value {
-		vm.Set("HTTP_RESP", "ok")
-		return otto.Value{}
-	})
-
-	vm.Set("_sendJSONResp", func(call otto.FunctionCall) otto.Value {
-		argString, _ := call.Argument(0).ToString()
-		vm.Set("HTTP_HEADER", "application/json")
-		vm.Set("HTTP_RESP", argString)
-		return otto.Value{}
-	})
-
-	vm.Run(`
-		sendJSONResp = function(object){
-			if (typeof(object) === "object"){
-				_sendJSONResp(JSON.stringify(object));
-			}else{
-				_sendJSONResp(object);
-			}
-		}
-	`)
-
-	vm.Set("addNightlyTask", func(call otto.FunctionCall) otto.Value {
-		scriptPath, _ := call.Argument(0).ToString() //From web directory
-		if isValidAGIScript(scriptPath) {
-			g.NightlyScripts = append(g.NightlyScripts, scriptPath)
-		} else {
-			return otto.FalseValue()
-		}
-		return otto.TrueValue()
-	})
-
-	//Database related
-	//newDBTableIfNotExists(tableName)
-	vm.Set("newDBTableIfNotExists", func(call otto.FunctionCall) otto.Value {
-		tableName, err := call.Argument(0).ToString()
-		if err != nil {
-			g.raiseError(err)
-			reply, _ := vm.ToValue(false)
-			return reply
-		}
-		//Create the table with given tableName
-		if g.filterDBTable(tableName, false) {
-			sysdb.NewTable(tableName)
-			//Return true
-			reply, _ := vm.ToValue(true)
-			return reply
-		}
-		reply, _ := vm.ToValue(false)
-		return reply
-	})
-
-	vm.Set("DBTableExists", func(call otto.FunctionCall) otto.Value {
-		tableName, err := call.Argument(0).ToString()
-		if err != nil {
-			g.raiseError(err)
-			reply, _ := vm.ToValue(false)
-			return reply
-		}
-		//Create the table with given tableName
-		if sysdb.TableExists(tableName) {
-			return otto.TrueValue()
-		}
-
-		return otto.FalseValue()
-	})
-
-	//dropDBTable(tablename)
-	vm.Set("dropDBTable", func(call otto.FunctionCall) otto.Value {
-		tableName, err := call.Argument(0).ToString()
-		if err != nil {
-			g.raiseError(err)
-			reply, _ := vm.ToValue(false)
-			return reply
-		}
-		//Create the table with given tableName
-		if g.filterDBTable(tableName, true) {
-			sysdb.DropTable(tableName)
-			reply, _ := vm.ToValue(true)
-			return reply
-		}
-
-		//Return true
-		reply, _ := vm.ToValue(false)
-		return reply
-	})
-
-	//writeDBItem(tablename, key, value) => return true when suceed
-	vm.Set("writeDBItem", func(call otto.FunctionCall) otto.Value {
-		tableName, err := call.Argument(0).ToString()
-		if err != nil {
-			g.raiseError(err)
-			reply, _ := vm.ToValue(false)
-			return reply
-		}
-
-		//Check if the tablename is reserved
-		if g.filterDBTable(tableName, true) {
-			keyString, err := call.Argument(1).ToString()
-			if err != nil {
-				g.raiseError(err)
-				reply, _ := vm.ToValue(false)
-				return reply
-			}
-			valueString, err := call.Argument(2).ToString()
-			if err != nil {
-				g.raiseError(err)
-				reply, _ := vm.ToValue(false)
-				return reply
-			}
-			sysdb.Write(tableName, keyString, valueString)
-			reply, _ := vm.ToValue(true)
-			return reply
-		}
-
-		reply, _ := vm.ToValue(false)
-		return reply
-
-	})
-
-	//readDBItem(tablename, key) => return value
-	vm.Set("readDBItem", func(call otto.FunctionCall) otto.Value {
-		tableName, _ := call.Argument(0).ToString()
-		keyString, _ := call.Argument(1).ToString()
-		returnValue := ""
-		reply, _ := vm.ToValue(nil)
-		if g.filterDBTable(tableName, true) {
-			sysdb.Read(tableName, keyString, &returnValue)
-			r, _ := vm.ToValue(returnValue)
-			reply = r
-		} else {
-			reply = otto.FalseValue()
-		}
-		return reply
-	})
-
-	//listDBTable(tablename) => Return key values array
-	vm.Set("listDBTable", func(call otto.FunctionCall) otto.Value {
-		tableName, _ := call.Argument(0).ToString()
-		returnValue := map[string]string{}
-		reply, _ := vm.ToValue(nil)
-		if g.filterDBTable(tableName, true) {
-			entries, _ := sysdb.ListTable(tableName)
-			for _, keypairs := range entries {
-				//Decode the string
-				result := ""
-				json.Unmarshal(keypairs[1], &result)
-				returnValue[string(keypairs[0])] = result
-			}
-			r, err := vm.ToValue(returnValue)
-			if err != nil {
-				return otto.NullValue()
-			}
-			return r
-		} else {
-			reply = otto.FalseValue()
-		}
-		return reply
-	})
-
-	//deleteDBItem(tablename, key) => Return true if success, false if failed
-	vm.Set("deleteDBItem", func(call otto.FunctionCall) otto.Value {
-		tableName, _ := call.Argument(0).ToString()
-		keyString, _ := call.Argument(1).ToString()
-		if g.filterDBTable(tableName, true) {
-			err := sysdb.Delete(tableName, keyString)
-			if err != nil {
-				return otto.FalseValue()
-			}
-		} else {
-			//Permission denied
-			return otto.FalseValue()
-		}
-
-		return otto.TrueValue()
-	})
-
-	//Module registry
-	vm.Set("registerModule", func(call otto.FunctionCall) otto.Value {
-		jsonModuleConfig, err := call.Argument(0).ToString()
-		if err != nil {
-			g.raiseError(err)
-			reply, _ := vm.ToValue(false)
-			return reply
-		}
-		//Try to decode it to a module Info
-		g.Option.ModuleRegisterParser(jsonModuleConfig)
-		if err != nil {
-			g.raiseError(err)
-			reply, _ := vm.ToValue(false)
-			return reply
-		}
-		return otto.Value{}
-	})
-
-	//Package Executation. Only usable when called to a given script File.
-	if scriptFile != "" && scriptScope != "" {
-		//Package request --> Install linux package if not exists
-		vm.Set("requirepkg", func(call otto.FunctionCall) otto.Value {
-			packageName, err := call.Argument(0).ToString()
-			if err != nil {
-				g.raiseError(err)
-				return otto.FalseValue()
-			}
-			requireComply, err := call.Argument(1).ToBoolean()
-			if err != nil {
-				g.raiseError(err)
-				return otto.FalseValue()
-			}
-
-			scriptRoot := getScriptRoot(scriptFile, scriptScope)
-
-			//Check if this module already get registered.
-			alreadyRegistered := false
-			for _, pkgRequest := range g.AllowAccessPkgs[strings.ToLower(packageName)] {
-				if pkgRequest.InitRoot == scriptRoot {
-					alreadyRegistered = true
-					break
-				}
-			}
-
-			if !alreadyRegistered {
-				//Register this packge to this script and allow the module to call this package
-				g.AllowAccessPkgs[strings.ToLower(packageName)] = append(g.AllowAccessPkgs[strings.ToLower(packageName)], AgiPackage{
-					InitRoot: scriptRoot,
-				})
-			}
-
-			//Try to install the package via apt
-			err = g.Option.PackageManager.InstallIfNotExists(packageName, requireComply)
-			if err != nil {
-				g.raiseError(err)
-				return otto.FalseValue()
-			}
-
-			return otto.TrueValue()
-		})
-
-		//Exec required pkg with permission control
-		vm.Set("execpkg", func(call otto.FunctionCall) otto.Value {
-			//Check if the pkg is already registered
-			scriptRoot := getScriptRoot(scriptFile, scriptScope)
-			packageName, err := call.Argument(0).ToString()
-			if err != nil {
-				g.raiseError(err)
-				return otto.FalseValue()
-			}
-
-			if val, ok := g.AllowAccessPkgs[packageName]; ok {
-				//Package already registered by at least one module. Check if this script root registered
-				thisModuleRegistered := false
-				for _, registeredPkgInterface := range val {
-					if registeredPkgInterface.InitRoot == scriptRoot {
-						//This package registered this command. Allow access
-						thisModuleRegistered = true
-					}
-				}
-
-				if !thisModuleRegistered {
-					g.raiseError(errors.New("Package request not registered: " + packageName))
-					return otto.FalseValue()
-				}
-
-			} else {
-				g.raiseError(errors.New("Package request not registered: " + packageName))
-				return otto.FalseValue()
-			}
-
-			//Ok. Allow paramter to be loaded
-			execParamters, _ := call.Argument(1).ToString()
-
-			// Split input paramters into []string
-			r := csv.NewReader(strings.NewReader(execParamters))
-			r.Comma = ' ' // space
-			fields, err := r.Read()
-			if err != nil {
-				g.raiseError(err)
-				return otto.FalseValue()
-			}
-
-			//Run os.Exec on the given commands
-			cmd := exec.Command(packageName, fields...)
-			out, err := cmd.CombinedOutput()
-			if err != nil {
-				log.Println(string(out))
-				g.raiseError(err)
-				return otto.FalseValue()
-			}
-
-			reply, _ := vm.ToValue(string(out))
-			return reply
-		})
-
-		//Include another js in runtime
-		vm.Set("includes", func(call otto.FunctionCall) otto.Value {
-			//Check if the pkg is already registered
-			scriptName, err := call.Argument(0).ToString()
-			if err != nil {
-				g.raiseError(err)
-				return otto.FalseValue()
-			}
-
-			//Check if it is calling itself
-			if filepath.Base(scriptFile) == filepath.Base(scriptName) {
-				g.raiseError(errors.New("*AGI* Self calling is not allowed"))
-				return otto.FalseValue()
-			}
-
-			//Check if the script file exists
-			targetScriptPath := filepath.ToSlash(filepath.Join(filepath.Dir(scriptFile), scriptName))
-			if !fileExists(targetScriptPath) {
-				g.raiseError(errors.New("*AGI* Target path not exists!"))
-				return otto.FalseValue()
-			}
-
-			//Run the script
-			scriptContent, _ := ioutil.ReadFile(targetScriptPath)
-			_, err = vm.Run(string(scriptContent))
-			if err != nil {
-				//Script execution failed
-				log.Println("Script Execution Failed: ", err.Error())
-				g.raiseError(err)
-				return otto.FalseValue()
-			}
-
-			return otto.TrueValue()
-		})
-
-	}
-
-	//Delay, sleep given ms
-	vm.Set("delay", func(call otto.FunctionCall) otto.Value {
-		delayTime, err := call.Argument(0).ToInteger()
-		if err != nil {
-			g.raiseError(err)
-			return otto.FalseValue()
-		}
-		time.Sleep(time.Duration(delayTime) * time.Millisecond)
-		return otto.TrueValue()
-	})
-
-	//Exit
-	vm.Set("exit", func(call otto.FunctionCall) otto.Value {
-		vm.Interrupt <- func() {
-			panic(exitcall)
-		}
-		return otto.NullValue()
-	})
-}
+package agi
+
+import (
+	"encoding/csv"
+	"encoding/json"
+	"errors"
+	"io/ioutil"
+	"log"
+	"os/exec"
+	"path/filepath"
+	"strings"
+	"time"
+
+	"github.com/robertkrimen/otto"
+)
+
+//Inject aroz online custom functions into the virtual machine
+func (g *Gateway) injectStandardLibs(vm *otto.Otto, scriptFile string, scriptScope string) {
+	//Define system core modules and definations
+	sysdb := g.Option.UserHandler.GetDatabase()
+
+	//Define VM global variables
+	vm.Set("BUILD_VERSION", g.Option.BuildVersion)
+	vm.Set("INTERNAL_VERSION", g.Option.InternalVersion)
+	vm.Set("LOADED_MODULES", g.Option.LoadedModule)
+	vm.Set("LOADED_STORAGES", g.Option.UserHandler.GetStoragePool())
+	vm.Set("HTTP_RESP", "")
+	vm.Set("HTTP_HEADER", "text/plain")
+
+	//Response related
+	vm.Set("sendResp", func(call otto.FunctionCall) otto.Value {
+		argString, _ := call.Argument(0).ToString()
+		vm.Set("HTTP_RESP", argString)
+		return otto.Value{}
+	})
+
+	vm.Set("echo", func(call otto.FunctionCall) otto.Value {
+		argString, _ := call.Argument(0).ToString()
+		currentResp, err := vm.Get("HTTP_RESP")
+		if err != nil {
+			vm.Set("HTTP_RESP", argString)
+		} else {
+			currentRespText, err := currentResp.ToString()
+			if err != nil {
+				//Unable to parse this as string. Overwrite response
+				vm.Set("HTTP_RESP", argString)
+			}
+			vm.Set("HTTP_RESP", currentRespText+argString)
+		}
+
+		return otto.Value{}
+	})
+
+	vm.Set("sendOK", func(call otto.FunctionCall) otto.Value {
+		vm.Set("HTTP_RESP", "ok")
+		return otto.Value{}
+	})
+
+	vm.Set("_sendJSONResp", func(call otto.FunctionCall) otto.Value {
+		argString, _ := call.Argument(0).ToString()
+		vm.Set("HTTP_HEADER", "application/json")
+		vm.Set("HTTP_RESP", argString)
+		return otto.Value{}
+	})
+
+	vm.Run(`
+		sendJSONResp = function(object){
+			if (typeof(object) === "object"){
+				_sendJSONResp(JSON.stringify(object));
+			}else{
+				_sendJSONResp(object);
+			}
+		}
+	`)
+
+	vm.Set("addNightlyTask", func(call otto.FunctionCall) otto.Value {
+		scriptPath, _ := call.Argument(0).ToString() //From web directory
+		if isValidAGIScript(scriptPath) {
+			g.NightlyScripts = append(g.NightlyScripts, scriptPath)
+		} else {
+			return otto.FalseValue()
+		}
+		return otto.TrueValue()
+	})
+
+	//Database related
+	//newDBTableIfNotExists(tableName)
+	vm.Set("newDBTableIfNotExists", func(call otto.FunctionCall) otto.Value {
+		tableName, err := call.Argument(0).ToString()
+		if err != nil {
+			g.raiseError(err)
+			reply, _ := vm.ToValue(false)
+			return reply
+		}
+		//Create the table with given tableName
+		if g.filterDBTable(tableName, false) {
+			sysdb.NewTable(tableName)
+			//Return true
+			reply, _ := vm.ToValue(true)
+			return reply
+		}
+		reply, _ := vm.ToValue(false)
+		return reply
+	})
+
+	vm.Set("DBTableExists", func(call otto.FunctionCall) otto.Value {
+		tableName, err := call.Argument(0).ToString()
+		if err != nil {
+			g.raiseError(err)
+			reply, _ := vm.ToValue(false)
+			return reply
+		}
+		//Create the table with given tableName
+		if sysdb.TableExists(tableName) {
+			return otto.TrueValue()
+		}
+
+		return otto.FalseValue()
+	})
+
+	//dropDBTable(tablename)
+	vm.Set("dropDBTable", func(call otto.FunctionCall) otto.Value {
+		tableName, err := call.Argument(0).ToString()
+		if err != nil {
+			g.raiseError(err)
+			reply, _ := vm.ToValue(false)
+			return reply
+		}
+		//Create the table with given tableName
+		if g.filterDBTable(tableName, true) {
+			sysdb.DropTable(tableName)
+			reply, _ := vm.ToValue(true)
+			return reply
+		}
+
+		//Return true
+		reply, _ := vm.ToValue(false)
+		return reply
+	})
+
+	//writeDBItem(tablename, key, value) => return true when suceed
+	vm.Set("writeDBItem", func(call otto.FunctionCall) otto.Value {
+		tableName, err := call.Argument(0).ToString()
+		if err != nil {
+			g.raiseError(err)
+			reply, _ := vm.ToValue(false)
+			return reply
+		}
+
+		//Check if the tablename is reserved
+		if g.filterDBTable(tableName, true) {
+			keyString, err := call.Argument(1).ToString()
+			if err != nil {
+				g.raiseError(err)
+				reply, _ := vm.ToValue(false)
+				return reply
+			}
+			valueString, err := call.Argument(2).ToString()
+			if err != nil {
+				g.raiseError(err)
+				reply, _ := vm.ToValue(false)
+				return reply
+			}
+			sysdb.Write(tableName, keyString, valueString)
+			reply, _ := vm.ToValue(true)
+			return reply
+		}
+
+		reply, _ := vm.ToValue(false)
+		return reply
+
+	})
+
+	//readDBItem(tablename, key) => return value
+	vm.Set("readDBItem", func(call otto.FunctionCall) otto.Value {
+		tableName, _ := call.Argument(0).ToString()
+		keyString, _ := call.Argument(1).ToString()
+		returnValue := ""
+		reply, _ := vm.ToValue(nil)
+		if g.filterDBTable(tableName, true) {
+			sysdb.Read(tableName, keyString, &returnValue)
+			r, _ := vm.ToValue(returnValue)
+			reply = r
+		} else {
+			reply = otto.FalseValue()
+		}
+		return reply
+	})
+
+	//listDBTable(tablename) => Return key values array
+	vm.Set("listDBTable", func(call otto.FunctionCall) otto.Value {
+		tableName, _ := call.Argument(0).ToString()
+		returnValue := map[string]string{}
+		reply, _ := vm.ToValue(nil)
+		if g.filterDBTable(tableName, true) {
+			entries, _ := sysdb.ListTable(tableName)
+			for _, keypairs := range entries {
+				//Decode the string
+				result := ""
+				json.Unmarshal(keypairs[1], &result)
+				returnValue[string(keypairs[0])] = result
+			}
+			r, err := vm.ToValue(returnValue)
+			if err != nil {
+				return otto.NullValue()
+			}
+			return r
+		} else {
+			reply = otto.FalseValue()
+		}
+		return reply
+	})
+
+	//deleteDBItem(tablename, key) => Return true if success, false if failed
+	vm.Set("deleteDBItem", func(call otto.FunctionCall) otto.Value {
+		tableName, _ := call.Argument(0).ToString()
+		keyString, _ := call.Argument(1).ToString()
+		if g.filterDBTable(tableName, true) {
+			err := sysdb.Delete(tableName, keyString)
+			if err != nil {
+				return otto.FalseValue()
+			}
+		} else {
+			//Permission denied
+			return otto.FalseValue()
+		}
+
+		return otto.TrueValue()
+	})
+
+	//Module registry
+	vm.Set("registerModule", func(call otto.FunctionCall) otto.Value {
+		jsonModuleConfig, err := call.Argument(0).ToString()
+		if err != nil {
+			g.raiseError(err)
+			reply, _ := vm.ToValue(false)
+			return reply
+		}
+		//Try to decode it to a module Info
+		g.Option.ModuleRegisterParser(jsonModuleConfig)
+		if err != nil {
+			g.raiseError(err)
+			reply, _ := vm.ToValue(false)
+			return reply
+		}
+		return otto.Value{}
+	})
+
+	//Package Executation. Only usable when called to a given script File.
+	if scriptFile != "" && scriptScope != "" {
+		//Package request --> Install linux package if not exists
+		vm.Set("requirepkg", func(call otto.FunctionCall) otto.Value {
+			packageName, err := call.Argument(0).ToString()
+			if err != nil {
+				g.raiseError(err)
+				return otto.FalseValue()
+			}
+			requireComply, err := call.Argument(1).ToBoolean()
+			if err != nil {
+				g.raiseError(err)
+				return otto.FalseValue()
+			}
+
+			scriptRoot := getScriptRoot(scriptFile, scriptScope)
+
+			//Check if this module already get registered.
+			alreadyRegistered := false
+			for _, pkgRequest := range g.AllowAccessPkgs[strings.ToLower(packageName)] {
+				if pkgRequest.InitRoot == scriptRoot {
+					alreadyRegistered = true
+					break
+				}
+			}
+
+			if !alreadyRegistered {
+				//Register this packge to this script and allow the module to call this package
+				g.AllowAccessPkgs[strings.ToLower(packageName)] = append(g.AllowAccessPkgs[strings.ToLower(packageName)], AgiPackage{
+					InitRoot: scriptRoot,
+				})
+			}
+
+			//Try to install the package via apt
+			err = g.Option.PackageManager.InstallIfNotExists(packageName, requireComply)
+			if err != nil {
+				g.raiseError(err)
+				return otto.FalseValue()
+			}
+
+			return otto.TrueValue()
+		})
+
+		//Exec required pkg with permission control
+		vm.Set("execpkg", func(call otto.FunctionCall) otto.Value {
+			//Check if the pkg is already registered
+			scriptRoot := getScriptRoot(scriptFile, scriptScope)
+			packageName, err := call.Argument(0).ToString()
+			if err != nil {
+				g.raiseError(err)
+				return otto.FalseValue()
+			}
+
+			if val, ok := g.AllowAccessPkgs[packageName]; ok {
+				//Package already registered by at least one module. Check if this script root registered
+				thisModuleRegistered := false
+				for _, registeredPkgInterface := range val {
+					if registeredPkgInterface.InitRoot == scriptRoot {
+						//This package registered this command. Allow access
+						thisModuleRegistered = true
+					}
+				}
+
+				if !thisModuleRegistered {
+					g.raiseError(errors.New("Package request not registered: " + packageName))
+					return otto.FalseValue()
+				}
+
+			} else {
+				g.raiseError(errors.New("Package request not registered: " + packageName))
+				return otto.FalseValue()
+			}
+
+			//Ok. Allow paramter to be loaded
+			execParamters, _ := call.Argument(1).ToString()
+
+			// Split input paramters into []string
+			r := csv.NewReader(strings.NewReader(execParamters))
+			r.Comma = ' ' // space
+			fields, err := r.Read()
+			if err != nil {
+				g.raiseError(err)
+				return otto.FalseValue()
+			}
+
+			//Run os.Exec on the given commands
+			cmd := exec.Command(packageName, fields...)
+			out, err := cmd.CombinedOutput()
+			if err != nil {
+				log.Println(string(out))
+				g.raiseError(err)
+				return otto.FalseValue()
+			}
+
+			reply, _ := vm.ToValue(string(out))
+			return reply
+		})
+
+		//Include another js in runtime
+		vm.Set("includes", func(call otto.FunctionCall) otto.Value {
+			//Check if the pkg is already registered
+			scriptName, err := call.Argument(0).ToString()
+			if err != nil {
+				g.raiseError(err)
+				return otto.FalseValue()
+			}
+
+			//Check if it is calling itself
+			if filepath.Base(scriptFile) == filepath.Base(scriptName) {
+				g.raiseError(errors.New("*AGI* Self calling is not allowed"))
+				return otto.FalseValue()
+			}
+
+			//Check if the script file exists
+			targetScriptPath := filepath.ToSlash(filepath.Join(filepath.Dir(scriptFile), scriptName))
+			if !fileExists(targetScriptPath) {
+				g.raiseError(errors.New("*AGI* Target path not exists!"))
+				return otto.FalseValue()
+			}
+
+			//Run the script
+			scriptContent, _ := ioutil.ReadFile(targetScriptPath)
+			_, err = vm.Run(string(scriptContent))
+			if err != nil {
+				//Script execution failed
+				log.Println("Script Execution Failed: ", err.Error())
+				g.raiseError(err)
+				return otto.FalseValue()
+			}
+
+			return otto.TrueValue()
+		})
+
+	}
+
+	//Delay, sleep given ms
+	vm.Set("delay", func(call otto.FunctionCall) otto.Value {
+		delayTime, err := call.Argument(0).ToInteger()
+		if err != nil {
+			g.raiseError(err)
+			return otto.FalseValue()
+		}
+		time.Sleep(time.Duration(delayTime) * time.Millisecond)
+		return otto.TrueValue()
+	})
+
+	//Exit
+	vm.Set("exit", func(call otto.FunctionCall) otto.Value {
+		vm.Interrupt <- func() {
+			panic(exitcall)
+		}
+		return otto.NullValue()
+	})
+}

BIN
mod/time/scheduler.zip


+ 0 - 171
mod/time/scheduler/common.go

@@ -1,171 +0,0 @@
-package scheduler
-
-import (
-	"bufio"
-	"encoding/base64"
-	"errors"
-	"io/ioutil"
-	"log"
-	"net/http"
-	"os"
-	"time"
-)
-
-/*
-	SYSTEM COMMON FUNCTIONS
-
-	This is a system function that put those we usually use function but not belongs to
-	any module / system.
-
-	E.g. fileExists / IsDir etc
-
-*/
-
-/*
-	Basic Response Functions
-
-	Send response with ease
-*/
-//Send text response with given w and message as string
-func sendTextResponse(w http.ResponseWriter, msg string) {
-	w.Write([]byte(msg))
-}
-
-//Send JSON response, with an extra json header
-func sendJSONResponse(w http.ResponseWriter, json string) {
-	w.Header().Set("Content-Type", "application/json")
-	w.Write([]byte(json))
-}
-
-func sendErrorResponse(w http.ResponseWriter, errMsg string) {
-	w.Header().Set("Content-Type", "application/json")
-	w.Write([]byte("{\"error\":\"" + errMsg + "\"}"))
-}
-
-func sendOK(w http.ResponseWriter) {
-	w.Header().Set("Content-Type", "application/json")
-	w.Write([]byte("\"OK\""))
-}
-
-/*
-	The paramter move function (mv)
-
-	You can find similar things in the PHP version of ArOZ Online Beta. You need to pass in
-	r (HTTP Request Object)
-	getParamter (string, aka $_GET['This string])
-
-	Will return
-	Paramter string (if any)
-	Error (if error)
-
-*/
-func mv(r *http.Request, getParamter string, postMode bool) (string, error) {
-	if postMode == false {
-		//Access the paramter via GET
-		keys, ok := r.URL.Query()[getParamter]
-
-		if !ok || len(keys[0]) < 1 {
-			//log.Println("Url Param " + getParamter +" is missing")
-			return "", errors.New("GET paramter " + getParamter + " not found or it is empty")
-		}
-
-		// Query()["key"] will return an array of items,
-		// we only want the single item.
-		key := keys[0]
-		return string(key), nil
-	} else {
-		//Access the parameter via POST
-		r.ParseForm()
-		x := r.Form.Get(getParamter)
-		if len(x) == 0 || x == "" {
-			return "", errors.New("POST paramter " + getParamter + " not found or it is empty")
-		}
-		return string(x), nil
-	}
-
-}
-
-func stringInSlice(a string, list []string) bool {
-	for _, b := range list {
-		if b == a {
-			return true
-		}
-	}
-	return false
-}
-
-func fileExists(filename string) bool {
-	_, err := os.Stat(filename)
-	if os.IsNotExist(err) {
-		return false
-	}
-	return true
-}
-
-func isDir(path string) bool {
-	if fileExists(path) == false {
-		return false
-	}
-	fi, err := os.Stat(path)
-	if err != nil {
-		log.Fatal(err)
-		return false
-	}
-	switch mode := fi.Mode(); {
-	case mode.IsDir():
-		return true
-	case mode.IsRegular():
-		return false
-	}
-	return false
-}
-
-func inArray(arr []string, str string) bool {
-	for _, a := range arr {
-		if a == str {
-			return true
-		}
-	}
-	return false
-}
-
-func timeToString(targetTime time.Time) string {
-	return targetTime.Format("2006-01-02 15:04:05")
-}
-
-func loadImageAsBase64(filepath string) (string, error) {
-	if !fileExists(filepath) {
-		return "", errors.New("File not exists")
-	}
-	f, _ := os.Open(filepath)
-	reader := bufio.NewReader(f)
-	content, _ := ioutil.ReadAll(reader)
-	encoded := base64.StdEncoding.EncodeToString(content)
-	return string(encoded), nil
-}
-
-func pushToSliceIfNotExist(slice []string, newItem string) []string {
-	itemExists := false
-	for _, item := range slice {
-		if item == newItem {
-			itemExists = true
-		}
-	}
-
-	if !itemExists {
-		slice = append(slice, newItem)
-	}
-
-	return slice
-}
-
-func removeFromSliceIfExists(slice []string, target string) []string {
-	newSlice := []string{}
-	for _, item := range slice {
-		if item != target {
-			newSlice = append(newSlice, item)
-		}
-	}
-
-	return newSlice
-}

+ 0 - 243
mod/time/scheduler/handlers.go

@@ -1,243 +0,0 @@
-package scheduler
-
-import (
-	"encoding/json"
-	"io/ioutil"
-	"net/http"
-	"path/filepath"
-	"strconv"
-	"strings"
-	"time"
-
-	"github.com/tidwall/pretty"
-)
-
-//List all the jobs related to the given user
-func (a *Scheduler) HandleListJobs(w http.ResponseWriter, r *http.Request) {
-	userinfo, err := a.userHandler.GetUserInfoFromRequest(w, r)
-	if err != nil {
-		sendErrorResponse(w, "User not logged in")
-		return
-	}
-
-	//Get username from user info
-	username := userinfo.Username
-	isAdmin := userinfo.IsAdmin()
-
-	//Check if the user request list all
-	listAll := false
-	la, _ := mv(r, "listall", false)
-	if la == "true" && isAdmin {
-		listAll = true
-	}
-
-	//Find the scheduled task that belongs to this user
-	userCreatedJobs := []*Job{}
-
-	for _, thisJob := range a.jobs {
-		if listAll {
-			//List all the user jobs.
-			userCreatedJobs = append(userCreatedJobs, thisJob)
-		} else {
-			//Only list user's job
-			if thisJob.Creator == username {
-				userCreatedJobs = append(userCreatedJobs, thisJob)
-			}
-		}
-
-	}
-
-	//Return the values as json
-	js, err := json.Marshal(userCreatedJobs)
-	sendJSONResponse(w, string(js))
-}
-
-func (a *Scheduler) HandleAddJob(w http.ResponseWriter, r *http.Request) {
-	userinfo, err := a.userHandler.GetUserInfoFromRequest(w, r)
-	if err != nil {
-		sendErrorResponse(w, "User not logged in")
-		return
-	}
-
-	//Get required paramaters
-	taskName, err := mv(r, "name", true)
-	if err != nil {
-		sendErrorResponse(w, "Invalid task name")
-		return
-	}
-
-	//Check taskname length valid
-	if len(taskName) > 32 {
-		sendErrorResponse(w, "Task name must be shorter than 32 characters")
-		return
-	}
-
-	//Check if the name already existsed
-	for _, runningJob := range a.jobs {
-		if runningJob.Name == taskName {
-			sendErrorResponse(w, "Task Name already occupied")
-			return
-		}
-	}
-
-	scriptpath, err := mv(r, "path", true)
-	if err != nil {
-		sendErrorResponse(w, "Invalid script path")
-		return
-	}
-
-	//Can be empty
-	jobDescription, _ := mv(r, "desc", true)
-	fsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(scriptpath)
-	if err != nil {
-		sendErrorResponse(w, err.Error())
-		return
-	}
-	fshAbs := fsh.FileSystemAbstraction
-	realScriptPath, err := fshAbs.VirtualPathToRealPath(scriptpath, userinfo.Username)
-	if err != nil {
-		sendErrorResponse(w, err.Error())
-		return
-	}
-
-	//Check if the user has permission to create this script
-	if filepath.Ext(realScriptPath) != ".js" && filepath.Ext(realScriptPath) != ".agi" {
-		//Require admin permission
-		if !userinfo.IsAdmin() {
-			sendErrorResponse(w, "Admin permission required for scheduling non AGI script")
-			return
-		}
-	}
-
-	interval := int64(86400) //default 1 day in seconds
-	intervalString, err := mv(r, "interval", true)
-	if err != nil {
-		//Default 1 day
-
-	} else {
-		//Parse the intervalString into int
-		intervalInt, err := strconv.ParseInt(intervalString, 10, 64)
-		if err != nil {
-			//Failed to parse interval to int
-			sendErrorResponse(w, "Invalid interval")
-			return
-		}
-
-		interval = intervalInt
-	}
-
-	baseUnixTime := time.Now().Unix()
-	baseTimeString, err := mv(r, "base", true)
-	if err != nil {
-		//Use curent timestamp as base
-
-	} else {
-		baseTimeInt, err := strconv.Atoi(baseTimeString)
-		if err != nil {
-			//Failed to parse interval to int
-			sendErrorResponse(w, "Invalid Base Time")
-			return
-		}
-
-		baseUnixTime = int64(baseTimeInt)
-	}
-
-	//Create a new job
-	newJob := Job{
-		Name:              taskName,
-		Creator:           userinfo.Username,
-		Admin:             userinfo.IsAdmin(),
-		Description:       jobDescription,
-		ExecutionInterval: int64(interval),
-		BaseTime:          baseUnixTime,
-		ScriptFile:        realScriptPath,
-		FshID:             fsh.UUID,
-	}
-
-	//Write current job lists to file
-	a.jobs = append(a.jobs, &newJob)
-
-	js, _ := json.MarshalIndent(a.jobs, "", " ")
-
-	ioutil.WriteFile(a.cronfile, js, 0755)
-
-	//OK
-	sendOK(w)
-}
-
-func (a *Scheduler) HandleJobRemoval(w http.ResponseWriter, r *http.Request) {
-	userinfo, err := a.userHandler.GetUserInfoFromRequest(w, r)
-	if err != nil {
-		sendErrorResponse(w, "User not logged in")
-		return
-	}
-
-	//Get required paramaters
-	taskName, err := mv(r, "name", true)
-	if err != nil {
-		sendErrorResponse(w, "Invalid task name")
-		return
-	}
-
-	//Check if Job exists
-	if !a.JobExists(taskName) {
-		//Job with that name not exists
-		sendErrorResponse(w, "Job not exists")
-		return
-	}
-
-	targetJob := a.GetScheduledJobByName(taskName)
-
-	//Job exists. Check if the job is created by the user.
-	//User can only remove job created by himself or all job is he is admin
-	allowRemove := false
-	if userinfo.IsAdmin() == false && targetJob.Creator == userinfo.Username {
-		allowRemove = true
-	} else if userinfo.IsAdmin() == true {
-		allowRemove = true
-	}
-
-	if !allowRemove {
-		sendErrorResponse(w, "Permission Denied")
-		return
-	}
-
-	//Ok. Remove Job from the list
-	a.RemoveJobFromScheduleList(taskName)
-
-	//Write current job lists to file
-	js, _ := json.Marshal(a.jobs)
-	js = []byte(pretty.Pretty(js))
-	ioutil.WriteFile(a.cronfile, js, 0755)
-
-	sendOK(w)
-}
-
-func (a *Scheduler) HandleShowLog(w http.ResponseWriter, r *http.Request) {
-	filename, _ := mv(r, "filename", false)
-	if filename == "" {
-		//Show index
-		logFiles, _ := filepath.Glob(logFolder + "*.log")
-
-		//Convert all to linux syntax
-		linuxLogFiles := []string{}
-		for _, lf := range logFiles {
-			linuxLogFiles = append(linuxLogFiles, filepath.Base(lf))
-		}
-		js, _ := json.Marshal(linuxLogFiles)
-		sendJSONResponse(w, string(js))
-	} else {
-		//Show log content
-		filename = strings.ReplaceAll(filepath.ToSlash(filename), "/", "")
-		if fileExists(filepath.Join(logFolder, filename)) {
-			logContent, err := ioutil.ReadFile(filepath.Join(logFolder, filename))
-			if err != nil {
-				sendTextResponse(w, "Unable to load log file: "+filename)
-			} else {
-				sendTextResponse(w, string(logContent))
-			}
-		} else {
-			sendTextResponse(w, "Unable to load log file: "+filename)
-		}
-	}
-}

+ 0 - 312
mod/time/scheduler/scheduler.go

@@ -1,312 +0,0 @@
-package scheduler
-
-import (
-	"encoding/json"
-	"errors"
-	"io/ioutil"
-	"log"
-	"os"
-	"os/exec"
-	"path/filepath"
-	"runtime"
-	"strings"
-	"time"
-
-	"imuslab.com/arozos/mod/agi"
-	"imuslab.com/arozos/mod/user"
-)
-
-/*
-	ArozOS System Scheduler
-	author: tobychui
-
-	This module provide scheduling executable feature for ArozOS
-	Some feature was migrated from the v1.113 aecron module
-*/
-
-type Job struct {
-	Name              string //The name of this job
-	Creator           string //The creator of this job. When execute, this user permission will be used
-	Description       string //Job description, can be empty
-	Admin             bool   //If the creator has admin permission during the creation of this job. If this doesn't match with the runtime instance, this job wille be skipped
-	ExecutionInterval int64  //Execuation interval in seconds
-	BaseTime          int64  //Exeuction basetime. The next interval is calculated using (current time - base time ) % execution interval
-	JobType           string //Job type, accept {file/function}. If not set default to file
-	FshID             string
-	ScriptFile        string                 //The script file being called. Can be an agi script (.agi / .js) or shell script (.bat or .sh)
-	ScriptFunc        func() (string, error) `json:"-"` //The target function to execute
-}
-
-type Scheduler struct {
-	jobs        []*Job
-	cronfile    string
-	userHandler *user.UserHandler
-	gateway     *agi.Gateway
-	ticker      chan bool
-}
-
-var (
-	logFolder string = "./system/aecron/"
-)
-
-func NewScheduler(userHandler *user.UserHandler, gateway *agi.Gateway, cronfile string) (*Scheduler, error) {
-	if !fileExists(cronfile) {
-		//Cronfile not exists. Create it
-		emptyJobList := []*Job{}
-		ls, _ := json.Marshal(emptyJobList)
-		err := ioutil.WriteFile(cronfile, ls, 0755)
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	//Load previous jobs from file
-	jobs, err := loadJobsFromFile(cronfile)
-	if err != nil {
-		return nil, err
-	}
-
-	//Create the ArOZ Emulated Crontask
-	thisScheduler := Scheduler{
-		jobs:        jobs,
-		userHandler: userHandler,
-		gateway:     gateway,
-		cronfile:    cronfile,
-	}
-
-	//Create log folder
-	os.MkdirAll(logFolder, 0755)
-
-	//Start the cronjob at 1 minute ticker interval
-	go func() {
-		//Delay start: Wait until seconds = 0
-		for time.Now().Unix()%60 > 0 {
-			time.Sleep(500 * time.Millisecond)
-		}
-		stopChannel := thisScheduler.createTicker(1 * time.Minute)
-		thisScheduler.ticker = stopChannel
-		log.Println("ArozOS System Scheduler Started")
-	}()
-
-	//Return the crontask
-	return &thisScheduler, nil
-}
-
-//Load a list of jobs from file
-func loadJobsFromFile(cronfile string) ([]*Job, error) {
-	//Try to read the cronfile
-	filecontent, err := ioutil.ReadFile(cronfile)
-	if err != nil {
-		return []*Job{}, err
-	}
-
-	//Phrase the cronfile
-	prevousJobs := []Job{}
-	err = json.Unmarshal(filecontent, &prevousJobs)
-	if err != nil {
-		return []*Job{}, err
-	}
-
-	//Convert the json objets to pointer for easy changing by other process
-	jobsPointers := []*Job{}
-	for _, thisJob := range prevousJobs {
-		thisJob.JobType = "file"
-		var newJobPointer Job = thisJob
-		jobsPointers = append(jobsPointers, &newJobPointer)
-	}
-
-	return jobsPointers, nil
-}
-
-func (a *Scheduler) createTicker(duration time.Duration) chan bool {
-	ticker := time.NewTicker(duration)
-	stop := make(chan bool, 1)
-
-	go func() {
-		defer log.Println("Scheduler Stopped")
-		for {
-			select {
-			case <-ticker.C:
-				//Run jobs
-				for _, thisJob := range a.jobs {
-					if (time.Now().Unix()-thisJob.BaseTime)%thisJob.ExecutionInterval == 0 {
-						//Execute this job
-						if thisJob.JobType == "function" {
-							//Execute the script function
-							returnvalue, err := thisJob.ScriptFunc()
-							if err != nil {
-								//Execution error. Kill this scheule
-								log.Println(`*Scheduler* Error occured when running task ` + thisJob.Name + ": " + err.Error())
-								a.RemoveJobFromScheduleList(thisJob.Name)
-								cronlog("[ERROR]: " + err.Error())
-							}
-
-							//Execution suceed. Log the return value
-							if len(returnvalue) > 0 {
-								cronlog(returnvalue)
-							}
-
-						} else {
-							//This is requesting to execute a script file
-							scriptFile := thisJob.ScriptFile
-							if !fileExists(scriptFile) {
-								//This job no longer exists in the file system. Remove it
-								a.RemoveJobFromScheduleList(thisJob.Name)
-							}
-							clonedJobStructure := *thisJob
-							ext := filepath.Ext(scriptFile)
-							if ext == ".js" || ext == ".agi" {
-								//Run using AGI interface in go routine
-								go func(thisJob Job) {
-									userinfo, err := a.userHandler.GetUserInfoFromUsername(thisJob.Creator)
-									if err != nil {
-										//This user not exists. Skip this script
-										cronlog("[ERROR] User not exists: " + thisJob.Creator + ". Skipping scheduled job: " + thisJob.Name + ".")
-										return
-									}
-
-									//Run the script with this user scope
-									resp, err := a.gateway.ExecuteAGIScriptAsUser(thisJob.ScriptFile, userinfo, nil)
-									if err != nil {
-										cronlog("[ERROR] " + thisJob.Name + " " + err.Error())
-									} else {
-										cronlog(thisJob.Name + " " + resp)
-									}
-								}(clonedJobStructure)
-
-							} else if ext == ".bat" || ext == ".sh" {
-								//Run as shell script
-								go func(thisJob Job) {
-									scriptPath := thisJob.ScriptFile
-									if runtime.GOOS == "windows" {
-										scriptPath = strings.ReplaceAll(filepath.ToSlash(scriptPath), "/", "\\")
-									}
-									cmd := exec.Command(scriptPath)
-									out, err := cmd.CombinedOutput()
-									if err != nil {
-										cronlog("[ERROR] " + thisJob.Name + " " + err.Error() + " => " + string(out))
-									}
-									cronlog(thisJob.Name + " " + string(out))
-								}(clonedJobStructure)
-							} else {
-								//Unknown script file. Ignore this
-								log.Println("This extension is not yet supported: ", ext)
-							}
-						}
-
-					}
-				}
-			case <-stop:
-				return
-			}
-		}
-	}()
-
-	return stop
-}
-
-func (a *Scheduler) Close() {
-	if a.ticker != nil {
-		//Stop the ticker
-		a.ticker <- true
-	}
-}
-
-//Add an job object to system scheduler
-func (a *Scheduler) AddJobToScheduler(job *Job) error {
-	if job.JobType == "" {
-		if job.ScriptFunc == nil && job.ScriptFile == "" {
-			return errors.New("Invalid job file or function")
-		}
-
-		if job.ScriptFunc != nil {
-			job.JobType = "function"
-		} else if job.ScriptFile != "" {
-			job.JobType = "file"
-		}
-
-	}
-	a.jobs = append(a.jobs, job)
-	return nil
-}
-
-//Create a new scheduled function job in the scheduler
-func (a *Scheduler) CreateNewScheduledFunctionJob(name string, desc string, executionInterval int64, targetFunction func() (string, error)) error {
-	if name == "" || desc == "" {
-		return errors.New("Name or description of a scheduled task cannot be empty")
-	}
-
-	if executionInterval < 60 {
-		return errors.New("The minimum execution interval is 60 seconds.")
-	}
-
-	//Get the cloest minute
-	baseTime := time.Now().Unix() - (time.Now().Unix() % 60)
-
-	//Create a new scehduled job
-	newJob := Job{
-		Name:              name,
-		Creator:           "system",
-		Description:       desc,
-		Admin:             true,
-		ExecutionInterval: executionInterval,
-		BaseTime:          baseTime,
-		JobType:           "function",
-		ScriptFunc:        targetFunction,
-	}
-
-	//Add the new job to scheduler
-	a.AddJobToScheduler(&newJob)
-
-	return nil
-}
-
-func (a *Scheduler) GetScheduledJobByName(name string) *Job {
-	for _, thisJob := range a.jobs {
-		if thisJob.Name == name {
-			return thisJob
-		}
-	}
-
-	return nil
-}
-
-func (a *Scheduler) RemoveJobFromScheduleList(taskName string) {
-	newJobSlice := []*Job{}
-	for _, j := range a.jobs {
-		if j.Name != taskName {
-			thisJob := j
-			newJobSlice = append(newJobSlice, thisJob)
-		}
-	}
-	a.jobs = newJobSlice
-}
-
-func (a *Scheduler) JobExists(name string) bool {
-	targetJob := a.GetScheduledJobByName(name)
-	if targetJob == nil {
-		return false
-	} else {
-		return true
-	}
-}
-
-//Write the output to log file. Default to ./system/aecron/{date}.log
-func cronlog(message string) {
-	currentTime := time.Now()
-	timestamp := currentTime.Format("2006-01-02 15:04:05")
-	message = timestamp + " " + message
-	currentLogFile := filepath.ToSlash(filepath.Clean(logFolder)) + "/" + time.Now().Format("2006-02-01") + ".log"
-	f, err := os.OpenFile(currentLogFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
-	if err != nil {
-		//Unable to write to file. Log to STDOUT instead
-		log.Println(message)
-		return
-	}
-	if _, err := f.WriteString(message + "\n"); err != nil {
-		log.Println(message)
-		return
-	}
-	defer f.Close()
-
-}

+ 190 - 176
mod/www/www.go

@@ -1,176 +1,190 @@
-package www
-
-import (
-	"io"
-	"io/ioutil"
-	"net/http"
-	"net/url"
-	"path/filepath"
-	"strings"
-
-	"imuslab.com/arozos/mod/database"
-	"imuslab.com/arozos/mod/user"
-)
-
-/*
-	www package
-
-	This package is the replacement handler for global homepage function in ArozOS.
-	This allow users to host and create their website using any folder within the user
-	access file system.
-
-*/
-
-type Options struct {
-	UserHandler *user.UserHandler
-	Database    *database.Database
-}
-
-type Handler struct {
-	Options Options
-}
-
-/*
-	New WebRoot Handler create a new handler for handling and routing webroots
-*/
-func NewWebRootHandler(options Options) *Handler {
-	//Create the homepage database table
-	options.Database.NewTable("www")
-
-	//Return the handler object
-	return &Handler{
-		Options: options,
-	}
-}
-
-func (h *Handler) RouteRequest(w http.ResponseWriter, r *http.Request) {
-	//Check if it is reaching www root folder or any files directly under www.
-	if filepath.ToSlash(filepath.Clean(r.URL.Path)) == "/www" {
-		//Direct access of the root folder. Serve the homepage description.
-		http.ServeFile(w, r, "web/SystemAO/www/index.html")
-		return
-	} else if filepath.ToSlash(filepath.Dir(r.URL.Path)) == "/www" {
-		//Missing the last / at the end of the path
-		r.URL.Path = r.URL.Path + "/"
-		http.Redirect(w, r, filepath.ToSlash(filepath.Dir(r.URL.Path))+"/", http.StatusTemporaryRedirect)
-		return
-	}
-
-	//Escape the URL
-	decodedValue, err := url.QueryUnescape(r.URL.Path)
-	if err != nil {
-		//failed to decode. Just use its raw value
-		decodedValue = r.URL.Path
-	}
-
-	//Check the user name of the user root
-	parsedRequestURL := strings.Split(filepath.ToSlash(filepath.Clean(decodedValue)[1:]), "/")
-	//Malparsed URL. Ignore request
-	if len(parsedRequestURL) < 2 {
-		serveNotFoundTemplate(w, r)
-		return
-	}
-
-	//Extract user information
-	username := parsedRequestURL[1]
-
-	userinfo, err := h.Options.UserHandler.GetUserInfoFromUsername(username)
-	if err != nil {
-		serveNotFoundTemplate(w, r)
-		return
-	}
-
-	//Check if this user enabled homepage
-	enabled := h.CheckUserHomePageEnabled(userinfo.Username)
-	if !enabled {
-		serveNotFoundTemplate(w, r)
-		return
-	}
-
-	//Check if the user have his webroot correctly configured
-	webroot, err := h.GetUserWebRoot(userinfo.Username)
-	if err != nil {
-		//User do not have a correctly configured webroot. Serve instruction
-		handleWebrootError(w)
-		return
-	}
-
-	//User webroot real path conversion
-	fsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(webroot)
-	if err != nil {
-		handleWebrootError(w)
-		return
-	}
-
-	webrootRealpath, err := fsh.FileSystemAbstraction.VirtualPathToRealPath(webroot, userinfo.Username)
-	if err != nil {
-		handleWebrootError(w)
-		return
-	}
-
-	//Perform path rewrite
-	rewrittenPath := strings.Join(parsedRequestURL[2:], "/")
-
-	//Actual accessing file path
-	targetFilePath := filepath.ToSlash(filepath.Join(webrootRealpath, rewrittenPath))
-
-	//Check if the file exists
-	if !fsh.FileSystemAbstraction.FileExists(targetFilePath) {
-		serveNotFoundTemplate(w, r)
-		return
-	}
-
-	//Fix mimetype of js files on Windows 10 bug
-	if filepath.Ext(targetFilePath) == ".js" {
-		w.Header().Set("Content-Type", "application/javascript")
-	}
-
-	if filepath.Ext(targetFilePath) == "" {
-		//Reading a folder. Check if index.htm or index.html exists.
-		if fsh.FileSystemAbstraction.FileExists(filepath.Join(targetFilePath, "index.html")) {
-			targetFilePath = filepath.ToSlash(filepath.Join(targetFilePath, "index.html"))
-		} else if fsh.FileSystemAbstraction.FileExists(filepath.Join(targetFilePath, "index.htm")) {
-			targetFilePath = filepath.ToSlash(filepath.Join(targetFilePath, "index.htm"))
-		} else {
-			//Not allow listing folder
-			http.ServeFile(w, r, "system/errors/forbidden.html")
-			return
-		}
-
-	}
-
-	//Record the client IP for analysis, to be added in the future if needed
-
-	//Serve the file
-	if fileExists(targetFilePath) {
-		http.ServeFile(w, r, targetFilePath)
-	} else {
-		f, err := fsh.FileSystemAbstraction.ReadStream(targetFilePath)
-		if err != nil {
-			w.Write([]byte(err.Error()))
-			return
-		}
-		io.Copy(w, f)
-		f.Close()
-	}
-
-}
-
-func serveNotFoundTemplate(w http.ResponseWriter, r *http.Request) {
-	http.ServeFile(w, r, "system/errors/notfound.html")
-}
-
-func handleWebrootError(w http.ResponseWriter) {
-	w.WriteHeader(http.StatusInternalServerError)
-	if fileExists("./system/www/nowebroot.html") {
-		content, err := ioutil.ReadFile("./system/www/nowebroot.html")
-		if err != nil {
-			w.Write([]byte("500 - Internal Server Error"))
-		} else {
-			w.Write(content)
-		}
-
-	} else {
-		w.Write([]byte("500 - Internal Server Error"))
-	}
-}
+package www
+
+import (
+	"io"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"path/filepath"
+	"strings"
+
+	"imuslab.com/arozos/mod/agi"
+	"imuslab.com/arozos/mod/database"
+	"imuslab.com/arozos/mod/user"
+)
+
+/*
+	www package
+
+	This package is the replacement handler for global homepage function in ArozOS.
+	This allow users to host and create their website using any folder within the user
+	access file system.
+
+*/
+
+type Options struct {
+	UserHandler *user.UserHandler
+	Database    *database.Database
+	AgiGateway  *agi.Gateway
+}
+
+type Handler struct {
+	Options Options
+}
+
+/*
+	New WebRoot Handler create a new handler for handling and routing webroots
+*/
+func NewWebRootHandler(options Options) *Handler {
+	//Create the homepage database table
+	options.Database.NewTable("www")
+
+	//Return the handler object
+	return &Handler{
+		Options: options,
+	}
+}
+
+func (h *Handler) RouteRequest(w http.ResponseWriter, r *http.Request) {
+	//Check if it is reaching www root folder or any files directly under www.
+	if filepath.ToSlash(filepath.Clean(r.URL.Path)) == "/www" {
+		//Direct access of the root folder. Serve the homepage description.
+		http.ServeFile(w, r, "web/SystemAO/www/index.html")
+		return
+	} else if filepath.ToSlash(filepath.Dir(r.URL.Path)) == "/www" {
+		//Missing the last / at the end of the path
+		r.URL.Path = r.URL.Path + "/"
+		http.Redirect(w, r, filepath.ToSlash(filepath.Dir(r.URL.Path))+"/", http.StatusTemporaryRedirect)
+		return
+	}
+
+	//Escape the URL
+	decodedValue, err := url.QueryUnescape(r.URL.Path)
+	if err != nil {
+		//failed to decode. Just use its raw value
+		decodedValue = r.URL.Path
+	}
+
+	//Check the user name of the user root
+	parsedRequestURL := strings.Split(filepath.ToSlash(filepath.Clean(decodedValue)[1:]), "/")
+	//Malparsed URL. Ignore request
+	if len(parsedRequestURL) < 2 {
+		serveNotFoundTemplate(w, r)
+		return
+	}
+
+	//Extract user information
+	username := parsedRequestURL[1]
+
+	userinfo, err := h.Options.UserHandler.GetUserInfoFromUsername(username)
+	if err != nil {
+		serveNotFoundTemplate(w, r)
+		return
+	}
+
+	//Check if this user enabled homepage
+	enabled := h.CheckUserHomePageEnabled(userinfo.Username)
+	if !enabled {
+		serveNotFoundTemplate(w, r)
+		return
+	}
+
+	//Check if the user have his webroot correctly configured
+	webroot, err := h.GetUserWebRoot(userinfo.Username)
+	if err != nil {
+		//User do not have a correctly configured webroot. Serve instruction
+		handleWebrootError(w)
+		return
+	}
+
+	//User webroot real path conversion
+	fsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(webroot)
+	if err != nil {
+		handleWebrootError(w)
+		return
+	}
+
+	webrootRealpath, err := fsh.FileSystemAbstraction.VirtualPathToRealPath(webroot, userinfo.Username)
+	if err != nil {
+		handleWebrootError(w)
+		return
+	}
+
+	//Perform path rewrite
+	rewrittenPath := strings.Join(parsedRequestURL[2:], "/")
+
+	//Actual accessing file path
+	targetFilePath := filepath.ToSlash(filepath.Join(webrootRealpath, rewrittenPath))
+
+	//Check if the file exists
+	if !fsh.FileSystemAbstraction.FileExists(targetFilePath) {
+		serveNotFoundTemplate(w, r)
+		return
+	}
+
+	//Fix mimetype of js files on Windows 10 bug
+	if filepath.Ext(targetFilePath) == ".js" {
+		w.Header().Set("Content-Type", "application/javascript")
+	}
+
+	if filepath.Ext(targetFilePath) == "" {
+		//Reading a folder. Check if index.htm or index.html exists.
+		if fsh.FileSystemAbstraction.FileExists(filepath.Join(targetFilePath, "index.html")) {
+			targetFilePath = filepath.ToSlash(filepath.Join(targetFilePath, "index.html"))
+		} else if fsh.FileSystemAbstraction.FileExists(filepath.Join(targetFilePath, "index.htm")) {
+			targetFilePath = filepath.ToSlash(filepath.Join(targetFilePath, "index.htm"))
+		} else {
+			//Not allow listing folder
+			http.ServeFile(w, r, "system/errors/forbidden.html")
+			return
+		}
+
+	}
+
+	//Record the client IP for analysis, to be added in the future if needed
+
+	//Execute it if it is agi file
+	if fsh.FileSystemAbstraction.FileExists(targetFilePath) && filepath.Ext(targetFilePath) == ".agi" {
+		result, err := h.Options.AgiGateway.ExecuteAGIScriptAsUser(fsh, targetFilePath, userinfo, r)
+		if err != nil {
+			w.Write([]byte("500 - Internal Server Error \n" + err.Error()))
+			return
+		}
+
+		w.Write([]byte(result))
+		return
+	}
+
+	//Serve the file
+	if fsh.FileSystemAbstraction.FileExists(targetFilePath) {
+		http.ServeFile(w, r, targetFilePath)
+	} else {
+		f, err := fsh.FileSystemAbstraction.ReadStream(targetFilePath)
+		if err != nil {
+			w.Write([]byte(err.Error()))
+			return
+		}
+		io.Copy(w, f)
+		f.Close()
+	}
+
+}
+
+func serveNotFoundTemplate(w http.ResponseWriter, r *http.Request) {
+	http.ServeFile(w, r, "system/errors/notfound.html")
+}
+
+func handleWebrootError(w http.ResponseWriter) {
+	w.WriteHeader(http.StatusInternalServerError)
+	if fileExists("./system/www/nowebroot.html") {
+		content, err := ioutil.ReadFile("./system/www/nowebroot.html")
+		if err != nil {
+			w.Write([]byte("500 - Internal Server Error"))
+		} else {
+			w.Write(content)
+		}
+
+	} else {
+		w.Write([]byte("500 - Internal Server Error"))
+	}
+}

+ 223 - 222
network.go

@@ -1,222 +1,223 @@
-package main
-
-import (
-	"net/http"
-	"strconv"
-
-	"imuslab.com/arozos/mod/common"
-	network "imuslab.com/arozos/mod/network"
-	mdns "imuslab.com/arozos/mod/network/mdns"
-	"imuslab.com/arozos/mod/network/netstat"
-	ssdp "imuslab.com/arozos/mod/network/ssdp"
-	upnp "imuslab.com/arozos/mod/network/upnp"
-	"imuslab.com/arozos/mod/network/websocket"
-	prout "imuslab.com/arozos/mod/prouter"
-	"imuslab.com/arozos/mod/www"
-)
-
-var (
-	MDNS            *mdns.MDNSHost
-	UPNP            *upnp.UPnPClient
-	SSDP            *ssdp.SSDPHost
-	WebSocketRouter *websocket.Router
-)
-
-func NetworkServiceInit() {
-	systemWideLogger.PrintAndLog("Network", "Starting ArOZ Network Services", nil)
-
-	//Create a router that allow users with System Setting access to access these api endpoints
-	router := prout.NewModuleRouter(prout.RouterOption{
-		ModuleName:  "System Setting",
-		AdminOnly:   false,
-		UserHandler: userHandler,
-		DeniedHandler: func(w http.ResponseWriter, r *http.Request) {
-			common.SendErrorResponse(w, "Permission Denied")
-		},
-	})
-
-	/*
-		Standard Network Utilties
-	*/
-
-	//Register handler endpoints
-	if *allow_hardware_management {
-		router.HandleFunc("/system/network/getNICinfo", network.GetNICInfo)
-		router.HandleFunc("/system/network/getPing", network.GetPing)
-
-		//Register as a system setting
-		registerSetting(settingModule{
-			Name:     "Network Info",
-			Desc:     "System Information",
-			IconPath: "SystemAO/network/img/ethernet.png",
-			Group:    "Network",
-			StartDir: "SystemAO/network/hardware.html",
-		})
-	}
-
-	router.HandleFunc("/system/network/getNICUsage", netstat.HandleGetNetworkInterfaceStats)
-
-	//Start the services that depends on network interface
-	StartNetworkServices()
-
-	//Start the port forward configuration interface
-	portForwardInit()
-
-	//Start userhomepage if enabled
-	//Handle user webroot routings if homepage is enabled
-	if *allow_homepage {
-		userWwwHandler = www.NewWebRootHandler(www.Options{
-			UserHandler: userHandler,
-			Database:    sysdb,
-		})
-
-		router.HandleFunc("/system/network/www/toggle", userWwwHandler.HandleToggleHomepage)
-		router.HandleFunc("/system/network/www/webRoot", userWwwHandler.HandleSetWebRoot)
-
-		//Register as a system setting
-		registerSetting(settingModule{
-			Name:     "Personal Page",
-			Desc:     "Personal Web Page",
-			IconPath: "SystemAO/www/img/homepage.png",
-			Group:    "Network",
-			StartDir: "SystemAO/www/config.html",
-		})
-
-	}
-
-	userRouter := prout.NewModuleRouter(prout.RouterOption{
-		AdminOnly:   false,
-		UserHandler: userHandler,
-		DeniedHandler: func(w http.ResponseWriter, r *http.Request) {
-			common.SendErrorResponse(w, "Permission Denied")
-		},
-	})
-
-	WebSocketRouter = websocket.NewRouter()
-	userRouter.HandleFunc("/system/ws", WebSocketRouter.HandleWebSocketRouting)
-
-}
-
-func StartNetworkServices() {
-
-	/*
-		MDNS Services
-	*/
-	if *allow_mdns {
-		m, err := mdns.NewMDNS(mdns.NetworkHost{
-			HostName:     *host_name + "_" + deviceUUID, //To handle more than one identical model within the same network, this must be unique
-			Port:         *listen_port,
-			Domain:       "arozos.com",
-			Model:        deviceModel,
-			UUID:         deviceUUID,
-			Vendor:       deviceVendor,
-			BuildVersion: build_version,
-			MinorVersion: internal_version,
-		})
-
-		if err != nil {
-			systemWideLogger.PrintAndLog("Network", "MDNS Startup Failed. Running in Offline Mode.", err)
-		} else {
-			MDNS = m
-		}
-
-	}
-
-	/*
-		SSDP Discovery Services
-	*/
-	if *allow_ssdp {
-		//Get outbound ip
-		obip, err := network.GetOutboundIP()
-		if err != nil {
-			systemWideLogger.PrintAndLog("Network", "SSDP Startup Failed. Running in Offline Mode.", err)
-		} else {
-			thisIp := obip.String()
-			adv, err := ssdp.NewSSDPHost(thisIp, *listen_port, "system/ssdp.xml", ssdp.SSDPOption{
-				URLBase:   "http://" + thisIp + ":" + strconv.Itoa(*listen_port), //This must be http if used as local hosting devices
-				Hostname:  *host_name,
-				Vendor:    deviceVendor,
-				VendorURL: deviceVendorURL,
-				ModelName: deviceModel,
-				ModelDesc: deviceModelDesc,
-				UUID:      deviceUUID,
-				Serial:    "generic",
-			})
-
-			if err != nil {
-				systemWideLogger.PrintAndLog("Network", "SSDP Startup Failed. Running in Offline Mode.", err)
-			} else {
-				//OK! Start SSDP Service
-				SSDP = adv
-				SSDP.Start()
-			}
-		}
-
-	}
-
-	/*
-		UPNP / Setup automatic port forwarding
-	*/
-	if *allow_upnp {
-		var u *upnp.UPnPClient
-		var err error = nil
-		if *use_tls {
-			u, err = upnp.NewUPNPClient(*tls_listen_port, *host_name+"-https")
-		} else {
-			u, err = upnp.NewUPNPClient(*listen_port, *host_name+"-http")
-		}
-
-		if err != nil {
-			systemWideLogger.PrintAndLog("Network", "UPnP Startup Failed: "+err.Error(), err)
-		} else {
-
-			//Bind the http port if running in https and http server is not disabled
-			if *use_tls && !*disable_http {
-				u.ForwardPort(*listen_port, *host_name+"-http")
-			}
-
-			UPNP = u
-
-			//Register nightly listener for upnp renew
-			nightlyManager.RegisterNightlyTask(func() {
-				UPNP.RenewForwardRules()
-			})
-
-			//Show a tip for success port forward
-			connectionEndpoint := UPNP.ExternalIP + ":" + strconv.Itoa(*listen_port)
-			obip, err := network.GetOutboundIP()
-			obipstring := "[Outbound IP]"
-			if err != nil {
-
-			} else {
-				obipstring = obip.String()
-			}
-
-			localEndpoint := obipstring + ":" + strconv.Itoa(*listen_port)
-			systemWideLogger.PrintAndLog("Network", "Automatic Port Forwarding Completed. Forwarding all request from "+connectionEndpoint+" to "+localEndpoint, nil)
-
-		}
-
-	}
-}
-
-func StopNetworkServices() {
-	//systemWideLogger.PrintAndLog("Shutting Down Network Services...",nil)
-	//Shutdown uPNP service if enabled
-	if *allow_upnp {
-		systemWideLogger.PrintAndLog("System", "<!> Shutting down uPNP service", nil)
-		UPNP.Close()
-	}
-
-	//Shutdown SSDP service if enabled
-	if *allow_ssdp {
-		systemWideLogger.PrintAndLog("System", "<!> Shutting down SSDP service", nil)
-		SSDP.Close()
-	}
-
-	//Shutdown MDNS if enabled
-	if *allow_mdns {
-		systemWideLogger.PrintAndLog("System", "<!> Shutting down MDNS service", nil)
-		MDNS.Close()
-	}
-}
+package main
+
+import (
+	"net/http"
+	"strconv"
+
+	"imuslab.com/arozos/mod/common"
+	network "imuslab.com/arozos/mod/network"
+	mdns "imuslab.com/arozos/mod/network/mdns"
+	"imuslab.com/arozos/mod/network/netstat"
+	ssdp "imuslab.com/arozos/mod/network/ssdp"
+	upnp "imuslab.com/arozos/mod/network/upnp"
+	"imuslab.com/arozos/mod/network/websocket"
+	prout "imuslab.com/arozos/mod/prouter"
+	"imuslab.com/arozos/mod/www"
+)
+
+var (
+	MDNS            *mdns.MDNSHost
+	UPNP            *upnp.UPnPClient
+	SSDP            *ssdp.SSDPHost
+	WebSocketRouter *websocket.Router
+)
+
+func NetworkServiceInit() {
+	systemWideLogger.PrintAndLog("Network", "Starting ArOZ Network Services", nil)
+
+	//Create a router that allow users with System Setting access to access these api endpoints
+	router := prout.NewModuleRouter(prout.RouterOption{
+		ModuleName:  "System Setting",
+		AdminOnly:   false,
+		UserHandler: userHandler,
+		DeniedHandler: func(w http.ResponseWriter, r *http.Request) {
+			common.SendErrorResponse(w, "Permission Denied")
+		},
+	})
+
+	/*
+		Standard Network Utilties
+	*/
+
+	//Register handler endpoints
+	if *allow_hardware_management {
+		router.HandleFunc("/system/network/getNICinfo", network.GetNICInfo)
+		router.HandleFunc("/system/network/getPing", network.GetPing)
+
+		//Register as a system setting
+		registerSetting(settingModule{
+			Name:     "Network Info",
+			Desc:     "System Information",
+			IconPath: "SystemAO/network/img/ethernet.png",
+			Group:    "Network",
+			StartDir: "SystemAO/network/hardware.html",
+		})
+	}
+
+	router.HandleFunc("/system/network/getNICUsage", netstat.HandleGetNetworkInterfaceStats)
+
+	//Start the services that depends on network interface
+	StartNetworkServices()
+
+	//Start the port forward configuration interface
+	portForwardInit()
+
+	//Start userhomepage if enabled
+	//Handle user webroot routings if homepage is enabled
+	if *allow_homepage {
+		userWwwHandler = www.NewWebRootHandler(www.Options{
+			UserHandler: userHandler,
+			Database:    sysdb,
+			AgiGateway:  AGIGateway,
+		})
+
+		router.HandleFunc("/system/network/www/toggle", userWwwHandler.HandleToggleHomepage)
+		router.HandleFunc("/system/network/www/webRoot", userWwwHandler.HandleSetWebRoot)
+
+		//Register as a system setting
+		registerSetting(settingModule{
+			Name:     "Personal Page",
+			Desc:     "Personal Web Page",
+			IconPath: "SystemAO/www/img/homepage.png",
+			Group:    "Network",
+			StartDir: "SystemAO/www/config.html",
+		})
+
+	}
+
+	userRouter := prout.NewModuleRouter(prout.RouterOption{
+		AdminOnly:   false,
+		UserHandler: userHandler,
+		DeniedHandler: func(w http.ResponseWriter, r *http.Request) {
+			common.SendErrorResponse(w, "Permission Denied")
+		},
+	})
+
+	WebSocketRouter = websocket.NewRouter()
+	userRouter.HandleFunc("/system/ws", WebSocketRouter.HandleWebSocketRouting)
+
+}
+
+func StartNetworkServices() {
+
+	/*
+		MDNS Services
+	*/
+	if *allow_mdns {
+		m, err := mdns.NewMDNS(mdns.NetworkHost{
+			HostName:     *host_name + "_" + deviceUUID, //To handle more than one identical model within the same network, this must be unique
+			Port:         *listen_port,
+			Domain:       "arozos.com",
+			Model:        deviceModel,
+			UUID:         deviceUUID,
+			Vendor:       deviceVendor,
+			BuildVersion: build_version,
+			MinorVersion: internal_version,
+		})
+
+		if err != nil {
+			systemWideLogger.PrintAndLog("Network", "MDNS Startup Failed. Running in Offline Mode.", err)
+		} else {
+			MDNS = m
+		}
+
+	}
+
+	/*
+		SSDP Discovery Services
+	*/
+	if *allow_ssdp {
+		//Get outbound ip
+		obip, err := network.GetOutboundIP()
+		if err != nil {
+			systemWideLogger.PrintAndLog("Network", "SSDP Startup Failed. Running in Offline Mode.", err)
+		} else {
+			thisIp := obip.String()
+			adv, err := ssdp.NewSSDPHost(thisIp, *listen_port, "system/ssdp.xml", ssdp.SSDPOption{
+				URLBase:   "http://" + thisIp + ":" + strconv.Itoa(*listen_port), //This must be http if used as local hosting devices
+				Hostname:  *host_name,
+				Vendor:    deviceVendor,
+				VendorURL: deviceVendorURL,
+				ModelName: deviceModel,
+				ModelDesc: deviceModelDesc,
+				UUID:      deviceUUID,
+				Serial:    "generic",
+			})
+
+			if err != nil {
+				systemWideLogger.PrintAndLog("Network", "SSDP Startup Failed. Running in Offline Mode.", err)
+			} else {
+				//OK! Start SSDP Service
+				SSDP = adv
+				SSDP.Start()
+			}
+		}
+
+	}
+
+	/*
+		UPNP / Setup automatic port forwarding
+	*/
+	if *allow_upnp {
+		var u *upnp.UPnPClient
+		var err error = nil
+		if *use_tls {
+			u, err = upnp.NewUPNPClient(*tls_listen_port, *host_name+"-https")
+		} else {
+			u, err = upnp.NewUPNPClient(*listen_port, *host_name+"-http")
+		}
+
+		if err != nil {
+			systemWideLogger.PrintAndLog("Network", "UPnP Startup Failed: "+err.Error(), err)
+		} else {
+
+			//Bind the http port if running in https and http server is not disabled
+			if *use_tls && !*disable_http {
+				u.ForwardPort(*listen_port, *host_name+"-http")
+			}
+
+			UPNP = u
+
+			//Register nightly listener for upnp renew
+			nightlyManager.RegisterNightlyTask(func() {
+				UPNP.RenewForwardRules()
+			})
+
+			//Show a tip for success port forward
+			connectionEndpoint := UPNP.ExternalIP + ":" + strconv.Itoa(*listen_port)
+			obip, err := network.GetOutboundIP()
+			obipstring := "[Outbound IP]"
+			if err != nil {
+
+			} else {
+				obipstring = obip.String()
+			}
+
+			localEndpoint := obipstring + ":" + strconv.Itoa(*listen_port)
+			systemWideLogger.PrintAndLog("Network", "Automatic Port Forwarding Completed. Forwarding all request from "+connectionEndpoint+" to "+localEndpoint, nil)
+
+		}
+
+	}
+}
+
+func StopNetworkServices() {
+	//systemWideLogger.PrintAndLog("Shutting Down Network Services...",nil)
+	//Shutdown uPNP service if enabled
+	if *allow_upnp {
+		systemWideLogger.PrintAndLog("System", "<!> Shutting down uPNP service", nil)
+		UPNP.Close()
+	}
+
+	//Shutdown SSDP service if enabled
+	if *allow_ssdp {
+		systemWideLogger.PrintAndLog("System", "<!> Shutting down SSDP service", nil)
+		SSDP.Close()
+	}
+
+	//Shutdown MDNS if enabled
+	if *allow_mdns {
+		systemWideLogger.PrintAndLog("System", "<!> Shutting down MDNS service", nil)
+		MDNS.Close()
+	}
+}

+ 95 - 98
scheduler.go

@@ -1,98 +1,95 @@
-package main
-
-import (
-	"net/http"
-
-	"imuslab.com/arozos/mod/common"
-	module "imuslab.com/arozos/mod/modules"
-	prout "imuslab.com/arozos/mod/prouter"
-	"imuslab.com/arozos/mod/time/nightly"
-	"imuslab.com/arozos/mod/time/scheduler"
-)
-
-/*
-	Nightly.go
-	author: tobychui
-
-	This is a handle for putting everything that is required to run everynight.
-	Default: Run once every day 3am in the morning.
-
-*/
-
-var (
-	nightlyManager  *nightly.TaskManager
-	systemScheduler *scheduler.Scheduler
-)
-
-func NightlyTasksInit() {
-	/*
-		Nighty Task Manager
-
-		The tasks that should be done once per night. Internal function only.
-	*/
-	nightlyManager = nightly.NewNightlyTaskManager(*nightlyTaskRunTime)
-
-}
-
-func SchedulerInit() {
-
-	/*
-		System Scheudler
-
-		The internal scheudler for arozos
-	*/
-	//Create an user router and its module
-	router := prout.NewModuleRouter(prout.RouterOption{
-		ModuleName:  "Tasks Scheduler",
-		AdminOnly:   false,
-		UserHandler: userHandler,
-		DeniedHandler: func(w http.ResponseWriter, r *http.Request) {
-			common.SendErrorResponse(w, "Permission Denied")
-		},
-	})
-
-	//Register the module
-	moduleHandler.RegisterModule(module.ModuleInfo{
-		Name:        "Tasks Scheduler",
-		Group:       "System Tools",
-		IconPath:    "SystemAO/arsm/img/scheduler.png",
-		Version:     "1.2",
-		StartDir:    "SystemAO/arsm/scheduler.html",
-		SupportFW:   true,
-		InitFWSize:  []int{1080, 580},
-		LaunchFWDir: "SystemAO/arsm/scheduler.html",
-		SupportEmb:  false,
-	})
-
-	//Startup the ArOZ Emulated Crontab Service
-	newScheduler, err := scheduler.NewScheduler(userHandler, AGIGateway, "system/cron.json")
-	if err != nil {
-		systemWideLogger.PrintAndLog("Cron", "ArOZ Emulated Cron Startup Failed. Stopping all scheduled tasks.", err)
-	}
-
-	systemScheduler = newScheduler
-
-	//Register Endpoints
-	http.HandleFunc("/system/arsm/aecron/list", func(w http.ResponseWriter, r *http.Request) {
-		if authAgent.CheckAuth(r) {
-			//User logged in
-			newScheduler.HandleListJobs(w, r)
-		} else {
-			//User not logged in
-			errorHandlePermissionDenied(w, r)
-		}
-	})
-	router.HandleFunc("/system/arsm/aecron/add", systemScheduler.HandleAddJob)
-	router.HandleFunc("/system/arsm/aecron/remove", systemScheduler.HandleJobRemoval)
-	router.HandleFunc("/system/arsm/aecron/listlog", systemScheduler.HandleShowLog)
-
-	//Register settings
-	registerSetting(settingModule{
-		Name:         "Tasks Scheduler",
-		Desc:         "System Tasks and Excution Scheduler",
-		IconPath:     "SystemAO/arsm/img/small_icon.png",
-		Group:        "Cluster",
-		StartDir:     "SystemAO/arsm/aecron.html",
-		RequireAdmin: false,
-	})
-}
+package main
+
+import (
+	"imuslab.com/arozos/mod/time/nightly"
+	//"imuslab.com/arozos/mod/time/scheduler"
+)
+
+/*
+	Nightly.go
+	author: tobychui
+
+	This is a handle for putting everything that is required to run everynight.
+	Default: Run once every day 3am in the morning.
+
+*/
+
+var (
+	nightlyManager *nightly.TaskManager
+	//systemScheduler *scheduler.Scheduler
+)
+
+func NightlyTasksInit() {
+	/*
+		Nighty Task Manager
+
+		The tasks that should be done once per night. Internal function only.
+	*/
+	nightlyManager = nightly.NewNightlyTaskManager(*nightlyTaskRunTime)
+
+}
+
+func SchedulerInit() {
+
+	/*
+		System Scheudler
+
+		The internal scheudler for arozos
+	*/
+	//Create an user router and its module
+	/*
+		router := prout.NewModuleRouter(prout.RouterOption{
+			ModuleName:  "Tasks Scheduler",
+			AdminOnly:   false,
+			UserHandler: userHandler,
+			DeniedHandler: func(w http.ResponseWriter, r *http.Request) {
+				common.SendErrorResponse(w, "Permission Denied")
+			},
+		})
+
+		//Register the module
+		moduleHandler.RegisterModule(module.ModuleInfo{
+			Name:        "Tasks Scheduler",
+			Group:       "System Tools",
+			IconPath:    "SystemAO/arsm/img/scheduler.png",
+			Version:     "1.2",
+			StartDir:    "SystemAO/arsm/scheduler.html",
+			SupportFW:   true,
+			InitFWSize:  []int{1080, 580},
+			LaunchFWDir: "SystemAO/arsm/scheduler.html",
+			SupportEmb:  false,
+		})
+
+		//Startup the ArOZ Emulated Crontab Service
+		newScheduler, err := scheduler.NewScheduler(userHandler, AGIGateway, "system/cron.json")
+		if err != nil {
+			systemWideLogger.PrintAndLog("Cron", "ArOZ Emulated Cron Startup Failed. Stopping all scheduled tasks.", err)
+		}
+
+		systemScheduler = newScheduler
+
+		//Register Endpoints
+		http.HandleFunc("/system/arsm/aecron/list", func(w http.ResponseWriter, r *http.Request) {
+			if authAgent.CheckAuth(r) {
+				//User logged in
+				newScheduler.HandleListJobs(w, r)
+			} else {
+				//User not logged in
+				errorHandlePermissionDenied(w, r)
+			}
+		})
+		router.HandleFunc("/system/arsm/aecron/add", systemScheduler.HandleAddJob)
+		router.HandleFunc("/system/arsm/aecron/remove", systemScheduler.HandleJobRemoval)
+		router.HandleFunc("/system/arsm/aecron/listlog", systemScheduler.HandleShowLog)
+
+		//Register settings
+		registerSetting(settingModule{
+			Name:         "Tasks Scheduler",
+			Desc:         "System Tasks and Excution Scheduler",
+			IconPath:     "SystemAO/arsm/img/small_icon.png",
+			Group:        "Cluster",
+			StartDir:     "SystemAO/arsm/aecron.html",
+			RequireAdmin: false,
+		})
+	*/
+}