agi.file.go 16 KB

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