systemFunc.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. package agi
  2. import (
  3. "encoding/csv"
  4. "encoding/json"
  5. "errors"
  6. "log"
  7. "os"
  8. "os/exec"
  9. "path/filepath"
  10. "strings"
  11. "time"
  12. "github.com/robertkrimen/otto"
  13. "imuslab.com/arozos/mod/agi/static"
  14. "imuslab.com/arozos/mod/utils"
  15. )
  16. // Inject aroz online custom functions into the virtual machine
  17. func (g *Gateway) injectStandardLibs(vm *otto.Otto, scriptFile string, scriptScope string) {
  18. //Define system core modules and definations
  19. sysdb := g.Option.UserHandler.GetDatabase()
  20. //Define VM global variables
  21. vm.Set("BUILD_VERSION", g.Option.BuildVersion)
  22. vm.Set("INTERNAL_VERSION", g.Option.InternalVersion)
  23. vm.Set("LOADED_MODULES", g.Option.LoadedModule)
  24. vm.Set("LOADED_STORAGES", g.Option.UserHandler.GetStoragePool())
  25. vm.Set("__FILE__", scriptFile)
  26. vm.Set("HTTP_RESP", "")
  27. vm.Set("HTTP_HEADER", "text/plain")
  28. //Response related
  29. vm.Set("sendResp", func(call otto.FunctionCall) otto.Value {
  30. argString, _ := call.Argument(0).ToString()
  31. vm.Set("HTTP_RESP", argString)
  32. return otto.Value{}
  33. })
  34. vm.Set("echo", func(call otto.FunctionCall) otto.Value {
  35. argString, _ := call.Argument(0).ToString()
  36. currentResp, err := vm.Get("HTTP_RESP")
  37. if err != nil {
  38. vm.Set("HTTP_RESP", argString)
  39. } else {
  40. currentRespText, err := currentResp.ToString()
  41. if err != nil {
  42. //Unable to parse this as string. Overwrite response
  43. vm.Set("HTTP_RESP", argString)
  44. }
  45. vm.Set("HTTP_RESP", currentRespText+argString)
  46. }
  47. return otto.Value{}
  48. })
  49. vm.Set("sendOK", func(call otto.FunctionCall) otto.Value {
  50. vm.Set("HTTP_RESP", "ok")
  51. return otto.Value{}
  52. })
  53. vm.Set("_sendJSONResp", func(call otto.FunctionCall) otto.Value {
  54. argString, _ := call.Argument(0).ToString()
  55. vm.Set("HTTP_HEADER", "application/json")
  56. vm.Set("HTTP_RESP", argString)
  57. return otto.Value{}
  58. })
  59. vm.Run(`
  60. sendJSONResp = function(object){
  61. if (typeof(object) === "object"){
  62. _sendJSONResp(JSON.stringify(object));
  63. }else{
  64. _sendJSONResp(object);
  65. }
  66. }
  67. `)
  68. vm.Set("addNightlyTask", func(call otto.FunctionCall) otto.Value {
  69. scriptPath, _ := call.Argument(0).ToString() //From web directory
  70. if static.IsValidAGIScript(scriptPath) {
  71. g.NightlyScripts = append(g.NightlyScripts, scriptPath)
  72. } else {
  73. return otto.FalseValue()
  74. }
  75. return otto.TrueValue()
  76. })
  77. //Database related
  78. //newDBTableIfNotExists(tableName)
  79. vm.Set("newDBTableIfNotExists", func(call otto.FunctionCall) otto.Value {
  80. tableName, err := call.Argument(0).ToString()
  81. if err != nil {
  82. g.RaiseError(err)
  83. reply, _ := vm.ToValue(false)
  84. return reply
  85. }
  86. //Create the table with given tableName
  87. if g.filterDBTable(tableName, false) {
  88. sysdb.NewTable(tableName)
  89. //Return true
  90. reply, _ := vm.ToValue(true)
  91. return reply
  92. }
  93. reply, _ := vm.ToValue(false)
  94. return reply
  95. })
  96. vm.Set("DBTableExists", func(call otto.FunctionCall) otto.Value {
  97. tableName, err := call.Argument(0).ToString()
  98. if err != nil {
  99. g.RaiseError(err)
  100. reply, _ := vm.ToValue(false)
  101. return reply
  102. }
  103. //Create the table with given tableName
  104. if sysdb.TableExists(tableName) {
  105. return otto.TrueValue()
  106. }
  107. return otto.FalseValue()
  108. })
  109. //dropDBTable(tablename)
  110. vm.Set("dropDBTable", func(call otto.FunctionCall) otto.Value {
  111. tableName, err := call.Argument(0).ToString()
  112. if err != nil {
  113. g.RaiseError(err)
  114. reply, _ := vm.ToValue(false)
  115. return reply
  116. }
  117. //Create the table with given tableName
  118. if g.filterDBTable(tableName, true) {
  119. sysdb.DropTable(tableName)
  120. reply, _ := vm.ToValue(true)
  121. return reply
  122. }
  123. //Return true
  124. reply, _ := vm.ToValue(false)
  125. return reply
  126. })
  127. //writeDBItem(tablename, key, value) => return true when suceed
  128. vm.Set("writeDBItem", func(call otto.FunctionCall) otto.Value {
  129. tableName, err := call.Argument(0).ToString()
  130. if err != nil {
  131. g.RaiseError(err)
  132. reply, _ := vm.ToValue(false)
  133. return reply
  134. }
  135. //Check if the tablename is reserved
  136. if g.filterDBTable(tableName, true) {
  137. keyString, err := call.Argument(1).ToString()
  138. if err != nil {
  139. g.RaiseError(err)
  140. reply, _ := vm.ToValue(false)
  141. return reply
  142. }
  143. valueString, err := call.Argument(2).ToString()
  144. if err != nil {
  145. g.RaiseError(err)
  146. reply, _ := vm.ToValue(false)
  147. return reply
  148. }
  149. sysdb.Write(tableName, keyString, valueString)
  150. reply, _ := vm.ToValue(true)
  151. return reply
  152. }
  153. reply, _ := vm.ToValue(false)
  154. return reply
  155. })
  156. //readDBItem(tablename, key) => return value
  157. vm.Set("readDBItem", func(call otto.FunctionCall) otto.Value {
  158. tableName, _ := call.Argument(0).ToString()
  159. keyString, _ := call.Argument(1).ToString()
  160. returnValue := ""
  161. reply, _ := vm.ToValue(nil)
  162. if g.filterDBTable(tableName, true) {
  163. sysdb.Read(tableName, keyString, &returnValue)
  164. r, _ := vm.ToValue(returnValue)
  165. reply = r
  166. } else {
  167. reply = otto.FalseValue()
  168. }
  169. return reply
  170. })
  171. //listDBTable(tablename) => Return key values array
  172. vm.Set("listDBTable", func(call otto.FunctionCall) otto.Value {
  173. tableName, _ := call.Argument(0).ToString()
  174. returnValue := map[string]string{}
  175. reply, _ := vm.ToValue(nil)
  176. if g.filterDBTable(tableName, true) {
  177. entries, _ := sysdb.ListTable(tableName)
  178. for _, keypairs := range entries {
  179. //Decode the string
  180. result := ""
  181. json.Unmarshal(keypairs[1], &result)
  182. returnValue[string(keypairs[0])] = result
  183. }
  184. r, err := vm.ToValue(returnValue)
  185. if err != nil {
  186. return otto.NullValue()
  187. }
  188. return r
  189. } else {
  190. reply = otto.FalseValue()
  191. }
  192. return reply
  193. })
  194. //deleteDBItem(tablename, key) => Return true if success, false if failed
  195. vm.Set("deleteDBItem", func(call otto.FunctionCall) otto.Value {
  196. tableName, _ := call.Argument(0).ToString()
  197. keyString, _ := call.Argument(1).ToString()
  198. if g.filterDBTable(tableName, true) {
  199. err := sysdb.Delete(tableName, keyString)
  200. if err != nil {
  201. return otto.FalseValue()
  202. }
  203. } else {
  204. //Permission denied
  205. return otto.FalseValue()
  206. }
  207. return otto.TrueValue()
  208. })
  209. //Module registry
  210. vm.Set("registerModule", func(call otto.FunctionCall) otto.Value {
  211. jsonModuleConfig, err := call.Argument(0).ToString()
  212. if err != nil {
  213. g.RaiseError(err)
  214. reply, _ := vm.ToValue(false)
  215. return reply
  216. }
  217. //Try to decode it to a module Info
  218. g.Option.ModuleRegisterParser(jsonModuleConfig)
  219. if err != nil {
  220. g.RaiseError(err)
  221. reply, _ := vm.ToValue(false)
  222. return reply
  223. }
  224. return otto.Value{}
  225. })
  226. //Package Executation. Only usable when called to a given script File.
  227. if scriptFile != "" && scriptScope != "" {
  228. //Package request --> Install linux package if not exists
  229. vm.Set("requirepkg", func(call otto.FunctionCall) otto.Value {
  230. packageName, err := call.Argument(0).ToString()
  231. if err != nil {
  232. g.RaiseError(err)
  233. return otto.FalseValue()
  234. }
  235. requireComply, err := call.Argument(1).ToBoolean()
  236. if err != nil {
  237. g.RaiseError(err)
  238. return otto.FalseValue()
  239. }
  240. scriptRoot := static.GetScriptRoot(scriptFile, scriptScope)
  241. //Check if this module already get registered.
  242. alreadyRegistered := false
  243. for _, pkgRequest := range g.AllowAccessPkgs[strings.ToLower(packageName)] {
  244. if pkgRequest.InitRoot == scriptRoot {
  245. alreadyRegistered = true
  246. break
  247. }
  248. }
  249. if !alreadyRegistered {
  250. //Register this packge to this script and allow the module to call this package
  251. g.AllowAccessPkgs[strings.ToLower(packageName)] = append(g.AllowAccessPkgs[strings.ToLower(packageName)], AgiPackage{
  252. InitRoot: scriptRoot,
  253. })
  254. }
  255. //Try to install the package via apt
  256. err = g.Option.PackageManager.InstallIfNotExists(packageName, requireComply)
  257. if err != nil {
  258. g.RaiseError(err)
  259. return otto.FalseValue()
  260. }
  261. return otto.TrueValue()
  262. })
  263. //Exec required pkg with permission control
  264. vm.Set("execpkg", func(call otto.FunctionCall) otto.Value {
  265. //Check if the pkg is already registered
  266. scriptRoot := static.GetScriptRoot(scriptFile, scriptScope)
  267. packageName, err := call.Argument(0).ToString()
  268. if err != nil {
  269. g.RaiseError(err)
  270. return otto.FalseValue()
  271. }
  272. if val, ok := g.AllowAccessPkgs[packageName]; ok {
  273. //Package already registered by at least one module. Check if this script root registered
  274. thisModuleRegistered := false
  275. for _, registeredPkgInterface := range val {
  276. if registeredPkgInterface.InitRoot == scriptRoot {
  277. //This package registered this command. Allow access
  278. thisModuleRegistered = true
  279. }
  280. }
  281. if !thisModuleRegistered {
  282. g.RaiseError(errors.New("Package request not registered: " + packageName))
  283. return otto.FalseValue()
  284. }
  285. } else {
  286. g.RaiseError(errors.New("Package request not registered: " + packageName))
  287. return otto.FalseValue()
  288. }
  289. //Ok. Allow paramter to be loaded
  290. execParamters, _ := call.Argument(1).ToString()
  291. // Split input paramters into []string
  292. r := csv.NewReader(strings.NewReader(execParamters))
  293. r.Comma = ' ' // space
  294. fields, err := r.Read()
  295. if err != nil {
  296. g.RaiseError(err)
  297. return otto.FalseValue()
  298. }
  299. //Run os.Exec on the given commands
  300. cmd := exec.Command(packageName, fields...)
  301. out, err := cmd.CombinedOutput()
  302. if err != nil {
  303. log.Println(string(out))
  304. g.RaiseError(err)
  305. return otto.FalseValue()
  306. }
  307. reply, _ := vm.ToValue(string(out))
  308. return reply
  309. })
  310. //Include another js in runtime
  311. vm.Set("includes", func(call otto.FunctionCall) otto.Value {
  312. //Check if the pkg is already registered
  313. scriptName, err := call.Argument(0).ToString()
  314. if err != nil {
  315. g.RaiseError(err)
  316. return otto.FalseValue()
  317. }
  318. //Check if it is calling itself
  319. if filepath.Base(scriptFile) == filepath.Base(scriptName) {
  320. g.RaiseError(errors.New("*AGI* Self calling is not allowed"))
  321. return otto.FalseValue()
  322. }
  323. //Check if the script file exists
  324. targetScriptPath := filepath.ToSlash(filepath.Join(filepath.Dir(scriptFile), scriptName))
  325. if !utils.FileExists(targetScriptPath) {
  326. g.RaiseError(errors.New("*AGI* Target path not exists!"))
  327. return otto.FalseValue()
  328. }
  329. //Run the script
  330. scriptContent, _ := os.ReadFile(targetScriptPath)
  331. _, err = vm.Run(string(scriptContent))
  332. if err != nil {
  333. //Script execution failed
  334. log.Println("Script Execution Failed: ", err.Error())
  335. g.RaiseError(err)
  336. return otto.FalseValue()
  337. }
  338. return otto.TrueValue()
  339. })
  340. }
  341. //Delay, sleep given ms
  342. vm.Set("delay", func(call otto.FunctionCall) otto.Value {
  343. delayTime, err := call.Argument(0).ToInteger()
  344. if err != nil {
  345. g.RaiseError(err)
  346. return otto.FalseValue()
  347. }
  348. time.Sleep(time.Duration(delayTime) * time.Millisecond)
  349. return otto.TrueValue()
  350. })
  351. //Exit
  352. vm.Set("exit", func(call otto.FunctionCall) otto.Value {
  353. vm.Interrupt <- func() {
  354. panic(errExitcall)
  355. }
  356. return otto.NullValue()
  357. })
  358. }