agi.file.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. package agi
  2. import (
  3. "errors"
  4. "io/ioutil"
  5. "log"
  6. "os"
  7. "path/filepath"
  8. "sort"
  9. "strings"
  10. "github.com/robertkrimen/otto"
  11. fs "imuslab.com/arozos/mod/filesystem"
  12. user "imuslab.com/arozos/mod/user"
  13. )
  14. /*
  15. AJGI File Processing Library
  16. This is a library for handling image related functionalities in agi scripts.
  17. By Alanyueng 2020 <- This person write shitty code that need me to tidy up (by tobychui)
  18. Complete rewrite by tobychui in Sept 2020
  19. */
  20. func (g *Gateway) FileLibRegister() {
  21. err := g.RegisterLib("filelib", g.injectFileLibFunctions)
  22. if err != nil {
  23. log.Fatal(err)
  24. }
  25. }
  26. func (g *Gateway) injectFileLibFunctions(vm *otto.Otto, u *user.User) {
  27. //Legacy File system API
  28. //writeFile(virtualFilepath, content) => return true/false when succeed / failed
  29. vm.Set("_filelib_writeFile", func(call otto.FunctionCall) otto.Value {
  30. vpath, err := call.Argument(0).ToString()
  31. if err != nil {
  32. g.raiseError(err)
  33. reply, _ := vm.ToValue(false)
  34. return reply
  35. }
  36. //Check for permission
  37. if !u.CanWrite(vpath) {
  38. panic(vm.MakeCustomError("PermissionDenied", "Path access denied: "+vpath))
  39. }
  40. content, err := call.Argument(1).ToString()
  41. if err != nil {
  42. g.raiseError(err)
  43. reply, _ := vm.ToValue(false)
  44. return reply
  45. }
  46. //Check if there is quota for the given length
  47. if !u.StorageQuota.HaveSpace(int64(len(content))) {
  48. //User have no remaining storage quota
  49. g.raiseError(errors.New("Storage Quota Fulled"))
  50. reply, _ := vm.ToValue(false)
  51. return reply
  52. }
  53. //Translate the virtual path to realpath
  54. rpath, err := virtualPathToRealPath(vpath, u)
  55. if err != nil {
  56. g.raiseError(err)
  57. reply, _ := vm.ToValue(false)
  58. return reply
  59. }
  60. //Check if file already exists.
  61. if fileExists(rpath) {
  62. //Check if this user own this file
  63. isOwner := u.IsOwnerOfFile(rpath)
  64. if isOwner {
  65. //This user own this system. Remove this file from his quota
  66. u.RemoveOwnershipFromFile(rpath)
  67. }
  68. }
  69. //Create and write to file using ioutil
  70. err = ioutil.WriteFile(rpath, []byte(content), 0755)
  71. if err != nil {
  72. g.raiseError(err)
  73. reply, _ := vm.ToValue(false)
  74. return reply
  75. }
  76. //Add the filesize to user quota
  77. u.SetOwnerOfFile(rpath)
  78. reply, _ := vm.ToValue(true)
  79. return reply
  80. })
  81. vm.Set("_filelib_deleteFile", func(call otto.FunctionCall) otto.Value {
  82. vpath, err := call.Argument(0).ToString()
  83. if err != nil {
  84. g.raiseError(err)
  85. reply, _ := vm.ToValue(false)
  86. return reply
  87. }
  88. //Check for permission
  89. if !u.CanWrite(vpath) {
  90. panic(vm.MakeCustomError("PermissionDenied", "Path access denied: "+vpath))
  91. }
  92. //Translate the virtual path to realpath
  93. rpath, err := virtualPathToRealPath(vpath, u)
  94. if err != nil {
  95. g.raiseError(err)
  96. reply, _ := vm.ToValue(false)
  97. return reply
  98. }
  99. //Check if file already exists.
  100. if fileExists(rpath) {
  101. //Check if this user own this file
  102. isOwner := u.IsOwnerOfFile(rpath)
  103. if isOwner {
  104. //This user own this system. Remove this file from his quota
  105. u.RemoveOwnershipFromFile(rpath)
  106. }
  107. } else {
  108. g.raiseError(errors.New("File not exists"))
  109. reply, _ := vm.ToValue(false)
  110. return reply
  111. }
  112. //Remove the file
  113. os.Remove(rpath)
  114. reply, _ := vm.ToValue(true)
  115. return reply
  116. })
  117. //readFile(virtualFilepath) => return content in string
  118. vm.Set("_filelib_readFile", func(call otto.FunctionCall) otto.Value {
  119. vpath, err := call.Argument(0).ToString()
  120. if err != nil {
  121. g.raiseError(err)
  122. reply, _ := vm.ToValue(false)
  123. return reply
  124. }
  125. //Check for permission
  126. if !u.CanRead(vpath) {
  127. panic(vm.MakeCustomError("PermissionDenied", "Path access denied: "+vpath))
  128. }
  129. //Translate the virtual path to realpath
  130. rpath, err := virtualPathToRealPath(vpath, u)
  131. if err != nil {
  132. g.raiseError(err)
  133. reply, _ := vm.ToValue(false)
  134. return reply
  135. }
  136. //Create and write to file using ioUtil
  137. content, err := ioutil.ReadFile(rpath)
  138. if err != nil {
  139. g.raiseError(err)
  140. reply, _ := vm.ToValue(false)
  141. return reply
  142. }
  143. reply, _ := vm.ToValue(string(content))
  144. return reply
  145. })
  146. //Listdir
  147. //readdir("user:/Desktop") => return filelist in array
  148. vm.Set("_filelib_readdir", func(call otto.FunctionCall) otto.Value {
  149. vpath, err := call.Argument(0).ToString()
  150. if err != nil {
  151. g.raiseError(err)
  152. reply, _ := vm.ToValue(false)
  153. return reply
  154. }
  155. //Translate the virtual path to realpath
  156. rpath, err := virtualPathToRealPath(vpath, u)
  157. if err != nil {
  158. g.raiseError(err)
  159. reply, _ := vm.ToValue(false)
  160. return reply
  161. }
  162. fileList, err := specialGlob(rpath)
  163. if err != nil {
  164. g.raiseError(err)
  165. reply, _ := vm.ToValue(false)
  166. return reply
  167. }
  168. //Translate all paths to virtual paths
  169. results := []string{}
  170. for _, file := range fileList {
  171. if IsDir(file) {
  172. thisRpath, _ := realpathToVirtualpath(file, u)
  173. results = append(results, thisRpath)
  174. }
  175. }
  176. reply, _ := vm.ToValue(results)
  177. return reply
  178. })
  179. //Usage
  180. //filelib.walk("user:/") => list everything recursively
  181. //filelib.walk("user:/", "folder") => list all folder recursively
  182. //filelib.walk("user:/", "file") => list all files recursively
  183. vm.Set("_filelib_walk", func(call otto.FunctionCall) otto.Value {
  184. vpath, err := call.Argument(0).ToString()
  185. if err != nil {
  186. g.raiseError(err)
  187. reply, _ := vm.ToValue(false)
  188. return reply
  189. }
  190. mode, err := call.Argument(1).ToString()
  191. if err != nil {
  192. mode = "all"
  193. }
  194. rpath, err := virtualPathToRealPath(vpath, u)
  195. if err != nil {
  196. g.raiseError(err)
  197. reply, _ := vm.ToValue(false)
  198. return reply
  199. }
  200. results := []string{}
  201. err = filepath.Walk(rpath, func(path string, info os.FileInfo, err error) error {
  202. thisVpath, err := realpathToVirtualpath(path, u)
  203. if mode == "file" {
  204. if !info.IsDir() {
  205. results = append(results, thisVpath)
  206. }
  207. } else if mode == "folder" {
  208. if info.IsDir() {
  209. results = append(results, thisVpath)
  210. }
  211. } else {
  212. results = append(results, thisVpath)
  213. }
  214. return nil
  215. })
  216. reply, _ := vm.ToValue(results)
  217. return reply
  218. })
  219. //Glob
  220. //glob("user:/Desktop/*.mp3") => return fileList in array
  221. //glob("/") => return a list of root directories
  222. vm.Set("_filelib_glob", func(call otto.FunctionCall) otto.Value {
  223. regex, err := call.Argument(0).ToString()
  224. if err != nil {
  225. g.raiseError(err)
  226. reply, _ := vm.ToValue(false)
  227. return reply
  228. }
  229. //Handle when regex = "." or "./" (listroot)
  230. if filepath.ToSlash(filepath.Clean(regex)) == "/" || filepath.Clean(regex) == "." {
  231. //List Root
  232. rootDirs := []string{}
  233. fileHandlers := u.GetAllFileSystemHandler()
  234. for _, fsh := range fileHandlers {
  235. rootDirs = append(rootDirs, fsh.UUID+":/")
  236. }
  237. reply, _ := vm.ToValue(rootDirs)
  238. return reply
  239. } else {
  240. //Check for permission
  241. if !u.CanRead(regex) {
  242. panic(vm.MakeCustomError("PermissionDenied", "Path access denied"))
  243. }
  244. //This function can only handle wildcard in filename but not in dir name
  245. vrootPath := filepath.Dir(regex)
  246. regexFilename := filepath.Base(regex)
  247. //Translate the virtual path to realpath
  248. rrootPath, err := virtualPathToRealPath(vrootPath, u)
  249. if err != nil {
  250. g.raiseError(err)
  251. reply, _ := vm.ToValue(false)
  252. return reply
  253. }
  254. suitableFiles, err := filepath.Glob(rrootPath + "/" + regexFilename)
  255. if err != nil {
  256. g.raiseError(err)
  257. reply, _ := vm.ToValue(false)
  258. return reply
  259. }
  260. results := []string{}
  261. for _, file := range suitableFiles {
  262. thisRpath, _ := realpathToVirtualpath(filepath.ToSlash(file), u)
  263. results = append(results, thisRpath)
  264. }
  265. reply, _ := vm.ToValue(results)
  266. return reply
  267. }
  268. })
  269. //Advance Glob using file system special Glob, cannot use to scan root dirs
  270. vm.Set("_filelib_aglob", func(call otto.FunctionCall) otto.Value {
  271. regex, err := call.Argument(0).ToString()
  272. if err != nil {
  273. g.raiseError(err)
  274. reply, _ := vm.ToValue(false)
  275. return reply
  276. }
  277. sortMode, _ := call.Argument(1).ToString()
  278. if regex != "/" && !u.CanRead(regex) {
  279. panic(vm.MakeCustomError("PermissionDenied", "Path access denied"))
  280. }
  281. //This function can only handle wildcard in filename but not in dir name
  282. vrootPath := filepath.Dir(regex)
  283. regexFilename := filepath.Base(regex)
  284. //Translate the virtual path to realpath
  285. rrootPath, err := virtualPathToRealPath(vrootPath, u)
  286. if err != nil {
  287. g.raiseError(err)
  288. reply, _ := vm.ToValue(false)
  289. return reply
  290. }
  291. suitableFiles, err := specialGlob(rrootPath + "/" + regexFilename)
  292. if err != nil {
  293. g.raiseError(err)
  294. reply, _ := vm.ToValue(false)
  295. return reply
  296. }
  297. type SortingFileData struct {
  298. Filename string
  299. Filepath string
  300. Filesize int64
  301. ModTime int64
  302. }
  303. parsedFilelist := []fs.FileData{}
  304. for _, file := range suitableFiles {
  305. vpath, _ := realpathToVirtualpath(filepath.ToSlash(file), u)
  306. modtime, _ := fs.GetModTime(file)
  307. parsedFilelist = append(parsedFilelist, fs.FileData{
  308. Filename: filepath.Base(file),
  309. Filepath: vpath,
  310. Filesize: fs.GetFileSize(file),
  311. ModTime: modtime,
  312. })
  313. }
  314. if sortMode != "" {
  315. if sortMode == "reverse" {
  316. //Sort by reverse name
  317. sort.Slice(parsedFilelist, func(i, j int) bool {
  318. return strings.ToLower(parsedFilelist[i].Filename) > strings.ToLower(parsedFilelist[j].Filename)
  319. })
  320. } else if sortMode == "smallToLarge" {
  321. sort.Slice(parsedFilelist, func(i, j int) bool { return parsedFilelist[i].Filesize < parsedFilelist[j].Filesize })
  322. } else if sortMode == "largeToSmall" {
  323. sort.Slice(parsedFilelist, func(i, j int) bool { return parsedFilelist[i].Filesize > parsedFilelist[j].Filesize })
  324. } else if sortMode == "mostRecent" {
  325. sort.Slice(parsedFilelist, func(i, j int) bool { return parsedFilelist[i].ModTime > parsedFilelist[j].ModTime })
  326. } else if sortMode == "leastRecent" {
  327. sort.Slice(parsedFilelist, func(i, j int) bool { return parsedFilelist[i].ModTime < parsedFilelist[j].ModTime })
  328. } else {
  329. sort.Slice(parsedFilelist, func(i, j int) bool {
  330. return strings.ToLower(parsedFilelist[i].Filename) < strings.ToLower(parsedFilelist[j].Filename)
  331. })
  332. }
  333. }
  334. //Parse the results (Only extract the filepath)
  335. results := []string{}
  336. for _, fileData := range parsedFilelist {
  337. results = append(results, fileData.Filepath)
  338. }
  339. reply, _ := vm.ToValue(results)
  340. return reply
  341. })
  342. //filesize("user:/Desktop/test.txt")
  343. vm.Set("_filelib_filesize", func(call otto.FunctionCall) otto.Value {
  344. vpath, err := call.Argument(0).ToString()
  345. if err != nil {
  346. g.raiseError(err)
  347. reply, _ := vm.ToValue(false)
  348. return reply
  349. }
  350. //Check for permission
  351. if !u.CanRead(vpath) {
  352. panic(vm.MakeCustomError("PermissionDenied", "Path access denied"))
  353. }
  354. //Translate the virtual path to realpath
  355. rpath, err := virtualPathToRealPath(vpath, u)
  356. if err != nil {
  357. g.raiseError(err)
  358. reply, _ := vm.ToValue(false)
  359. return reply
  360. }
  361. //Get filesize of file
  362. rawsize := fs.GetFileSize(rpath)
  363. if err != nil {
  364. g.raiseError(err)
  365. reply, _ := vm.ToValue(false)
  366. return reply
  367. }
  368. reply, _ := vm.ToValue(rawsize)
  369. return reply
  370. })
  371. //fileExists("user:/Desktop/test.txt") => return true / false
  372. vm.Set("_filelib_fileExists", func(call otto.FunctionCall) otto.Value {
  373. vpath, err := call.Argument(0).ToString()
  374. if err != nil {
  375. g.raiseError(err)
  376. reply, _ := vm.ToValue(false)
  377. return reply
  378. }
  379. //Check for permission
  380. if !u.CanRead(vpath) {
  381. panic(vm.MakeCustomError("PermissionDenied", "Path access denied"))
  382. }
  383. //Translate the virtual path to realpath
  384. rpath, err := virtualPathToRealPath(vpath, u)
  385. if err != nil {
  386. g.raiseError(err)
  387. reply, _ := vm.ToValue(false)
  388. return reply
  389. }
  390. if fileExists(rpath) {
  391. reply, _ := vm.ToValue(true)
  392. return reply
  393. } else {
  394. reply, _ := vm.ToValue(false)
  395. return reply
  396. }
  397. })
  398. //fileExists("user:/Desktop/test.txt") => return true / false
  399. vm.Set("_filelib_isDir", func(call otto.FunctionCall) otto.Value {
  400. vpath, err := call.Argument(0).ToString()
  401. if err != nil {
  402. g.raiseError(err)
  403. reply, _ := vm.ToValue(false)
  404. return reply
  405. }
  406. //Check for permission
  407. if !u.CanRead(vpath) {
  408. panic(vm.MakeCustomError("PermissionDenied", "Path access denied: "+vpath))
  409. }
  410. //Translate the virtual path to realpath
  411. rpath, err := virtualPathToRealPath(vpath, u)
  412. if err != nil {
  413. g.raiseError(err)
  414. reply, _ := vm.ToValue(false)
  415. return reply
  416. }
  417. if _, err := os.Stat(rpath); os.IsNotExist(err) {
  418. //File not exists
  419. panic(vm.MakeCustomError("File Not Exists", "Required path not exists"))
  420. }
  421. if IsDir(rpath) {
  422. reply, _ := vm.ToValue(true)
  423. return reply
  424. } else {
  425. reply, _ := vm.ToValue(false)
  426. return reply
  427. }
  428. })
  429. //Make directory command
  430. vm.Set("_filelib_mkdir", func(call otto.FunctionCall) otto.Value {
  431. vdir, err := call.Argument(0).ToString()
  432. if err != nil {
  433. return otto.FalseValue()
  434. }
  435. //Check for permission
  436. if !u.CanWrite(vdir) {
  437. panic(vm.MakeCustomError("PermissionDenied", "Path access denied"))
  438. }
  439. //Translate the path to realpath
  440. rdir, err := virtualPathToRealPath(vdir, u)
  441. if err != nil {
  442. log.Println(err.Error())
  443. return otto.FalseValue()
  444. }
  445. //Create the directory at rdir location
  446. err = os.MkdirAll(rdir, 0755)
  447. if err != nil {
  448. log.Println(err.Error())
  449. return otto.FalseValue()
  450. }
  451. return otto.TrueValue()
  452. })
  453. //Get MD5 of the given filepath
  454. vm.Set("_filelib_md5", func(call otto.FunctionCall) otto.Value {
  455. log.Println("Call to MD5 Functions!")
  456. return otto.FalseValue()
  457. })
  458. //Get the root name of the given virtual path root
  459. vm.Set("_filelib_rname", func(call otto.FunctionCall) otto.Value {
  460. //Get virtual path from the function input
  461. vpath, err := call.Argument(0).ToString()
  462. if err != nil {
  463. g.raiseError(err)
  464. return otto.FalseValue()
  465. }
  466. //Get fs handler from the vpath
  467. fsHandler, err := u.GetFileSystemHandlerFromVirtualPath(vpath)
  468. if err != nil {
  469. g.raiseError(err)
  470. return otto.FalseValue()
  471. }
  472. //Return the name of the fsHandler
  473. name, _ := vm.ToValue(fsHandler.Name)
  474. return name
  475. })
  476. vm.Set("_filelib_mtime", func(call otto.FunctionCall) otto.Value {
  477. vpath, err := call.Argument(0).ToString()
  478. if err != nil {
  479. g.raiseError(err)
  480. reply, _ := vm.ToValue(false)
  481. return reply
  482. }
  483. //Check for permission
  484. if !u.CanRead(vpath) {
  485. panic(vm.MakeCustomError("PermissionDenied", "Path access denied"))
  486. }
  487. parseToUnix, err := call.Argument(1).ToBoolean()
  488. if err != nil {
  489. parseToUnix = false
  490. }
  491. rpath, err := virtualPathToRealPath(vpath, u)
  492. if err != nil {
  493. log.Println(err.Error())
  494. return otto.FalseValue()
  495. }
  496. info, err := os.Stat(rpath)
  497. if err != nil {
  498. log.Println(err.Error())
  499. return otto.FalseValue()
  500. }
  501. modTime := info.ModTime()
  502. if parseToUnix {
  503. result, _ := otto.ToValue(modTime.Unix())
  504. return result
  505. } else {
  506. result, _ := otto.ToValue(modTime.Format("2006-01-02 15:04:05"))
  507. return result
  508. }
  509. })
  510. /*
  511. vm.Set("_filelib_decodeURI", func(call otto.FunctionCall) otto.Value {
  512. originalURI, err := call.Argument(0).ToString()
  513. if err != nil {
  514. g.raiseError(err)
  515. reply, _ := vm.ToValue(false)
  516. return reply
  517. }
  518. decodedURI := specialURIDecode(originalURI)
  519. result, err := otto.ToValue(decodedURI)
  520. if err != nil {
  521. g.raiseError(err)
  522. reply, _ := vm.ToValue(false)
  523. return reply
  524. }
  525. return result
  526. })
  527. */
  528. //Other file operations, wip
  529. //Wrap all the native code function into an imagelib class
  530. vm.Run(`
  531. var filelib = {};
  532. filelib.writeFile = _filelib_writeFile;
  533. filelib.readFile = _filelib_readFile;
  534. filelib.deleteFile = _filelib_deleteFile;
  535. filelib.readdir = _filelib_readdir;
  536. filelib.walk = _filelib_walk;
  537. filelib.glob = _filelib_glob;
  538. filelib.aglob = _filelib_aglob;
  539. filelib.filesize = _filelib_filesize;
  540. filelib.fileExists = _filelib_fileExists;
  541. filelib.isDir = _filelib_isDir;
  542. filelib.md5 = _filelib_md5;
  543. filelib.mkdir = _filelib_mkdir;
  544. filelib.mtime = _filelib_mtime;
  545. filelib.rname = _filelib_rname;
  546. `)
  547. }