system.file_system.go 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796
  1. package main
  2. import (
  3. "net/http"
  4. "path/filepath"
  5. "strings"
  6. "encoding/json"
  7. "os"
  8. "io"
  9. "io/ioutil"
  10. "fmt"
  11. "time"
  12. "sort"
  13. "strconv"
  14. "mime/multipart"
  15. "net/url"
  16. "runtime"
  17. "errors"
  18. "compress/flate"
  19. dircpy "github.com/otiai10/copy"
  20. archiver "github.com/mholt/archiver/v3"
  21. mimetype "github.com/gabriel-vasile/mimetype"
  22. "log"
  23. )
  24. func system_fs_service_init(){
  25. //Register all endpoints
  26. http.HandleFunc("/system/file_system/validateFileOpr", system_fs_validateFileOpr)
  27. http.HandleFunc("/system/file_system/fileOpr", system_fs_handleOpr)
  28. http.HandleFunc("/system/file_system/listDir", system_fs_handleList)
  29. http.HandleFunc("/system/file_system/listRoots", system_fs_listRoot)
  30. http.HandleFunc("/system/file_system/listDrives", system_fs_listDrives)
  31. http.HandleFunc("/system/file_system/newItem", system_fs_handleNewObjects)
  32. http.HandleFunc("/system/file_system/preference", system_fs_handleUserPreference)
  33. http.HandleFunc("/system/file_system/upload", system_fs_handleUpload)
  34. http.HandleFunc("/system/file_system/listTrash", system_fs_scanTrashBin)
  35. http.HandleFunc("/system/file_system/clearTrash", system_fs_clearTrashBin)
  36. http.HandleFunc("/system/file_system/restoreTrash", system_fs_restoreFile)
  37. http.HandleFunc("/system/file_system/zipHandler", system_fs_zipHandler)
  38. http.HandleFunc("/system/file_system/getProperties", system_fs_getFileProperties)
  39. http.HandleFunc("/system/file_system/pathTranslate", system_fs_handlePathTranslate)
  40. http.HandleFunc("/system/file_system/handleFileWrite", system_fs_handleFileWrite)
  41. //Register the module
  42. registerModule(moduleInfo{
  43. Name: "File Manager",
  44. Group: "System Tools",
  45. IconPath: "SystemAO/file_system/img/small_icon.png",
  46. Version: "1.0",
  47. StartDir: "SystemAO/file_system/file_explorer.html",
  48. SupportFW: true,
  49. InitFWSize: []int{1080,580},
  50. LaunchFWDir: "SystemAO/file_system/file_explorer.html",
  51. SupportEmb: false,
  52. })
  53. //Register the Trashbin module
  54. registerModule(moduleInfo{
  55. Name: "Trash Bin",
  56. Group: "System Tools",
  57. IconPath: "SystemAO/file_system/trashbin_img/small_icon.png",
  58. Version: "1.0",
  59. StartDir: "SystemAO/file_system/trashbin.html",
  60. SupportFW: true,
  61. InitFWSize: []int{1080,580},
  62. LaunchFWDir: "SystemAO/file_system/trashbin.html",
  63. SupportEmb: false,
  64. SupportedExt: []string{"*"},
  65. })
  66. //Create user root if not exists
  67. err := os.MkdirAll(*root_directory + "users/", 0755)
  68. if (err != nil){
  69. log.Println("Failed to create system storage root.")
  70. panic(err);
  71. os.Exit(0);
  72. }
  73. //Create database table if not exists
  74. err = system_db_newTable(sysdb, "fs");
  75. if (err != nil){
  76. log.Println("Failed to create table for file system")
  77. panic(err)
  78. os.Exit(0);
  79. }
  80. }
  81. //Handle upload.
  82. func system_fs_handleUpload(w http.ResponseWriter, r *http.Request){
  83. username, err := system_auth_getUserName(w,r);
  84. if (err != nil){
  85. sendErrorResponse(w,"User not logged in")
  86. return;
  87. }
  88. //Limit the max upload size to the user defined size
  89. if (max_upload_size != 0){
  90. r.Body = http.MaxBytesReader(w, r.Body, max_upload_size)
  91. }
  92. //Check if this is running under demo mode. If yes, reject upload
  93. if (*demo_mode){
  94. sendErrorResponse(w, "You cannot upload in demo mode")
  95. return
  96. }
  97. err = r.ParseMultipartForm(int64(*upload_buf) << 20)
  98. if (err != nil){
  99. //Filesize too big
  100. sendErrorResponse(w,"File too large");
  101. return;
  102. }
  103. file, handler, err := r.FormFile("file")
  104. if err != nil {
  105. log.Println("Error Retrieving File from upload by user: " + username)
  106. sendErrorResponse(w,"Unable to parse file from upload");
  107. return;
  108. }
  109. //Get upload target directory
  110. uploadTarget, _ := mv(r, "path",true)
  111. if (uploadTarget == ""){
  112. sendErrorResponse(w,"Upload target cannot be empty.");
  113. return;
  114. }
  115. //Translate the upload target directory
  116. realUploadPath, err := virtualPathToRealPath(uploadTarget, username);
  117. if (err != nil){
  118. sendErrorResponse(w,"Upload target is invalid or permission denied.");
  119. return;
  120. }
  121. storeFilename := handler.Filename //Filename of the uploaded file
  122. destFilepath := filepath.Clean(realUploadPath) + "/" + storeFilename
  123. //Check if the upload target is read only.
  124. if (system_storage_getAccessMode(realUploadPath, username) == "readonly"){
  125. sendErrorResponse(w,"The upload target is Read Only.");
  126. return
  127. }
  128. //Check if the filesize < user storage remaining quota
  129. if system_disk_quota_checkIfQuotaApply(destFilepath, username) && !system_disk_quota_validateQuota(username, handler.Size){
  130. //File too big to fit in user quota
  131. sendErrorResponse(w, "Storage Quota Fulled")
  132. return
  133. }
  134. //Prepare the file to be created (uploaded)
  135. destination, err := os.Create(destFilepath)
  136. if err != nil {
  137. sendErrorResponse(w, err.Error())
  138. return
  139. }
  140. //Move the file to destination file location
  141. go func(r *http.Request, file multipart.File, destination *os.File){
  142. //Do the file copying using a buffered reader
  143. defer destination.Close()
  144. defer file.Close()
  145. buf := make([]byte, 8192)
  146. for {
  147. n, err := file.Read(buf)
  148. if err != nil && err != io.EOF {
  149. log.Println(err.Error())
  150. return
  151. }
  152. if n == 0 {
  153. break
  154. }
  155. if _, err := destination.Write(buf[:n]); err != nil {
  156. log.Println(err.Error())
  157. return
  158. }
  159. }
  160. //Clear up buffered files
  161. r.MultipartForm.RemoveAll()
  162. }(r, file, destination)
  163. //Finish up the upload
  164. //fmt.Printf("Uploaded File: %+v\n", handler.Filename)
  165. //fmt.Printf("File Size: %+v\n", handler.Size)
  166. //fmt.Printf("MIME Header: %+v\n", handler.Header)
  167. //fmt.Println("Upload target: " + realUploadPath)
  168. //Fnish upload. Fix the tmp filename
  169. log.Println(username + " uploaded a file: " + handler.Filename);
  170. //Do file scaning here if needed, like compare file hash to known virus?
  171. //To be implemented
  172. sendOK(w)
  173. return
  174. }
  175. //Use for copying large file using buffering method. Allowing copying large file with little RAM
  176. func system_fs_bufferedLargeFileCopy(src string, dst string, BUFFERSIZE int64) error {
  177. sourceFileStat, err := os.Stat(src)
  178. if err != nil {
  179. return err
  180. }
  181. if !sourceFileStat.Mode().IsRegular() {
  182. return errors.New("Invalid file source")
  183. }
  184. source, err := os.Open(src)
  185. if err != nil {
  186. return err
  187. }
  188. defer source.Close()
  189. destination, err := os.Create(dst)
  190. if err != nil {
  191. return err
  192. }
  193. defer destination.Close()
  194. buf := make([]byte, BUFFERSIZE)
  195. for {
  196. n, err := source.Read(buf)
  197. if err != nil && err != io.EOF {
  198. return err
  199. }
  200. if n == 0 {
  201. break
  202. }
  203. if _, err := destination.Write(buf[:n]); err != nil {
  204. return err
  205. }
  206. }
  207. return err
  208. }
  209. //Validate if the copy and target process will involve file overwriting problem.
  210. func system_fs_validateFileOpr(w http.ResponseWriter, r *http.Request){
  211. username, err := system_auth_getUserName(w,r);
  212. if (err != nil){
  213. redirectToLoginPage(w,r)
  214. return;
  215. }
  216. vsrcFiles, _ := mv(r, "src", true);
  217. vdestFile, _ := mv(r, "dest",true);
  218. var duplicateFiles []string;
  219. //Loop through all files are see if there are duplication during copy and paste
  220. sourceFiles := []string{}
  221. decodedSourceFiles, _ := url.QueryUnescape(vsrcFiles)
  222. err = json.Unmarshal([]byte(decodedSourceFiles),&sourceFiles)
  223. if (err != nil){
  224. sendErrorResponse(w,"Source file JSON parse error.");
  225. return;
  226. }
  227. rdestFile, _ := virtualPathToRealPath(vdestFile,username);
  228. for _, file := range sourceFiles{
  229. rsrcFile, _ := virtualPathToRealPath(string(file),username);
  230. if (fileExists(rdestFile + filepath.Base(rsrcFile))){
  231. //File exists already.
  232. vpath, _ := realpathToVirtualpath(rsrcFile,username);
  233. duplicateFiles = append(duplicateFiles, vpath)
  234. }
  235. }
  236. jsonString,_ := json.Marshal(duplicateFiles);
  237. sendJSONResponse(w, string(jsonString));
  238. return;
  239. }
  240. //Scan all the directory and get trash files within the system
  241. func system_fs_scanTrashBin(w http.ResponseWriter, r *http.Request){
  242. username, err := system_auth_getUserName(w,r);
  243. if (err != nil){
  244. sendErrorResponse(w,"User not logged in")
  245. return;
  246. }
  247. type trashedFile struct{
  248. Filename string;
  249. Filepath string;
  250. FileExt string;
  251. IsDir bool;
  252. Filesize int64;
  253. RemoveTimestamp int64;
  254. RemoveDate string;
  255. OriginalPath string;
  256. OriginalFilename string;
  257. }
  258. results := []trashedFile{}
  259. files, err := system_fs_listTrash(username)
  260. if (err != nil){
  261. sendErrorResponse(w, err.Error())
  262. return
  263. }
  264. //Get information of each files and process it into results
  265. for _, file := range files{
  266. timestamp := filepath.Ext(file)[1:];
  267. originalName := strings.TrimSuffix(filepath.Base(file), filepath.Ext(filepath.Base(file)))
  268. originalExt := filepath.Ext(filepath.Base(originalName));
  269. virtualFilepath, _ := realpathToVirtualpath(file, username)
  270. virtualOrgPath, _ := realpathToVirtualpath(filepath.Dir(filepath.Dir(file)), username);
  271. rawsize, _, _, _ := system_fs_getFileSize(file)
  272. timestampInt64, _ := StringToInt64(timestamp)
  273. removeTimeDate := time.Unix(timestampInt64, 0)
  274. if IsDir(file){
  275. originalExt = ""
  276. }
  277. results = append(results, trashedFile{
  278. Filename: filepath.Base(file),
  279. Filepath: virtualFilepath,
  280. FileExt: originalExt,
  281. IsDir: IsDir(file),
  282. Filesize: int64(rawsize),
  283. RemoveTimestamp: timestampInt64,
  284. RemoveDate: timeToString(removeTimeDate),
  285. OriginalPath: virtualOrgPath,
  286. OriginalFilename: originalName,
  287. })
  288. }
  289. //Sort the results by date, latest on top
  290. sort.Slice(results[:], func(i, j int) bool {
  291. return results[i].RemoveTimestamp > results[j].RemoveTimestamp
  292. })
  293. //Format and return the json results
  294. jsonString, _ := json.Marshal(results)
  295. sendJSONResponse(w, string(jsonString))
  296. }
  297. //Restore a trashed file to its parent dir
  298. func system_fs_restoreFile(w http.ResponseWriter, r *http.Request){
  299. username, err := system_auth_getUserName(w,r);
  300. if (err != nil){
  301. sendErrorResponse(w,"User not logged in")
  302. return;
  303. }
  304. targetTrashedFile, err := mv(r, "src", true)
  305. if (err != nil){
  306. sendErrorResponse(w, "Invalid src given")
  307. return
  308. }
  309. //Translate it to realpath
  310. realpath, _ := virtualPathToRealPath(targetTrashedFile, username)
  311. if !fileExists(realpath){
  312. sendErrorResponse(w, "File not exists")
  313. return
  314. }
  315. //Check if this is really a trashed file
  316. if (filepath.Base(filepath.Dir(realpath)) != ".trash"){
  317. sendErrorResponse(w, "File not in trashbin")
  318. return;
  319. }
  320. //OK to proceed.
  321. targetPath := filepath.ToSlash(filepath.Dir(filepath.Dir(realpath))) + "/" + strings.TrimSuffix(filepath.Base(realpath), filepath.Ext(filepath.Base(realpath)))
  322. //log.Println(targetPath);
  323. os.Rename(realpath, targetPath)
  324. //Check if the parent dir has no more files. If yes, remove it
  325. filescounter, _ := filepath.Glob(filepath.Dir(realpath) + "/*");
  326. if len(filescounter) == 0{
  327. os.Remove(filepath.Dir(realpath));
  328. }
  329. sendOK(w);
  330. }
  331. //Clear all trashed file in the system
  332. func system_fs_clearTrashBin(w http.ResponseWriter, r *http.Request){
  333. username, err := system_auth_getUserName(w,r);
  334. if (err != nil){
  335. sendErrorResponse(w,"User not logged in")
  336. return;
  337. }
  338. fileList, err := system_fs_listTrash(username)
  339. if (err != nil){
  340. sendErrorResponse(w, "Unable to clear trash: " + err.Error())
  341. return
  342. }
  343. //Get list success. Remove each of them.
  344. for _, file := range fileList{
  345. os.RemoveAll(file);
  346. //Check if its parent directory have no files. If yes, remove the dir itself as well.
  347. filesInThisTrashBin, _ := filepath.Glob(filepath.Dir(file) + "/*")
  348. if (len(filesInThisTrashBin) == 0){
  349. os.Remove(filepath.Dir(file))
  350. }
  351. }
  352. sendOK(w);
  353. }
  354. //Get all trash in a string list
  355. func system_fs_listTrash(username string) ([]string, error){
  356. userRoot, _ := virtualPathToRealPath("user:/", username)
  357. scanningRoots := []string{
  358. userRoot,
  359. }
  360. //Get all roots to scan
  361. for _, storage := range storages{
  362. storageRoot, err := virtualPathToRealPath(storage.Uuid + ":/", username)
  363. if (err != nil){
  364. //Unable to decode this root. Skip this
  365. continue;
  366. }
  367. scanningRoots = append(scanningRoots, storageRoot)
  368. }
  369. files := []string{}
  370. for _, rootPath := range scanningRoots{
  371. err := filepath.Walk(rootPath, func(path string, info os.FileInfo, err error) error {
  372. oneLevelUpper := filepath.Base(filepath.Dir(path))
  373. if oneLevelUpper == ".trash"{
  374. //This is a trashbin dir.
  375. files = append(files, path)
  376. }
  377. return nil
  378. })
  379. if (err != nil){
  380. return []string{}, errors.New("Failed to scan file system.")
  381. }
  382. }
  383. return files, nil
  384. }
  385. /*
  386. Handle new file or folder functions
  387. Required information
  388. @type {folder / file}
  389. @ext {any that is listed in the template folder}
  390. if no paramter is passed in, default listing all the supported template file
  391. */
  392. func system_fs_handleNewObjects(w http.ResponseWriter, r *http.Request){
  393. username, err := system_auth_getUserName(w,r);
  394. if (err != nil){
  395. redirectToLoginPage(w,r)
  396. return;
  397. }
  398. fileType, _ := mv(r, "type", true) //File creation type, {file, folder}
  399. vsrc, _ := mv(r, "src", true) //Virtual file source folder, do not include filename
  400. filename, _ := mv(r, "filename", true) //Filename for the new file
  401. if (fileType == "" && filename == ""){
  402. //List all the supported new filetype
  403. if (!fileExists("system/newitem/")){
  404. os.MkdirAll("system/newitem/",0755)
  405. }
  406. type newItemObject struct{
  407. Desc string;
  408. Ext string;
  409. }
  410. var newItemList []newItemObject;
  411. newItemTemplate,_ := filepath.Glob("system/newitem/*");
  412. for _, file := range newItemTemplate{
  413. thisItem := new(newItemObject)
  414. thisItem.Desc = strings.TrimSuffix(filepath.Base(file), filepath.Ext(file))
  415. thisItem.Ext = filepath.Ext(file)[1:]
  416. newItemList = append(newItemList, *thisItem)
  417. }
  418. jsonString, err := json.Marshal(newItemList)
  419. if (err != nil){
  420. log.Fatal("Unable to parse JSON string for new item list!")
  421. sendErrorResponse(w,"Unable to parse new item list. See server log for more information.")
  422. return;
  423. }
  424. sendJSONResponse(w,string(jsonString));
  425. return;
  426. }else if (fileType != "" && filename != ""){
  427. if (vsrc == ""){
  428. sendErrorResponse(w,"Missing paramter: 'src'")
  429. return;
  430. }
  431. //Translate the path to realpath
  432. rpath, err := virtualPathToRealPath(vsrc, username)
  433. if (err != nil){
  434. sendErrorResponse(w,"Invalid path given.")
  435. return;
  436. }
  437. //Check if directory is readonly
  438. if (system_storage_getAccessMode(rpath, username) == "readonly"){
  439. sendErrorResponse(w,"This directory is Read Only.");
  440. return
  441. }
  442. //Check if the file already exists. If yes, fix its filename.
  443. newfilePath := rpath + filename
  444. if (fileType == "file"){
  445. for fileExists(newfilePath){
  446. sendErrorResponse(w,"Given filename already exists.")
  447. return;
  448. }
  449. ext := filepath.Ext(filename)
  450. if (ext == ""){
  451. //This is a file with no extension.
  452. f, err := os.Create(newfilePath)
  453. if err != nil {
  454. log.Fatal(err)
  455. sendErrorResponse(w,err.Error())
  456. return
  457. }
  458. f.Close()
  459. }else{
  460. templateFile, _ := filepath.Glob("system/newitem/*" + ext);
  461. if (len(templateFile) == 0){
  462. //This file extension is not in template
  463. f, err := os.Create(newfilePath)
  464. if err != nil {
  465. log.Fatal(err)
  466. sendErrorResponse(w,err.Error())
  467. return
  468. }
  469. f.Close()
  470. }else{
  471. //Copy file from templateFile[0] to current dir with the given name
  472. input, _ := ioutil.ReadFile(templateFile[0])
  473. err := ioutil.WriteFile(newfilePath, input, 0755)
  474. if err != nil {
  475. log.Fatal(err)
  476. sendErrorResponse(w,err.Error())
  477. return
  478. }
  479. }
  480. }
  481. }else if (fileType == "folder"){
  482. if (fileExists(newfilePath)){
  483. sendErrorResponse(w,"Given folder already exists.")
  484. return;
  485. }
  486. //Create the folder at target location
  487. err := os.Mkdir(newfilePath,0755)
  488. if (err != nil){
  489. sendErrorResponse(w,err.Error())
  490. return;
  491. }
  492. }
  493. sendJSONResponse(w, "\"OK\"");
  494. }else{
  495. sendErrorResponse(w,"Missing paramter(s).")
  496. return;
  497. }
  498. }
  499. /*
  500. Handle file operations
  501. Support {move, copy, delete, recycle, rename}
  502. */
  503. //Handle file operations.
  504. func system_fs_handleOpr(w http.ResponseWriter, r *http.Request){
  505. username, err := system_auth_getUserName(w,r);
  506. if (err != nil){
  507. redirectToLoginPage(w,r)
  508. return;
  509. }
  510. operation, _ := mv(r, "opr",true);
  511. vsrcFiles, _ := mv(r, "src", true);
  512. vdestFile, _ := mv(r, "dest",true);
  513. vnfilenames, _ := mv(r,"new",true); //Only use when rename or create new file / folder
  514. //Check if operation valid.
  515. if (operation == ""){
  516. //Undefined operations.
  517. sendErrorResponse(w,"Undefined operations paramter: Missing 'opr' in request header.")
  518. return;
  519. }
  520. //As the user can pass in multiple source files at the same time, parse sourceFiles from json string
  521. var sourceFiles []string;
  522. //This line is required in order to allow passing of special charaters
  523. decodedSourceFiles := system_fs_specialURIDecode(vsrcFiles)
  524. err = json.Unmarshal([]byte(decodedSourceFiles),&sourceFiles)
  525. if (err != nil){
  526. sendErrorResponse(w,"Source file JSON parse error.");
  527. return;
  528. }
  529. //Check if new filenames are also valid. If yes, translate it into string array
  530. var newFilenames []string
  531. if (vnfilenames != ""){
  532. vnfilenames, _ := url.QueryUnescape(vnfilenames)
  533. err = json.Unmarshal([]byte(vnfilenames),&newFilenames)
  534. if (err != nil){
  535. sendErrorResponse(w,"Unable to parse JSOn for new filenames");
  536. return;
  537. }
  538. }
  539. for i, vsrcFile := range sourceFiles{
  540. //Convert the virtual path to realpath on disk
  541. rsrcFile, _ := virtualPathToRealPath(string(vsrcFile),username);
  542. rdestFile, _ := virtualPathToRealPath(vdestFile,username);
  543. //Check if the source file exists
  544. if (!fileExists(rsrcFile)){
  545. sendErrorResponse(w,"Source file not exists.");
  546. return;
  547. }
  548. if (operation == "rename"){
  549. //Check if the usage is correct.
  550. if (vdestFile != ""){
  551. sendErrorResponse(w,"Rename only accept 'src' and 'new'. Please use move if you want to move a file.");
  552. return;
  553. }
  554. //Check if new name paramter is passed in.
  555. if (len(newFilenames) == 0){
  556. sendErrorResponse(w,"Missing paramter (JSON string): 'new'");
  557. return;
  558. }
  559. //Check if the source filenames and new filenanmes match
  560. if (len(newFilenames) != len(sourceFiles)){
  561. sendErrorResponse(w,"New filenames do not match with source filename's length.");
  562. return
  563. }
  564. //Check if the target dir is not readonly
  565. if (system_storage_getAccessMode(rsrcFile, username) == "readonly"){
  566. sendErrorResponse(w,"This directory is Read Only.");
  567. return
  568. }
  569. thisFilename := newFilenames[i]
  570. //Check if the name already exists. If yes, return false
  571. if (fileExists(filepath.Dir(rsrcFile) + "/" + thisFilename)){
  572. sendErrorResponse(w,"File already exists");
  573. return;
  574. }
  575. //Everything is ok. Rename the file.
  576. targetNewName := filepath.Dir(rsrcFile) + "/" + thisFilename;
  577. err = os.Rename(rsrcFile,targetNewName)
  578. if (err != nil){
  579. sendErrorResponse(w,err.Error());
  580. return;
  581. }
  582. }else if (operation == "move"){
  583. //File move operation. Check if the source file / dir and target directory exists
  584. /*
  585. //Example usage from file explorer
  586. $.ajax({
  587. type: 'POST',
  588. url: `/system/file_system/fileOpr`,
  589. data: {opr: "move" ,src: JSON.stringify(fileList), dest: targetDir},
  590. success: function(data){
  591. if (data.error !== undefined){
  592. msgbox("remove",data.error);
  593. }else{
  594. //OK, do something
  595. }
  596. }
  597. });
  598. */
  599. if (!fileExists(rsrcFile)){
  600. sendErrorResponse(w,"Source file not exists");
  601. return;
  602. }
  603. //Check if the source file is read only.
  604. if (system_storage_getAccessMode(rsrcFile, username) == "readonly"){
  605. sendErrorResponse(w,"This source file is Read Only.");
  606. return
  607. }
  608. if (rdestFile == ""){
  609. sendErrorResponse(w, "Undefined dest location.");
  610. return;
  611. }
  612. srcRealpath, _ := filepath.Abs(rsrcFile);
  613. destRealpath, _ := filepath.Abs(rdestFile);
  614. if (IsDir(rsrcFile) && strings.Contains(destRealpath, srcRealpath)){
  615. //Recursive operation. Reject
  616. sendErrorResponse(w,"Recursive move operation.");
  617. return;
  618. }
  619. if (!fileExists(rdestFile)){
  620. if (fileExists(filepath.Dir(rdestFile))){
  621. //User pass in the whole path for the folder. Report error usecase.
  622. sendErrorResponse(w,"Dest location should be an existing folder instead of the full path of the moved file.");
  623. return;
  624. }
  625. sendErrorResponse(w, "Dest folder not found");
  626. return;
  627. }
  628. //Fix the lacking / at the end if true
  629. if (rdestFile[len(rdestFile)-1:] != "/"){
  630. rdestFile = rdestFile + "/"
  631. }
  632. //Check if the source and destination folder are under the same root. If yes, use os.Rename for faster move operations
  633. underSameRoot := false;
  634. //Check if the two files are under the same user root path
  635. thisRoot, _ := filepath.Abs(*root_directory + "users/" + username + "/");
  636. srcAbs, _ := filepath.Abs(rsrcFile);
  637. destAbs, _ := filepath.Abs(rdestFile);
  638. if (strings.Contains(srcAbs, thisRoot) && strings.Contains(destAbs, thisRoot)){
  639. //File is both under user root.
  640. underSameRoot = true;
  641. }else{
  642. //Check other storage path and see if they are under the same root
  643. for _, rootPath := range storages{
  644. thisRoot = rootPath.Path
  645. thisRootAbs, err := filepath.Abs(thisRoot)
  646. if (err != nil){
  647. continue;
  648. }
  649. if (strings.Contains(srcAbs,thisRootAbs) && strings.Contains(destAbs,thisRootAbs)){
  650. underSameRoot = true;
  651. }
  652. }
  653. }
  654. //Check if the target file already exists.
  655. movedFilename := filepath.Base(rsrcFile);
  656. existsOpr, _ := mv(r, "existsresp",true)
  657. if (fileExists(rdestFile + filepath.Base(rsrcFile))){
  658. //Handle cases where file already exists
  659. if (existsOpr == ""){
  660. //Do not specific file exists principle
  661. sendErrorResponse(w, "Destination file already exists.");
  662. return;
  663. }else if (existsOpr == "skip"){
  664. //Skip this file
  665. break;
  666. }else if (existsOpr == "overwrite"){
  667. //Continue with the following code
  668. //Check if the copy and paste dest are identical
  669. if (rsrcFile == (rdestFile + filepath.Base(rsrcFile))){
  670. //Source and target identical. Cannot overwrite.
  671. sendErrorResponse(w,"Source and destination paths are identical.");
  672. return;
  673. }
  674. }else if (existsOpr == "keep"){
  675. //Keep the file but saved with 'Copy' suffix
  676. newFilename := strings.TrimSuffix(filepath.Base(rsrcFile), filepath.Ext(rsrcFile)) + " - Copy" + filepath.Ext(rsrcFile);
  677. //Check if the newFilename already exists. If yes, continue adding suffix
  678. duplicateCounter := 0;
  679. for fileExists(rdestFile + newFilename){
  680. duplicateCounter++;
  681. newFilename = strings.TrimSuffix(filepath.Base(rsrcFile), filepath.Ext(rsrcFile)) + " - Copy(" + strconv.Itoa(duplicateCounter)+ ")" + filepath.Ext(rsrcFile);
  682. if (duplicateCounter > 1024){
  683. //Maxmium loop encountered. For thread safty, terminate here
  684. sendErrorResponse(w, "Too many copies of identical files.");
  685. return;
  686. }
  687. }
  688. movedFilename = newFilename
  689. }else{
  690. //This exists opr not supported.
  691. sendErrorResponse(w, "Unknown file exists rules given.");
  692. return;
  693. }
  694. }
  695. if (underSameRoot){
  696. //Ready to move with the quick rename method
  697. realDest := rdestFile + movedFilename;
  698. os.Rename(rsrcFile,realDest)
  699. }else{
  700. //Ready to move. Check if both folder are located in the same root devices. If not, use copy and delete method.
  701. if (IsDir(rsrcFile)){
  702. //Source file is directory. CopyFolder
  703. realDest := rdestFile + movedFilename;
  704. err := dircpy.Copy(rsrcFile, realDest)
  705. if (err != nil){
  706. sendErrorResponse(w,err.Error());
  707. return;
  708. }
  709. //Move completed. Remove source file.
  710. os.RemoveAll(rsrcFile)
  711. }else{
  712. //Source is file only. Copy file.
  713. realDest := rdestFile + movedFilename;
  714. source, err := os.Open(rsrcFile)
  715. if err != nil {
  716. sendErrorResponse(w,err.Error());
  717. return;
  718. }
  719. destination, err := os.Create(realDest)
  720. if err != nil {
  721. sendErrorResponse(w,err.Error());
  722. return;
  723. }
  724. io.Copy(destination, source)
  725. source.Close()
  726. destination.Close()
  727. //Delete the source file after copy
  728. err = os.Remove(rsrcFile)
  729. if (err != nil){
  730. sendErrorResponse(w,err.Error());
  731. return;
  732. }
  733. }
  734. }
  735. }else if (operation == "copy"){
  736. //Copy file. See move example and change 'opr' to 'copy'
  737. if (!fileExists(rsrcFile)){
  738. sendErrorResponse(w,"Source file not exists");
  739. return;
  740. }
  741. //Check if the desintation is read only.
  742. if (system_storage_getAccessMode(rdestFile, username) == "readonly"){
  743. sendErrorResponse(w,"This directory is Read Only.");
  744. return
  745. }
  746. if (!fileExists(rdestFile)){
  747. if (fileExists(filepath.Dir(rdestFile))){
  748. //User pass in the whole path for the folder. Report error usecase.
  749. sendErrorResponse(w,"Dest location should be an existing folder instead of the full path of the copied file.");
  750. return;
  751. }
  752. sendErrorResponse(w, "Dest folder not found");
  753. return;
  754. }
  755. srcRealpath, _ := filepath.Abs(rsrcFile);
  756. destRealpath, _ := filepath.Abs(rdestFile);
  757. if (IsDir(rsrcFile) && strings.Contains(destRealpath, srcRealpath)){
  758. //Recursive operation. Reject
  759. sendErrorResponse(w,"Recursive copy operation.");
  760. return;
  761. }
  762. //Check if the copy destination file already have an identical file
  763. copiedFilename := filepath.Base(rsrcFile);
  764. existsOpr, _ := mv(r, "existsresp",true)
  765. if (fileExists(rdestFile + filepath.Base(rsrcFile))){
  766. if (existsOpr == ""){
  767. //Do not specific file exists principle
  768. sendErrorResponse(w, "Destination file already exists.");
  769. return;
  770. }else if (existsOpr == "skip"){
  771. //Skip this file
  772. break;
  773. }else if (existsOpr == "overwrite"){
  774. //Continue with the following code
  775. //Check if the copy and paste dest are identical
  776. if (rsrcFile == (rdestFile + filepath.Base(rsrcFile))){
  777. //Source and target identical. Cannot overwrite.
  778. sendErrorResponse(w,"Source and destination paths are identical.");
  779. return;
  780. }
  781. }else if (existsOpr == "keep"){
  782. //Keep the file but saved with 'Copy' suffix
  783. newFilename := strings.TrimSuffix(filepath.Base(rsrcFile), filepath.Ext(rsrcFile)) + " - Copy" + filepath.Ext(rsrcFile);
  784. //Check if the newFilename already exists. If yes, continue adding suffix
  785. duplicateCounter := 0;
  786. for fileExists(rdestFile + newFilename){
  787. duplicateCounter++;
  788. newFilename = strings.TrimSuffix(filepath.Base(rsrcFile), filepath.Ext(rsrcFile)) + " - Copy(" + strconv.Itoa(duplicateCounter)+ ")" + filepath.Ext(rsrcFile);
  789. if (duplicateCounter > 1024){
  790. //Maxmium loop encountered. For thread safty, terminate here
  791. sendErrorResponse(w, "Too many copies of identical files.");
  792. return;
  793. }
  794. }
  795. copiedFilename = newFilename
  796. }else{
  797. //This exists opr not supported.
  798. sendErrorResponse(w, "Unknown file exists rules given.");
  799. return;
  800. }
  801. }
  802. //Fix the lacking / at the end if true
  803. if (rdestFile[len(rdestFile)-1:] != "/"){
  804. rdestFile = rdestFile + "/"
  805. }
  806. //Ready to move. Check if both folder are located in the same root devices. If not, use copy and delete method.
  807. if (IsDir(rsrcFile)){
  808. //Source file is directory. CopyFolder
  809. realDest := rdestFile + copiedFilename;
  810. err := dircpy.Copy(rsrcFile, realDest)
  811. if (err != nil){
  812. sendErrorResponse(w,err.Error());
  813. return;
  814. }
  815. }else{
  816. //Source is file only. Copy file.
  817. realDest := rdestFile + copiedFilename;
  818. source, err := os.Open(rsrcFile)
  819. if err != nil {
  820. sendErrorResponse(w,err.Error());
  821. return;
  822. }
  823. destination, err := os.Create(realDest)
  824. if err != nil {
  825. sendErrorResponse(w,err.Error());
  826. return;
  827. }
  828. _, err = io.Copy(destination, source)
  829. if (err != nil){
  830. sendErrorResponse(w,err.Error());
  831. return;
  832. }
  833. source.Close()
  834. destination.Close()
  835. }
  836. }else if (operation == "delete"){
  837. //Delete the file permanently
  838. if (!fileExists(rsrcFile)){
  839. sendErrorResponse(w,"Source file not exists");
  840. return;
  841. }
  842. //Check if the desintation is read only.
  843. if (system_storage_getAccessMode(rsrcFile, username) == "readonly"){
  844. sendErrorResponse(w,"This directory is Read Only.");
  845. return
  846. }
  847. os.RemoveAll(rsrcFile);
  848. }else if (operation == "recycle"){
  849. //Put it into a subfolder named trash and allow it to to be removed later
  850. if (!fileExists(rsrcFile)){
  851. sendErrorResponse(w, "Source file not exists.")
  852. return;
  853. }
  854. //Check if the upload target is read only.
  855. if (system_storage_getAccessMode(rsrcFile, username) == "readonly"){
  856. sendErrorResponse(w,"This directory is Read Only.");
  857. return
  858. }
  859. //Create a trash directory for this folder
  860. trashDir := filepath.ToSlash(filepath.Dir(rsrcFile)) + "/.trash/";
  861. os.MkdirAll(trashDir, 0755)
  862. os.Rename(rsrcFile, trashDir + filepath.Base(rsrcFile) + "." + Int64ToString(GetUnixTime()))
  863. }else{
  864. sendErrorResponse(w,"Unknown file opeartion given.")
  865. return;
  866. }
  867. }
  868. sendJSONResponse(w,"\"OK\"");
  869. return;
  870. }
  871. //Allow systems to store key value pairs in the database as preferences.
  872. func system_fs_handleUserPreference(w http.ResponseWriter, r *http.Request){
  873. username, err := system_auth_getUserName(w,r);
  874. if (err != nil){
  875. redirectToLoginPage(w,r)
  876. return;
  877. }
  878. key, _ := mv(r, "key",false)
  879. value, _ := mv(r, "value",false)
  880. if (key != "" && value == ""){
  881. //Get mode. Read the prefernece with given key
  882. result := ""
  883. err := system_db_read(sysdb, "fs", "pref/" + key + "/" + username, &result);
  884. if (err != nil){
  885. sendJSONResponse(w,"{\"error\":\"Key not found.\"}")
  886. return;
  887. }
  888. sendTextResponse(w,result);
  889. }else if (key != "" && value != ""){
  890. //Set mode. Set the preference with given key
  891. system_db_write(sysdb, "fs","pref/" + key + "/" + username, value)
  892. sendJSONResponse(w,"\"OK\"")
  893. }
  894. }
  895. func system_fs_listDrives(w http.ResponseWriter, r *http.Request){
  896. if (system_auth_chkauth(w,r) == false){
  897. redirectToLoginPage(w,r)
  898. return;
  899. }
  900. type driveInfo struct{
  901. Drivepath string;
  902. DriveFreeSpace uint64;
  903. DriveTotalSpace uint64;
  904. DriveAvailSpace uint64;
  905. }
  906. var drives []driveInfo;
  907. if runtime.GOOS == "windows" {
  908. //Under windows
  909. for _, drive := range "ABCDEFGHIJKLMNOPQRSTUVWXYZ"{
  910. f, err := os.Open(string(drive)+":\\")
  911. if err == nil {
  912. thisdrive := new(driveInfo);
  913. thisdrive.Drivepath = string(drive) + ":\\"
  914. free, total, avail := system_storage_getDriveCapacity(string(drive) + ":\\");
  915. thisdrive.DriveFreeSpace = free;
  916. thisdrive.DriveTotalSpace = total;
  917. thisdrive.DriveAvailSpace = avail;
  918. drives = append(drives,*thisdrive)
  919. f.Close()
  920. }
  921. }
  922. } else {
  923. //Under linux environment
  924. //Append all the virtual directories root as root instead
  925. storageDevices := []string{ *root_directory + "users"}
  926. for _, vstorage := range storages{
  927. storageDevices = append(storageDevices, vstorage.Path)
  928. }
  929. //List all storage information of each devices
  930. for _, dev := range storageDevices{
  931. thisdrive := new(driveInfo);
  932. thisdrive.Drivepath = filepath.Base(dev)
  933. free, total, avail := system_storage_getDriveCapacity(string(dev));
  934. thisdrive.DriveFreeSpace = free;
  935. thisdrive.DriveTotalSpace = total;
  936. thisdrive.DriveAvailSpace = avail;
  937. drives = append(drives,*thisdrive)
  938. }
  939. }
  940. jsonString, _ := json.Marshal(drives);
  941. sendJSONResponse(w,string(jsonString))
  942. }
  943. func system_fs_listRoot(w http.ResponseWriter, r *http.Request){
  944. username, err := system_auth_getUserName(w,r);
  945. if (err != nil){
  946. //user not logged in. Redirect to login page.
  947. redirectToLoginPage(w,r)
  948. return;
  949. }
  950. userRoot, _ := mv(r,"user",false);
  951. if (userRoot == "true"){
  952. type fileObject struct{
  953. Filename string;
  954. Filepath string;
  955. IsDir bool;
  956. }
  957. //List the root media folders under user:/
  958. var filesInUserRoot []fileObject;
  959. filesInRoot, _ := filepath.Glob( *root_directory + "users/" + username + "/*")
  960. for _, file := range filesInRoot{
  961. thisFile := new(fileObject)
  962. thisFile.Filename = filepath.Base(file);
  963. thisFile.Filepath, _ = realpathToVirtualpath(file,username);
  964. thisFile.IsDir = IsDir(file);
  965. filesInUserRoot = append(filesInUserRoot, *thisFile)
  966. }
  967. jsonString, _ := json.Marshal(filesInUserRoot)
  968. sendJSONResponse(w,string(jsonString));
  969. }else{
  970. type rootObject struct{
  971. RootName string;
  972. RootPath string;
  973. }
  974. var roots []rootObject;
  975. roots = append(roots,rootObject{
  976. "User",
  977. "user:/",
  978. })
  979. for _, store := range storages{
  980. var thisDevice = new(rootObject)
  981. thisDevice.RootName = store.Name
  982. thisDevice.RootPath = store.Uuid + ":/"
  983. roots = append(roots, *thisDevice)
  984. }
  985. jsonString, _ := json.Marshal(roots)
  986. sendJSONResponse(w,string(jsonString));
  987. }
  988. }
  989. /*
  990. Special Glob for handling path with [ or ] inside.
  991. You can also pass in normal path for globing if you are not sure.
  992. */
  993. func system_fs_specialGlob(path string) ([]string, error){
  994. files, err := filepath.Glob(path)
  995. if (err != nil){
  996. return []string{}, err
  997. }
  998. if (strings.Contains(path, "[") == true || strings.Contains(path, "]") == true){
  999. if (len(files) == 0){
  1000. //Handle reverse check. Replace all [ and ] with *
  1001. newSearchPath := strings.ReplaceAll(path, "[","?")
  1002. newSearchPath = strings.ReplaceAll(newSearchPath, "]","?")
  1003. //Scan with all the similar structure except [ and ]
  1004. tmpFilelist, _ := filepath.Glob(newSearchPath)
  1005. for _, file := range tmpFilelist{
  1006. file = filepath.ToSlash(file)
  1007. if strings.Contains(file, filepath.ToSlash(filepath.Dir(path))){
  1008. files = append(files, file)
  1009. }
  1010. }
  1011. }
  1012. }
  1013. //Convert all filepaths to slash
  1014. for i:=0; i < len(files); i++{
  1015. files[i] = filepath.ToSlash(files[i])
  1016. }
  1017. return files, nil
  1018. }
  1019. func system_fs_specialURIDecode(inputPath string) string{
  1020. inputPath = strings.ReplaceAll(inputPath, "+","{{plus_sign}}")
  1021. inputPath, _ = url.QueryUnescape(inputPath)
  1022. inputPath = strings.ReplaceAll(inputPath, "{{plus_sign}}","+")
  1023. return inputPath;
  1024. }
  1025. func system_fs_matchFileExt(inputFilename string, extArray []string) bool{
  1026. inputExt := filepath.Ext(inputFilename);
  1027. if (stringInSlice(inputExt,extArray)){
  1028. return true
  1029. }
  1030. return false;
  1031. }
  1032. //Handle file properties request
  1033. func system_fs_getFileProperties(w http.ResponseWriter, r *http.Request){
  1034. username, err := system_auth_getUserName(w,r);
  1035. if (err != nil){
  1036. sendErrorResponse(w,"User not logged in")
  1037. return;
  1038. }
  1039. vpath, err := mv(r, "path", true)
  1040. if (err != nil){
  1041. sendErrorResponse(w, "path not defined")
  1042. return
  1043. }
  1044. rpath, err := virtualPathToRealPath(vpath, username);
  1045. if (err != nil){
  1046. sendErrorResponse(w, err.Error())
  1047. return
  1048. }
  1049. fileStat, err := os.Stat(rpath)
  1050. if (err != nil){
  1051. sendErrorResponse(w, err.Error())
  1052. return
  1053. }
  1054. type fileProperties struct{
  1055. VirtualPath string
  1056. StoragePath string
  1057. Basename string
  1058. VirtualDirname string
  1059. StorageDirname string
  1060. Ext string
  1061. MimeType string
  1062. Filesize int64
  1063. Permission string
  1064. LastModTime string
  1065. LastModUnix int64
  1066. IsDirectory bool
  1067. }
  1068. mime := "text/directory"
  1069. if (!fileStat.IsDir()){
  1070. m, _, err := system_fs_getMime(rpath)
  1071. if (err != nil){
  1072. mime = ""
  1073. }
  1074. mime = m
  1075. }
  1076. filesize := fileStat.Size()
  1077. //Get file overall size if this is folder
  1078. if (fileStat.IsDir()){
  1079. var size int64
  1080. filepath.Walk(rpath, func(_ string, info os.FileInfo, err error) error {
  1081. if err != nil {
  1082. return err
  1083. }
  1084. if !info.IsDir() {
  1085. size += info.Size()
  1086. }
  1087. return err
  1088. })
  1089. filesize = size
  1090. }
  1091. result := fileProperties{
  1092. VirtualPath: vpath,
  1093. StoragePath: rpath,
  1094. Basename: filepath.Base(rpath),
  1095. VirtualDirname: filepath.ToSlash(filepath.Dir(vpath)),
  1096. StorageDirname: filepath.ToSlash(filepath.Dir(rpath)),
  1097. Ext: filepath.Ext(rpath),
  1098. MimeType: mime,
  1099. Filesize: filesize,
  1100. Permission: fileStat.Mode().Perm().String(),
  1101. LastModTime: timeToString(fileStat.ModTime()),
  1102. LastModUnix: fileStat.ModTime().Unix(),
  1103. IsDirectory: fileStat.IsDir(),
  1104. }
  1105. jsonString, _ := json.Marshal(result);
  1106. sendJSONResponse(w, string(jsonString))
  1107. }
  1108. //Get the mime type of a given file, return MIME TYPE, Original MIME Extension and Error
  1109. func system_fs_getMime(filepath string) (string, string, error){
  1110. mime, err := mimetype.DetectFile(filepath)
  1111. return mime.String(), mime.Extension(), err
  1112. }
  1113. /*
  1114. List directory in the given path
  1115. Usage: Pass in dir like the following examples:
  1116. AOR:/Desktop <= Open /user/{username}/Desktop
  1117. S1:/ <= Open {uuid=S1}/
  1118. */
  1119. func system_fs_handleList(w http.ResponseWriter, r *http.Request){
  1120. currentDir, _ := mv(r, "dir",true);
  1121. currentDir, _ = url.QueryUnescape(currentDir)
  1122. sortMode, _ := mv(r,"sort",true);
  1123. showHidden, _ := mv(r, "showHidden", true)
  1124. username, err := system_auth_getUserName(w,r);
  1125. if (err != nil){
  1126. //user not logged in. Redirect to login page.
  1127. redirectToLoginPage(w,r)
  1128. return;
  1129. }
  1130. if (currentDir == ""){
  1131. sendErrorResponse(w, "Invalid dir given.")
  1132. return;
  1133. }
  1134. //Pad a slash at the end of currentDir if not exists
  1135. if (currentDir[len(currentDir) - 1 : ] != "/"){
  1136. currentDir = currentDir + "/"
  1137. }
  1138. //Convert the virutal path to realpath
  1139. realpath, err := virtualPathToRealPath(currentDir,username);
  1140. //log.Println(realpath)
  1141. if (err != nil){
  1142. sendTextResponse(w,"Error. Unable to parse path. " + err.Error());
  1143. return
  1144. }
  1145. if (!fileExists(realpath)){
  1146. userRoot, _ := virtualPathToRealPath("user:", username);
  1147. if (filepath.Clean(realpath) == filepath.Clean(userRoot)){
  1148. //Initiate user folder
  1149. system_file_initUserRoot(username);
  1150. }else{
  1151. //Folder not exists
  1152. sendJSONResponse(w,"{\"error\":\"Folder not exists\"}");
  1153. return;
  1154. }
  1155. }
  1156. if (sortMode == ""){
  1157. sortMode = "default"
  1158. }
  1159. //Check for really special exception in where the path contains [ or ] which cannot be handled via Golang Glob function
  1160. files, _ := system_fs_specialGlob(filepath.Clean(realpath) + "/*")
  1161. /*
  1162. //Moved to system_fs_specialGlob function
  1163. files, _ := filepath.Glob(realpath + "*")
  1164. if (strings.Contains(realpath, "[") == true || strings.Contains(realpath, "]") == true){
  1165. if (len(files) == 0){
  1166. //Handle reverse check. Replace all [ and ] with *
  1167. newSearchPath := strings.ReplaceAll(realpath, "[","*")
  1168. newSearchPath = strings.ReplaceAll(newSearchPath, "]","*")
  1169. //Scan with all the similar structure except [ and ]
  1170. tmpFilelist, _ := filepath.Glob(newSearchPath + "*")
  1171. for _, file := range tmpFilelist{
  1172. file = filepath.ToSlash(file)
  1173. if strings.Contains(file, realpath){
  1174. files = append(files, file)
  1175. }
  1176. }
  1177. }
  1178. }
  1179. */
  1180. type fileData struct{
  1181. Filename string;
  1182. Filepath string;
  1183. Realpath string;
  1184. IsDir bool;
  1185. Filesize float64;
  1186. Displaysize string;
  1187. }
  1188. var parsedFilelist []fileData;
  1189. for _, v := range files{
  1190. if showHidden != "true" && filepath.Base(v)[:1] == "."{
  1191. //Skipping hidden files
  1192. continue;
  1193. }
  1194. thisFile := new(fileData);
  1195. rawsize, filesize, unit, _ := system_fs_getFileSize(v)
  1196. thisFile.Filename = filepath.Base(v);
  1197. thisFile.Filepath = currentDir + filepath.Base(v);
  1198. thisFile.Realpath = v;
  1199. thisFile.IsDir = IsDir(v);
  1200. thisFile.Filesize = rawsize
  1201. thisFile.Displaysize = fmt.Sprintf("%.2f", filesize) + unit
  1202. parsedFilelist = append(parsedFilelist,*thisFile)
  1203. }
  1204. //Sort the filelist
  1205. if (sortMode == "default"){
  1206. //Sort by name, convert filename to window sorting methods
  1207. sort.Slice(parsedFilelist, func(i, j int) bool { return strings.ToLower(parsedFilelist[i].Filename) < strings.ToLower(parsedFilelist[j].Filename) })
  1208. }else if (sortMode == "reverse"){
  1209. //Sort by reverse name
  1210. sort.Slice(parsedFilelist, func(i, j int) bool { return strings.ToLower(parsedFilelist[i].Filename) > strings.ToLower(parsedFilelist[j].Filename) })
  1211. }else if (sortMode == "smallToLarge"){
  1212. sort.Slice(parsedFilelist, func(i, j int) bool { return parsedFilelist[i].Filesize < parsedFilelist[j].Filesize })
  1213. }else if (sortMode == "largeToSmall"){
  1214. sort.Slice(parsedFilelist, func(i, j int) bool { return parsedFilelist[i].Filesize > parsedFilelist[j].Filesize })
  1215. }
  1216. jsonString, _ := json.Marshal(parsedFilelist);
  1217. sendJSONResponse(w,string(jsonString))
  1218. }
  1219. /*
  1220. Virtual Path to Real path translator
  1221. Convert a virtual path like
  1222. user:/Desktop
  1223. S1:/demo
  1224. to a realpath like
  1225. ./files/users/{username}/Desktop
  1226. /media/storage1/demo/
  1227. */
  1228. func system_file_initUserRoot(username string){
  1229. //Create user subfolders
  1230. os.MkdirAll(*root_directory + "users/" + username + "/Desktop", 0755);
  1231. os.MkdirAll(*root_directory + "users/" + username + "/Music", 0755);
  1232. os.MkdirAll(*root_directory + "users/" + username + "/Video", 0755);
  1233. os.MkdirAll(*root_directory + "users/" + username + "/Document", 0755);
  1234. os.MkdirAll(*root_directory + "users/" + username + "/Photo", 0755);
  1235. }
  1236. func virtualPathToRealPath(virtualPath string, username string) (string, error){
  1237. virtualPath = strings.ReplaceAll(virtualPath,"\\","/")
  1238. virtualPath = strings.ReplaceAll(virtualPath,"../","")
  1239. if (strings.Contains(virtualPath,":") == false){
  1240. return "",errors.New("Path missing Virtual Device ID (e.g. user:/). Given: " + virtualPath)
  1241. }
  1242. //Parse the ID of the targeted virtual disk
  1243. tmp := strings.Split(virtualPath,":")
  1244. vdID := tmp[0]
  1245. pathSlice := tmp[1:]
  1246. path := strings.Join(pathSlice,":")
  1247. var realpath string;
  1248. if (vdID == "user"){
  1249. realpath = *root_directory + "users/" + username + path
  1250. }else if (vdID == "tmp"){
  1251. os.MkdirAll(filepath.Clean(*tmp_directory) + "/users/" + username, 0777)
  1252. realpath = filepath.Clean(*tmp_directory) + "/users/" + username + path
  1253. }else{
  1254. //Search for index located in the external storages
  1255. var storageRealPath string = "";
  1256. var targetStorageDevice storageDevice;
  1257. for _, storage := range storages{
  1258. if (storage.Uuid == vdID){
  1259. //This is the corret storage location
  1260. storageRealPath = storage.Path
  1261. targetStorageDevice = storage;
  1262. }
  1263. }
  1264. if (storageRealPath == ""){
  1265. //Storage device not found.
  1266. return "",errors.New("Storage device not found.")
  1267. }
  1268. if (storageRealPath[len(storageRealPath) - 1:] != "/"){
  1269. storageRealPath = storageRealPath + "/"
  1270. }
  1271. //Build real path
  1272. if (targetStorageDevice.Hierarchy == "public"){
  1273. realpath = storageRealPath + path;
  1274. }else if (targetStorageDevice.Hierarchy == "users"){
  1275. if (!fileExists(storageRealPath + "/users/")){
  1276. //Folder structure not initialized. Create this user now.
  1277. os.MkdirAll(storageRealPath + "users/" + username + "/",0755);
  1278. }
  1279. realpath = storageRealPath + "users/" + username + "/" + path;
  1280. }
  1281. }
  1282. realpath = strings.ReplaceAll(realpath, "//","/")
  1283. return realpath, nil
  1284. }
  1285. func realpathToVirtualpath(realpath string, username string) (string,error){
  1286. realpath = filepath.ToSlash(realpath)
  1287. realpath = strings.ReplaceAll(realpath,"../","")
  1288. //Get relative path of all allowed storage path. Find the one without directory travsal
  1289. var rootPaths []string;
  1290. var rootUUID []string;
  1291. var rootHierarchy []string;
  1292. //Append user root path into the rootpaths as default
  1293. rootUUID = append(rootUUID, "user:/")
  1294. rootPaths = append(rootPaths, filepath.Clean(*root_directory) + "/users/" + username + "/")
  1295. rootHierarchy = append(rootHierarchy, "users")
  1296. //Append the tmp directory as well
  1297. rootUUID = append(rootUUID, "tmp:/")
  1298. rootPaths = append(rootPaths, filepath.Clean(*tmp_directory) + "/users/" + username + "/")
  1299. rootHierarchy = append(rootHierarchy, "users")
  1300. //Process extra storage locations
  1301. for _, v := range storages{
  1302. thispath := v.Path;
  1303. if thispath[len(thispath) - 1:] != "/"{
  1304. thispath = thispath + "/"
  1305. }
  1306. rootPaths = append(rootPaths,thispath)
  1307. rootUUID = append(rootUUID,v.Uuid + ":/");
  1308. rootHierarchy = append(rootHierarchy, v.Hierarchy)
  1309. }
  1310. var relativePaths []string;
  1311. for _, path := range rootPaths{
  1312. thisRelative, err := filepath.Rel(path,realpath);
  1313. if (err != nil){
  1314. relativePaths = append(relativePaths,"../");
  1315. }else{
  1316. relativePaths = append(relativePaths,thisRelative);
  1317. }
  1318. }
  1319. //Loop through each of the relative path to see which do not have ../
  1320. var validRelativePath string = "";
  1321. var virtualRoot string = "";
  1322. var targetHierarchy string = "";
  1323. for i, path := range relativePaths{
  1324. path = strings.ReplaceAll(path,"\\","/")
  1325. if (strings.Contains(path,"../") == false){
  1326. validRelativePath = path;
  1327. virtualRoot = rootUUID[i]
  1328. targetHierarchy = rootHierarchy[i]
  1329. }
  1330. }
  1331. if (validRelativePath == ""){
  1332. return "", errors.New("Cannot parse virtualpath for this realpath")
  1333. }
  1334. //Match the storage type if it is under users mode (aka S1:/user/{username}/* => S1:/*)
  1335. if (targetHierarchy == "public"){
  1336. //Return the current path without post-processing
  1337. return virtualRoot + validRelativePath, nil
  1338. }else if (targetHierarchy == "users"){
  1339. userRootPath := "users/" + username + "/"
  1340. userRelativeVirtualPath := strings.Replace(validRelativePath,userRootPath,"",1)
  1341. userRelativeVirtualPath = virtualRoot + userRelativeVirtualPath;
  1342. return userRelativeVirtualPath, nil
  1343. }
  1344. //Unknown type. Return nothing
  1345. return "",nil
  1346. }
  1347. /*
  1348. Filesize function, return the rawsize, human readable filesize in float64 and its unit in string
  1349. Required
  1350. @path (string)
  1351. @humanReadable(bool) => If this is set to false, it will return filesize in bytes only
  1352. */
  1353. func system_fs_getFileSize(path string) (float64, float64, string, error){
  1354. file, err := os.Open(path)
  1355. if err != nil {
  1356. return -1, -1, "", errors.New("File not exists")
  1357. }
  1358. defer file.Close()
  1359. stat, err := file.Stat()
  1360. if err != nil {
  1361. return -1, -1,"",errors.New("Cannot read file statistic")
  1362. }
  1363. var bytes float64
  1364. bytes = float64(stat.Size())
  1365. var kilobytes float64
  1366. kilobytes = (bytes / 1024)
  1367. if (kilobytes < 1){
  1368. return bytes, bytes,"Bytes",nil
  1369. }
  1370. var megabytes float64
  1371. megabytes = (float64)(kilobytes / 1024)
  1372. if (megabytes < 1){
  1373. return bytes, kilobytes,"KB",nil
  1374. }
  1375. var gigabytes float64
  1376. gigabytes = (megabytes / 1024)
  1377. if (gigabytes < 1){
  1378. return bytes, megabytes,"MB",nil
  1379. }
  1380. var terabytes float64
  1381. terabytes = (gigabytes / 1024)
  1382. if (terabytes < 1){
  1383. return bytes, gigabytes,"GB",nil
  1384. }
  1385. var petabytes float64
  1386. petabytes = (terabytes / 1024)
  1387. if (petabytes < 1){
  1388. return bytes, terabytes,"TB",nil
  1389. }
  1390. var exabytes float64
  1391. exabytes = (petabytes / 1024)
  1392. if (exabytes < 1){
  1393. return bytes, petabytes,"PB",nil
  1394. }
  1395. var zettabytes float64
  1396. zettabytes = (exabytes / 1024)
  1397. if (zettabytes < 1){
  1398. return bytes, exabytes,"EB",nil
  1399. }
  1400. return -1, -1,"Too big to meausre",nil
  1401. }
  1402. /*
  1403. File zipping and unzipping functions
  1404. */
  1405. //Handle all zip related API
  1406. func system_fs_zipHandler(w http.ResponseWriter, r *http.Request){
  1407. username, err := system_auth_getUserName(w,r);
  1408. if (err != nil){
  1409. sendErrorResponse(w, "User not logged in");
  1410. return;
  1411. }
  1412. opr, err := mv(r, "opr", true)
  1413. if (err != nil){
  1414. sendErrorResponse(w, "Invalid opr or opr not defined")
  1415. return
  1416. }
  1417. vsrc, _ := mv(r, "src",true)
  1418. if (vsrc == ""){
  1419. sendErrorResponse(w, "Invalid src paramter")
  1420. return
  1421. }
  1422. vdest, _ := mv(r, "dest", true)
  1423. rdest := ""
  1424. //Convert source path from JSON string to object
  1425. virtualSourcePaths := []string{}
  1426. err = json.Unmarshal([]byte(vsrc), &virtualSourcePaths);
  1427. if (err != nil){
  1428. sendErrorResponse(w, err.Error())
  1429. return;
  1430. }
  1431. //Check each of the path
  1432. realSourcePaths := []string{}
  1433. for _, vpath := range virtualSourcePaths{
  1434. thisrpath, err := virtualPathToRealPath(vpath, username)
  1435. if (err != nil || !fileExists(thisrpath)){
  1436. sendErrorResponse(w, "File not exists: " + vpath)
  1437. return
  1438. }
  1439. realSourcePaths = append(realSourcePaths, thisrpath)
  1440. }
  1441. ///Convert dest to real if given
  1442. if (vdest != ""){
  1443. realdest, _ := virtualPathToRealPath(vdest, username)
  1444. rdest = realdest
  1445. }
  1446. if (opr == "zip"){
  1447. //Check if destination location exists
  1448. if (rdest == "" || !fileExists(filepath.Dir(rdest))){
  1449. sendErrorResponse(w, "Invalid dest location")
  1450. return
  1451. }
  1452. //OK. Create the zip at the desired location
  1453. err := system_fs_createZipFile(realSourcePaths, rdest, false);
  1454. if (err != nil){
  1455. sendErrorResponse(w, err.Error())
  1456. return;
  1457. }
  1458. sendOK(w);
  1459. }else if (opr == "tmpzip"){
  1460. //Zip to tmp folder
  1461. userTmpFolder, _ := virtualPathToRealPath("tmp:/", username)
  1462. filename := Int64ToString(GetUnixTime()) + ".zip";
  1463. rdest := filepath.Clean(userTmpFolder) + "/" + filename
  1464. log.Println(realSourcePaths, rdest);
  1465. err := system_fs_createZipFile(realSourcePaths, rdest, false);
  1466. if (err != nil){
  1467. sendErrorResponse(w, err.Error())
  1468. return;
  1469. }
  1470. //Send the tmp filename to the user
  1471. sendTextResponse(w, "tmp:/" + filename);
  1472. }else if (opr == "inspect"){
  1473. }else if (opr == "unzip"){
  1474. }
  1475. }
  1476. func system_fs_createZipFile(filelist []string, outputfile string, includeTopLevelFolder bool) error{
  1477. z := archiver.Zip{
  1478. CompressionLevel: flate.DefaultCompression,
  1479. MkdirAll: true,
  1480. SelectiveCompression: true,
  1481. OverwriteExisting: false,
  1482. ImplicitTopLevelFolder: includeTopLevelFolder,
  1483. }
  1484. err := z.Archive(filelist, outputfile)
  1485. return err
  1486. }
  1487. func system_fs_inspectZipFile(filepath string) ([]string, error){
  1488. z := archiver.Zip{}
  1489. filelist := []string{}
  1490. err := z.Walk(filepath, func(f archiver.File) error {
  1491. filelist = append(filelist, f.Name())
  1492. return nil
  1493. })
  1494. return filelist, err
  1495. }
  1496. //Translate path from and to virtual and realpath
  1497. func system_fs_handlePathTranslate(w http.ResponseWriter, r *http.Request){
  1498. username, err := system_auth_getUserName(w,r);
  1499. if (err != nil){
  1500. sendErrorResponse(w,"User not logged in")
  1501. return;
  1502. }
  1503. path, err := mv(r, "path", false)
  1504. if (err != nil){
  1505. sendErrorResponse(w, "Invalid path given")
  1506. return
  1507. }
  1508. rpath, err := virtualPathToRealPath(path, username)
  1509. if (err != nil){
  1510. //Try to convert it to virtualPath
  1511. vpath, err := realpathToVirtualpath(path, username)
  1512. if (err != nil){
  1513. sendErrorResponse(w, "Unknown path given")
  1514. }else{
  1515. jsonstring, _ := json.Marshal(vpath)
  1516. sendJSONResponse(w, string(jsonstring))
  1517. }
  1518. }else{
  1519. abrpath, _ := filepath.Abs(rpath);
  1520. jsonstring, _ := json.Marshal([]string{rpath, filepath.ToSlash(abrpath)})
  1521. sendJSONResponse(w, string(jsonstring))
  1522. }
  1523. }
  1524. //Functions for handling quick file write without the need to go through agi for simple apps
  1525. func system_fs_handleFileWrite(w http.ResponseWriter, r *http.Request){
  1526. //Get the username for this user
  1527. username, err := system_auth_getUserName(w,r);
  1528. if (err != nil){
  1529. sendErrorResponse(w,"User not logged in")
  1530. return;
  1531. }
  1532. //Get the file content and the filepath
  1533. content, _ := mv(r, "content", true)
  1534. targetFilepath, err := mv(r, "filepath", true)
  1535. if err != nil{
  1536. sendErrorResponse(w, "Filepath cannot be empty")
  1537. return
  1538. }
  1539. //Convert the filepath to realpath
  1540. rpath, err := virtualPathToRealPath(targetFilepath, username)
  1541. if err != nil{
  1542. sendErrorResponse(w, err.Error())
  1543. return
  1544. }
  1545. //Check if the path dir exists. If not, return error
  1546. if !fileExists(filepath.Dir(rpath)){
  1547. sendErrorResponse(w, "Directory not exists")
  1548. return
  1549. }
  1550. //OK. Write to that file
  1551. err = ioutil.WriteFile(rpath, []byte(content), 0755)
  1552. if (err != nil){
  1553. sendErrorResponse(w, err.Error())
  1554. return
  1555. }
  1556. sendOK(w);
  1557. }
  1558. //Check if the given filepath is and must inside the given directory path.
  1559. //You can pass both as relative
  1560. func system_fs_checkFileInDirectory(filesourcepath string, directory string) bool{
  1561. filepathAbs, err := filepath.Abs(filesourcepath)
  1562. if err != nil{
  1563. return false
  1564. }
  1565. directoryAbs, err := filepath.Abs(directory)
  1566. if err != nil{
  1567. return false
  1568. }
  1569. //Check if the filepathabs contain directoryAbs
  1570. if strings.Contains(filepathAbs, directoryAbs){
  1571. return true
  1572. }else{
  1573. return false
  1574. }
  1575. }