agi.file.go 17 KB

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