file_system.go 93 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205
  1. package main
  2. import (
  3. "crypto/sha256"
  4. "encoding/hex"
  5. "encoding/json"
  6. "fmt"
  7. "io"
  8. "log"
  9. "math"
  10. "net/http"
  11. "net/url"
  12. "os"
  13. "path/filepath"
  14. "runtime"
  15. "sort"
  16. "strconv"
  17. "strings"
  18. "time"
  19. "github.com/gorilla/websocket"
  20. uuid "github.com/satori/go.uuid"
  21. "imuslab.com/arozos/mod/common"
  22. "imuslab.com/arozos/mod/compatibility"
  23. "imuslab.com/arozos/mod/disk/hybridBackup"
  24. "imuslab.com/arozos/mod/filesystem"
  25. fs "imuslab.com/arozos/mod/filesystem"
  26. fsp "imuslab.com/arozos/mod/filesystem/fspermission"
  27. "imuslab.com/arozos/mod/filesystem/fuzzy"
  28. hidden "imuslab.com/arozos/mod/filesystem/hidden"
  29. "imuslab.com/arozos/mod/filesystem/localversion"
  30. metadata "imuslab.com/arozos/mod/filesystem/metadata"
  31. "imuslab.com/arozos/mod/filesystem/shortcut"
  32. module "imuslab.com/arozos/mod/modules"
  33. prout "imuslab.com/arozos/mod/prouter"
  34. "imuslab.com/arozos/mod/share"
  35. "imuslab.com/arozos/mod/share/shareEntry"
  36. storage "imuslab.com/arozos/mod/storage"
  37. )
  38. var (
  39. thumbRenderHandler *metadata.RenderHandler
  40. shareEntryTable *shareEntry.ShareEntryTable
  41. shareManager *share.Manager
  42. )
  43. type trashedFile struct {
  44. Filename string
  45. Filepath string
  46. FileExt string
  47. IsDir bool
  48. Filesize int64
  49. RemoveTimestamp int64
  50. RemoveDate string
  51. OriginalPath string
  52. OriginalFilename string
  53. }
  54. func FileSystemInit() {
  55. router := prout.NewModuleRouter(prout.RouterOption{
  56. ModuleName: "File Manager",
  57. AdminOnly: false,
  58. UserHandler: userHandler,
  59. DeniedHandler: func(w http.ResponseWriter, r *http.Request) {
  60. common.SendErrorResponse(w, "Permission Denied")
  61. },
  62. })
  63. //Upload related functions
  64. router.HandleFunc("/system/file_system/upload", system_fs_handleUpload)
  65. router.HandleFunc("/system/file_system/lowmemUpload", system_fs_handleLowMemoryUpload)
  66. //Other file operations
  67. router.HandleFunc("/system/file_system/validateFileOpr", system_fs_validateFileOpr)
  68. router.HandleFunc("/system/file_system/fileOpr", system_fs_handleOpr)
  69. router.HandleFunc("/system/file_system/ws/fileOpr", system_fs_handleWebSocketOpr)
  70. router.HandleFunc("/system/file_system/listDir", system_fs_handleList)
  71. router.HandleFunc("/system/file_system/listDirHash", system_fs_handleDirHash)
  72. router.HandleFunc("/system/file_system/listRoots", system_fs_listRoot)
  73. router.HandleFunc("/system/file_system/listDrives", system_fs_listDrives)
  74. router.HandleFunc("/system/file_system/newItem", system_fs_handleNewObjects)
  75. router.HandleFunc("/system/file_system/preference", system_fs_handleUserPreference)
  76. router.HandleFunc("/system/file_system/listTrash", system_fs_scanTrashBin)
  77. router.HandleFunc("/system/file_system/ws/listTrash", system_fs_WebSocketScanTrashBin)
  78. router.HandleFunc("/system/file_system/clearTrash", system_fs_clearTrashBin)
  79. router.HandleFunc("/system/file_system/restoreTrash", system_fs_restoreFile)
  80. router.HandleFunc("/system/file_system/zipHandler", system_fs_zipHandler)
  81. router.HandleFunc("/system/file_system/getProperties", system_fs_getFileProperties)
  82. router.HandleFunc("/system/file_system/pathTranslate", system_fs_handlePathTranslate)
  83. router.HandleFunc("/system/file_system/versionHistory", system_fs_FileVersionHistory)
  84. router.HandleFunc("/system/file_system/handleFilePermission", system_fs_handleFilePermission)
  85. router.HandleFunc("/system/file_system/search", system_fs_handleFileSearch)
  86. //Thumbnail caching functions
  87. router.HandleFunc("/system/file_system/handleFolderCache", system_fs_handleFolderCache)
  88. router.HandleFunc("/system/file_system/handleCacheRender", system_fs_handleCacheRender)
  89. router.HandleFunc("/system/file_system/loadThumbnail", system_fs_handleThumbnailLoad)
  90. //Directory specific config
  91. router.HandleFunc("/system/file_system/sortMode", system_fs_handleFolderSortModePreference)
  92. //Register the module
  93. moduleHandler.RegisterModule(module.ModuleInfo{
  94. Name: "File Manager",
  95. Group: "System Tools",
  96. IconPath: "SystemAO/file_system/img/small_icon.png",
  97. Version: "1.0",
  98. StartDir: "SystemAO/file_system/file_explorer.html",
  99. SupportFW: true,
  100. InitFWSize: []int{1080, 580},
  101. LaunchFWDir: "SystemAO/file_system/file_explorer.html",
  102. SupportEmb: false,
  103. })
  104. //Register the Trashbin module
  105. moduleHandler.RegisterModule(module.ModuleInfo{
  106. Name: "Trash Bin",
  107. Group: "System Tools",
  108. IconPath: "SystemAO/file_system/trashbin_img/small_icon.png",
  109. Version: "1.0",
  110. StartDir: "SystemAO/file_system/trashbin.html",
  111. SupportFW: true,
  112. InitFWSize: []int{400, 200},
  113. LaunchFWDir: "SystemAO/file_system/trashbin.html",
  114. SupportEmb: false,
  115. SupportedExt: []string{"*"},
  116. })
  117. //Register the Zip Extractor module
  118. moduleHandler.RegisterModule(module.ModuleInfo{
  119. Name: "Zip Extractor",
  120. Group: "System Tools",
  121. IconPath: "SystemAO/file_system/img/zip_extractor.png",
  122. Version: "1.0",
  123. SupportFW: false,
  124. LaunchEmb: "SystemAO/file_system/zip_extractor.html",
  125. SupportEmb: true,
  126. InitEmbSize: []int{260, 120},
  127. SupportedExt: []string{".zip"},
  128. })
  129. //Create user root if not exists
  130. err := os.MkdirAll(*root_directory+"users/", 0755)
  131. if err != nil {
  132. log.Println("Failed to create system storage root.")
  133. panic(err)
  134. }
  135. //Create database table if not exists
  136. err = sysdb.NewTable("fs")
  137. if err != nil {
  138. log.Println("Failed to create table for file system")
  139. panic(err)
  140. }
  141. //Create new table for sort preference
  142. err = sysdb.NewTable("fs-sortpref")
  143. if err != nil {
  144. log.Println("Failed to create table for file system")
  145. panic(err)
  146. }
  147. //Create a RenderHandler for caching thumbnails
  148. thumbRenderHandler = metadata.NewRenderHandler()
  149. /*
  150. Share Related Registering
  151. This section of functions create and register the file share service
  152. for the arozos
  153. */
  154. //Create a share manager to handle user file sharae
  155. shareEntryTable = shareEntry.NewShareEntryTable(sysdb)
  156. shareManager = share.NewShareManager(share.Options{
  157. AuthAgent: authAgent,
  158. ShareEntryTable: shareEntryTable,
  159. UserHandler: userHandler,
  160. HostName: *host_name,
  161. TmpFolder: *tmp_directory,
  162. })
  163. //Share related functions
  164. router.HandleFunc("/system/file_system/share/new", shareManager.HandleCreateNewShare)
  165. router.HandleFunc("/system/file_system/share/delete", shareManager.HandleDeleteShare)
  166. router.HandleFunc("/system/file_system/share/edit", shareManager.HandleEditShare)
  167. router.HandleFunc("/system/file_system/share/checkShared", shareManager.HandleShareCheck)
  168. //Handle the main share function
  169. //Share function is now routed by the main router
  170. //http.HandleFunc("/share", shareManager.HandleShareAccess)
  171. /*
  172. Nighly Tasks
  173. These functions allow file system to clear and maintain
  174. the arozos file system when no one is using the system
  175. */
  176. //Clear tmp folder if files is placed here too long
  177. nightlyManager.RegisterNightlyTask(system_fs_clearOldTmpFiles)
  178. //Clear shares that its parent file no longer exists in the system
  179. shareManager.ValidateAndClearShares()
  180. nightlyManager.RegisterNightlyTask(shareManager.ValidateAndClearShares)
  181. //Clear file version history that is more than 30 days
  182. go func() {
  183. //Start version history cleaning in background
  184. system_fs_clearVersionHistories()
  185. log.Println("[LocVer] Startup File Version History Cleaning Completed")
  186. }()
  187. log.Println("Started File Version History Cleaning in background")
  188. nightlyManager.RegisterNightlyTask(system_fs_clearVersionHistories)
  189. }
  190. /*
  191. File Search
  192. Handle file search in wildcard and recursive search
  193. */
  194. func system_fs_handleFileSearch(w http.ResponseWriter, r *http.Request) {
  195. //Get the user information
  196. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  197. if err != nil {
  198. common.SendErrorResponse(w, "User not logged in")
  199. return
  200. }
  201. //Get the search target root path
  202. vpath, err := common.Mv(r, "path", true)
  203. if err != nil {
  204. common.SendErrorResponse(w, "Invalid vpath given")
  205. return
  206. }
  207. keyword, err := common.Mv(r, "keyword", true)
  208. if err != nil {
  209. common.SendErrorResponse(w, "Invalid keyword given")
  210. return
  211. }
  212. //Check if case sensitive is enabled
  213. casesensitve, _ := common.Mv(r, "casesensitive", true)
  214. vrootID, subpath, err := fs.GetIDFromVirtualPath(vpath)
  215. var targetFSH *filesystem.FileSystemHandler = nil
  216. if err != nil {
  217. common.SendErrorResponse(w, "Invalid path given")
  218. return
  219. }
  220. targetFSH, _ = GetFsHandlerByUUID(vrootID)
  221. //Translate the vpath to realpath if this is an actual path on disk
  222. resolvedPath, err := targetFSH.FileSystemAbstraction.VirtualPathToRealPath(vpath, userinfo.Username)
  223. if err != nil {
  224. common.SendErrorResponse(w, "Invalid path given")
  225. return
  226. }
  227. rpath := resolvedPath
  228. //Check if the search mode is recursive keyword or wildcard
  229. if len(keyword) > 1 && keyword[:1] == "/" {
  230. //Wildcard
  231. //Updates 31-12-2021: Do not allow wildcard search on virtual type's FSH
  232. if targetFSH != nil && targetFSH.IsVirtual() {
  233. common.SendErrorResponse(w, "This virtual storage device do not allow wildcard search")
  234. return
  235. }
  236. wildcard := keyword[1:]
  237. matchingFiles, err := filepath.Glob(filepath.Join(rpath, wildcard))
  238. if err != nil {
  239. common.SendErrorResponse(w, err.Error())
  240. return
  241. }
  242. //Prepare result struct
  243. results := []fs.FileData{}
  244. //Process the matching files. Do not allow directory escape
  245. srcAbs, _ := filepath.Abs(rpath)
  246. srcAbs = filepath.ToSlash(srcAbs)
  247. escaped := false
  248. for _, matchedFile := range matchingFiles {
  249. absMatch, _ := filepath.Abs(matchedFile)
  250. absMatch = filepath.ToSlash(absMatch)
  251. if !strings.Contains(absMatch, srcAbs) {
  252. escaped = true
  253. }
  254. thisVpath, _ := targetFSH.FileSystemAbstraction.RealPathToVirtualPath(matchedFile, userinfo.Username)
  255. results = append(results, fs.GetFileDataFromPath(thisVpath, matchedFile, 2))
  256. }
  257. if escaped {
  258. common.SendErrorResponse(w, "Search keywords contain escape character!")
  259. return
  260. }
  261. //OK. Tidy up the results
  262. js, _ := json.Marshal(results)
  263. common.SendJSONResponse(w, string(js))
  264. } else {
  265. //Updates 2022-02-16: Build the fuzzy matcher if it is not a wildcard search
  266. matcher := fuzzy.NewFuzzyMatcher(keyword, casesensitve == "true")
  267. //Recursive keyword
  268. results := []fs.FileData{}
  269. var err error = nil
  270. if targetFSH != nil && targetFSH.UUID == "share" {
  271. //To be done: Move hardcoded vroot ID to interface for all virtual storage devices
  272. if casesensitve != "true" {
  273. keyword = strings.ToLower(keyword)
  274. }
  275. err = shareEntryTable.Walk(subpath, userinfo.Username, userinfo.GetUserPermissionGroupNames(), func(fileData fs.FileData) error {
  276. filename := filepath.Base(fileData.Filename)
  277. if casesensitve != "true" {
  278. filename = strings.ToLower(filename)
  279. }
  280. if matcher.Match(filename) {
  281. //This is a matching file
  282. if !fs.IsInsideHiddenFolder(fileData.Filepath) {
  283. results = append(results, fileData)
  284. }
  285. }
  286. return nil
  287. })
  288. } else {
  289. fshAbs := targetFSH.FileSystemAbstraction
  290. err = fshAbs.Walk(rpath, func(path string, info os.FileInfo, err error) error {
  291. thisFilename := filepath.Base(path)
  292. if casesensitve != "true" {
  293. thisFilename = strings.ToLower(thisFilename)
  294. }
  295. if !fs.IsInsideHiddenFolder(path) {
  296. if matcher.Match(thisFilename) {
  297. //This is a matching file
  298. thisVpath, _ := fshAbs.RealPathToVirtualPath(path, userinfo.Username)
  299. results = append(results, fs.GetFileDataFromPath(thisVpath, path, 2))
  300. }
  301. }
  302. return nil
  303. })
  304. }
  305. if err != nil {
  306. common.SendErrorResponse(w, err.Error())
  307. return
  308. }
  309. //OK. Tidy up the results
  310. js, _ := json.Marshal(results)
  311. common.SendJSONResponse(w, string(js))
  312. }
  313. }
  314. /*
  315. Handle low-memory upload operations
  316. This function is specailly designed to work with low memory devices
  317. (e.g. ZeroPi / Orange Pi Zero with 512MB RAM)
  318. */
  319. func system_fs_handleLowMemoryUpload(w http.ResponseWriter, r *http.Request) {
  320. //Get user info
  321. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  322. if err != nil {
  323. w.WriteHeader(http.StatusUnauthorized)
  324. w.Write([]byte("401 - Unauthorized"))
  325. return
  326. }
  327. //Get filename and upload path
  328. filename, err := common.Mv(r, "filename", false)
  329. if filename == "" || err != nil {
  330. w.WriteHeader(http.StatusInternalServerError)
  331. w.Write([]byte("500 - Invalid filename given"))
  332. return
  333. }
  334. //Get upload target directory
  335. uploadTarget, err := common.Mv(r, "path", false)
  336. if uploadTarget == "" || err != nil {
  337. w.WriteHeader(http.StatusInternalServerError)
  338. w.Write([]byte("500 - Invalid path given"))
  339. return
  340. }
  341. //Unescape the upload target path
  342. unescapedPath, err := url.PathUnescape(uploadTarget)
  343. if err != nil {
  344. unescapedPath = uploadTarget
  345. }
  346. //Check if the user can write to this folder
  347. if !userinfo.CanWrite(unescapedPath) {
  348. //No permission
  349. w.WriteHeader(http.StatusForbidden)
  350. w.Write([]byte("403 - Access Denied"))
  351. return
  352. }
  353. fsh, subpath, err := GetFSHandlerSubpathFromVpath(unescapedPath)
  354. if err != nil {
  355. w.WriteHeader(http.StatusInternalServerError)
  356. w.Write([]byte("500 - Path translation failed"))
  357. return
  358. }
  359. targetFs := fsh.FileSystemAbstraction
  360. //Translate the upload target directory
  361. realUploadPath, err := targetFs.VirtualPathToRealPath(subpath, userinfo.Username)
  362. if err != nil {
  363. w.WriteHeader(http.StatusInternalServerError)
  364. w.Write([]byte("500 - Path translation failed"))
  365. return
  366. }
  367. //Check if it is huge file upload mode
  368. isHugeFile := false
  369. hugefile, _ := common.Mv(r, "hugefile", false)
  370. if hugefile == "true" && !fsh.RequireBuffer {
  371. //Huge file mode is not compatible with buffer typed FS
  372. isHugeFile = true
  373. }
  374. targetUploadLocation := filepath.Join(realUploadPath, filename)
  375. if !targetFs.FileExists(realUploadPath) {
  376. targetFs.MkdirAll(realUploadPath, 0755)
  377. }
  378. //Generate an UUID for this upload
  379. uploadUUID := uuid.NewV4().String()
  380. uploadFolder := filepath.Join(*tmp_directory, "uploads", uploadUUID)
  381. if isHugeFile {
  382. //Upload to the same directory as the target location. This option do not allow buffered fs
  383. uploadFolder = filepath.Join(realUploadPath, ".metadata/.upload", uploadUUID)
  384. targetFs.MkdirAll(uploadFolder, 0700)
  385. } else {
  386. //Buffer to local tmp folder
  387. os.MkdirAll(uploadFolder, 0700)
  388. }
  389. //Start websocket connection
  390. var upgrader = websocket.Upgrader{}
  391. upgrader.CheckOrigin = func(r *http.Request) bool { return true }
  392. c, err := upgrader.Upgrade(w, r, nil)
  393. if err != nil {
  394. log.Println("Failed to upgrade websocket connection: ", err.Error())
  395. w.WriteHeader(http.StatusInternalServerError)
  396. w.Write([]byte("500 WebSocket upgrade failed"))
  397. return
  398. }
  399. defer c.Close()
  400. //Handle WebSocket upload
  401. blockCounter := 0
  402. chunkName := []string{}
  403. lastChunkArrivalTime := time.Now().Unix()
  404. //Setup a timeout listener, check if connection still active every 1 minute
  405. ticker := time.NewTicker(60 * time.Second)
  406. done := make(chan bool)
  407. go func() {
  408. for {
  409. select {
  410. case <-done:
  411. return
  412. case <-ticker.C:
  413. if time.Now().Unix()-lastChunkArrivalTime > 300 {
  414. //Already 5 minutes without new data arraival. Stop connection
  415. log.Println("Upload WebSocket connection timeout. Disconnecting.")
  416. c.WriteControl(8, []byte{}, time.Now().Add(time.Second))
  417. time.Sleep(1 * time.Second)
  418. c.Close()
  419. return
  420. }
  421. }
  422. }
  423. }()
  424. totalFileSize := int64(0)
  425. for {
  426. mt, message, err := c.ReadMessage()
  427. if err != nil {
  428. //Connection closed by client. Clear the tmp folder and exit
  429. log.Println("Upload terminated by client. Cleaning tmp folder.")
  430. //Clear the tmp folder
  431. time.Sleep(1 * time.Second)
  432. os.RemoveAll(uploadFolder)
  433. return
  434. }
  435. //The mt should be 2 = binary for file upload and 1 for control syntax
  436. if mt == 1 {
  437. msg := strings.TrimSpace(string(message))
  438. if msg == "done" {
  439. //Start the merging process
  440. break
  441. } else {
  442. //Unknown operations
  443. }
  444. } else if mt == 2 {
  445. //File block. Save it to tmp folder
  446. chunkFilepath := filepath.Join(uploadFolder, "upld_"+strconv.Itoa(blockCounter))
  447. chunkName = append(chunkName, chunkFilepath)
  448. writeErr := os.WriteFile(chunkFilepath, message, 0700)
  449. if writeErr != nil {
  450. //Unable to write block. Is the tmp folder fulled?
  451. log.Println("[Upload] Upload chunk write failed: " + err.Error())
  452. c.WriteMessage(1, []byte(`{\"error\":\"Write file chunk to disk failed\"}`))
  453. //Close the connection
  454. c.WriteControl(8, []byte{}, time.Now().Add(time.Second))
  455. time.Sleep(1 * time.Second)
  456. c.Close()
  457. //Clear the tmp files
  458. os.RemoveAll(uploadFolder)
  459. return
  460. }
  461. //Update the last upload chunk time
  462. lastChunkArrivalTime = time.Now().Unix()
  463. //Check if the file size is too big
  464. totalFileSize += fs.GetFileSize(chunkFilepath)
  465. if totalFileSize > max_upload_size {
  466. //File too big
  467. c.WriteMessage(1, []byte(`{\"error\":\"File size too large\"}`))
  468. //Close the connection
  469. c.WriteControl(8, []byte{}, time.Now().Add(time.Second))
  470. time.Sleep(1 * time.Second)
  471. c.Close()
  472. //Clear the tmp files
  473. os.RemoveAll(uploadFolder)
  474. return
  475. } else if !userinfo.StorageQuota.HaveSpace(totalFileSize) {
  476. //Quota exceeded
  477. c.WriteMessage(1, []byte(`{\"error\":\"User Storage Quota Exceeded\"}`))
  478. //Close the connection
  479. c.WriteControl(8, []byte{}, time.Now().Add(time.Second))
  480. time.Sleep(1 * time.Second)
  481. c.Close()
  482. //Clear the tmp files
  483. os.RemoveAll(uploadFolder)
  484. }
  485. blockCounter++
  486. //Request client to send the next chunk
  487. c.WriteMessage(1, []byte("next"))
  488. }
  489. //log.Println("recv:", len(message), "type", mt)
  490. }
  491. //Try to decode the location if possible
  492. decodedUploadLocation, err := url.PathUnescape(targetUploadLocation)
  493. if err != nil {
  494. decodedUploadLocation = targetUploadLocation
  495. }
  496. //Do not allow % sign in filename. Replace all with underscore
  497. decodedUploadLocation = strings.ReplaceAll(decodedUploadLocation, "%", "_")
  498. //Merge the file. Merge file location must be on local machine
  499. mergeFileLocation := decodedUploadLocation
  500. if fsh.RequireBuffer && !isHugeFile {
  501. mergeFileLocation = getFsBufferFilepath(decodedUploadLocation, false)
  502. }
  503. out, err := os.OpenFile(mergeFileLocation, os.O_CREATE|os.O_WRONLY, 0755)
  504. if err != nil {
  505. log.Println("Failed to open file:", err)
  506. c.WriteMessage(1, []byte(`{\"error\":\"Failed to open destination file\"}`))
  507. c.WriteControl(8, []byte{}, time.Now().Add(time.Second))
  508. time.Sleep(1 * time.Second)
  509. c.Close()
  510. return
  511. }
  512. for _, filesrc := range chunkName {
  513. srcChunkReader, err := os.Open(filesrc)
  514. if err != nil {
  515. log.Println("Failed to open Source Chunk", filesrc, " with error ", err.Error())
  516. c.WriteMessage(1, []byte(`{\"error\":\"Failed to open Source Chunk\"}`))
  517. return
  518. }
  519. io.Copy(out, srcChunkReader)
  520. srcChunkReader.Close()
  521. //Delete file immediately to save space
  522. os.Remove(filesrc)
  523. }
  524. out.Close()
  525. if fsh.RequireBuffer && !isHugeFile {
  526. //This is buffer file. Upload to dest fsh
  527. f, err := os.Open(mergeFileLocation)
  528. if err != nil {
  529. log.Println("Failed to open buffered file at ", mergeFileLocation, " with error ", err.Error())
  530. c.WriteMessage(1, []byte(`{\"error\":\"Failed to open buffered object\"}`))
  531. f.Close()
  532. return
  533. }
  534. err = fsh.FileSystemAbstraction.WriteStream(decodedUploadLocation, f, 0775)
  535. if err != nil {
  536. log.Println("Failed to write to file system: ", fsh.UUID, " with error ", err.Error())
  537. c.WriteMessage(1, []byte(`{\"error\":\"Failed to upload to remote file system\"}`))
  538. f.Close()
  539. return
  540. }
  541. //Remove the buffered file
  542. f.Close()
  543. os.Remove(mergeFileLocation)
  544. }
  545. //Check if the size fit in user quota
  546. fi, err := targetFs.Stat(decodedUploadLocation)
  547. if err != nil {
  548. // Could not obtain stat, handle error
  549. log.Println("Failed to validate uploaded file: ", decodedUploadLocation, ". Error Message: ", err.Error())
  550. c.WriteMessage(1, []byte(`{\"error\":\"Failed to validate uploaded file\"}`))
  551. return
  552. }
  553. if !userinfo.StorageQuota.HaveSpace(fi.Size()) {
  554. c.WriteMessage(1, []byte(`{\"error\":\"User Storage Quota Exceeded\"}`))
  555. os.RemoveAll(decodedUploadLocation)
  556. return
  557. }
  558. //Log the upload filename
  559. log.Println(userinfo.Username + " uploaded a file: " + filepath.Base(decodedUploadLocation))
  560. //Set owner of the new uploaded file
  561. userinfo.SetOwnerOfFile(decodedUploadLocation)
  562. //Return complete signal
  563. c.WriteMessage(1, []byte("OK"))
  564. //Stop the timeout listner
  565. done <- true
  566. //Clear the tmp folder
  567. time.Sleep(300 * time.Millisecond)
  568. if isHugeFile {
  569. targetFs.RemoveAll(uploadFolder)
  570. } else {
  571. os.RemoveAll(uploadFolder)
  572. }
  573. //Close WebSocket connection after finished
  574. c.WriteControl(8, []byte{}, time.Now().Add(time.Second))
  575. time.Sleep(300 * time.Second)
  576. c.Close()
  577. }
  578. /*
  579. Handle FORM POST based upload
  580. This function is design for general SBCs or computers with more than 2GB of RAM
  581. (e.g. Raspberry Pi 4 / Linux Server)
  582. */
  583. func system_fs_handleUpload(w http.ResponseWriter, r *http.Request) {
  584. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  585. if err != nil {
  586. common.SendErrorResponse(w, "User not logged in")
  587. return
  588. }
  589. //Limit the max upload size to the user defined size
  590. if max_upload_size != 0 {
  591. r.Body = http.MaxBytesReader(w, r.Body, max_upload_size)
  592. }
  593. //Check if this is running under demo mode. If yes, reject upload
  594. if *demo_mode {
  595. common.SendErrorResponse(w, "You cannot upload in demo mode")
  596. return
  597. }
  598. err = r.ParseMultipartForm(int64(*upload_buf) << 20)
  599. if err != nil {
  600. //Filesize too big
  601. log.Println(err)
  602. common.SendErrorResponse(w, "File too large")
  603. return
  604. }
  605. file, handler, err := r.FormFile("file")
  606. if err != nil {
  607. log.Println("Error Retrieving File from upload by user: " + userinfo.Username)
  608. common.SendErrorResponse(w, "Unable to parse file from upload")
  609. return
  610. }
  611. //Get upload target directory
  612. uploadTarget, _ := common.Mv(r, "path", true)
  613. if uploadTarget == "" {
  614. common.SendErrorResponse(w, "Upload target cannot be empty.")
  615. return
  616. }
  617. fsh, subpath, err := GetFSHandlerSubpathFromVpath(uploadTarget)
  618. if err != nil {
  619. common.SendErrorResponse(w, "Invalid upload target")
  620. return
  621. }
  622. targetFs := fsh.FileSystemAbstraction
  623. //Translate the upload target directory
  624. realUploadPath, err := targetFs.VirtualPathToRealPath(subpath, userinfo.Username)
  625. if err != nil {
  626. common.SendErrorResponse(w, "Upload target is invalid or permission denied.")
  627. return
  628. }
  629. storeFilename := handler.Filename //Filename of the uploaded file
  630. //Get request time
  631. uploadStartTime := time.Now().UnixNano() / int64(time.Millisecond)
  632. //Update for Firefox 94.0.2 (x64) -> Now firefox put its relative path inside Content-Disposition -> filename
  633. //Skip this handler logic if Firefox version is in between 84.0.2 to 94.0.2
  634. bypassMetaCheck := compatibility.FirefoxBrowserVersionForBypassUploadMetaHeaderCheck(r.UserAgent())
  635. if !bypassMetaCheck && strings.Contains(handler.Header["Content-Disposition"][0], "filename=") && strings.Contains(handler.Header["Content-Disposition"][0], "/") {
  636. //This is a firefox MIME Header for file inside folder. Look for the actual filename
  637. headerFields := strings.Split(handler.Header["Content-Disposition"][0], "; ")
  638. possibleRelativePathname := ""
  639. for _, hf := range headerFields {
  640. if strings.Contains(hf, "filename=") && len(hf) > 11 {
  641. //Found. Overwrite original filename with the latest one
  642. possibleRelativePathname = hf[10 : len(hf)-1]
  643. storeFilename = possibleRelativePathname
  644. break
  645. }
  646. }
  647. }
  648. destFilepath := filepath.ToSlash(filepath.Clean(realUploadPath)) + "/" + storeFilename
  649. if !targetFs.FileExists(filepath.Dir(destFilepath)) {
  650. targetFs.MkdirAll(filepath.Dir(destFilepath), 0775)
  651. }
  652. //Check if the upload target is read only.
  653. accmode := userinfo.GetPathAccessPermission(uploadTarget)
  654. if accmode == "readonly" {
  655. common.SendErrorResponse(w, "The upload target is Read Only.")
  656. return
  657. } else if accmode == "denied" {
  658. common.SendErrorResponse(w, "Access Denied")
  659. return
  660. }
  661. //Check for storage quota
  662. uploadFileSize := handler.Size
  663. if !userinfo.StorageQuota.HaveSpace(uploadFileSize) {
  664. common.SendErrorResponse(w, "User Storage Quota Exceeded")
  665. return
  666. }
  667. //Do not allow % sign in filename. Replace all with underscore
  668. destFilepath = strings.ReplaceAll(destFilepath, "%", "_")
  669. //Move the file to destination file location
  670. if *enable_asyncFileUpload {
  671. //Use Async upload method
  672. log.Println("[File System] AsyncFileUpload flag has been deprecated. Falling back to blocking upload.")
  673. }
  674. err = targetFs.WriteStream(destFilepath, file, 0775)
  675. if err != nil {
  676. log.Println(err.Error())
  677. }
  678. file.Close()
  679. //Clear up buffered files
  680. r.MultipartForm.RemoveAll()
  681. //Set the ownership of file
  682. userinfo.SetOwnerOfFile(destFilepath)
  683. //Finish up the upload
  684. /*
  685. fmt.Printf("Uploaded File: %+v\n", handler.Filename)
  686. fmt.Printf("File Size: %+v\n", handler.Size)
  687. fmt.Printf("MIME Header: %+v\n", handler.Header)
  688. fmt.Println("Upload target: " + realUploadPath)
  689. */
  690. //Fnish upload. Fix the tmp filename
  691. log.Println(userinfo.Username + " uploaded a file: " + handler.Filename)
  692. //Do upload finishing stuff
  693. //Add a delay to the complete message to make sure browser catch the return value
  694. currentTimeMilli := time.Now().UnixNano() / int64(time.Millisecond)
  695. if currentTimeMilli-uploadStartTime < 100 {
  696. //Sleep until at least 300 ms
  697. time.Sleep(time.Duration(100 - (currentTimeMilli - uploadStartTime)))
  698. }
  699. //Completed
  700. common.SendOK(w)
  701. }
  702. //Validate if the copy and target process will involve file overwriting problem.
  703. func system_fs_validateFileOpr(w http.ResponseWriter, r *http.Request) {
  704. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  705. if err != nil {
  706. common.SendErrorResponse(w, err.Error())
  707. return
  708. }
  709. vsrcFiles, _ := common.Mv(r, "src", true)
  710. vdestFile, _ := common.Mv(r, "dest", true)
  711. var duplicateFiles []string = []string{}
  712. //Loop through all files are see if there are duplication during copy and paste
  713. sourceFiles := []string{}
  714. decodedSourceFiles, _ := url.QueryUnescape(vsrcFiles)
  715. err = json.Unmarshal([]byte(decodedSourceFiles), &sourceFiles)
  716. if err != nil {
  717. common.SendErrorResponse(w, "Source file JSON parse error.")
  718. return
  719. }
  720. destFsh, destSubpath, _ := GetFSHandlerSubpathFromVpath(vdestFile)
  721. rdestFile, _ := destFsh.FileSystemAbstraction.VirtualPathToRealPath(destSubpath, userinfo.Username)
  722. for _, file := range sourceFiles {
  723. srcFsh, srcSubpath, _ := GetFSHandlerSubpathFromVpath(string(file))
  724. rsrcFile, _ := srcFsh.FileSystemAbstraction.VirtualPathToRealPath(srcSubpath, userinfo.Username)
  725. if srcFsh.FileSystemAbstraction.FileExists(filepath.Join(rdestFile, filepath.Base(rsrcFile))) {
  726. //File exists already.
  727. vpath, _ := srcFsh.FileSystemAbstraction.RealPathToVirtualPath(rsrcFile, userinfo.Username)
  728. duplicateFiles = append(duplicateFiles, vpath)
  729. }
  730. }
  731. jsonString, _ := json.Marshal(duplicateFiles)
  732. common.SendJSONResponse(w, string(jsonString))
  733. }
  734. //Scan all directory and get trash file and send back results with WebSocket
  735. func system_fs_WebSocketScanTrashBin(w http.ResponseWriter, r *http.Request) {
  736. //Get and check user permission
  737. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  738. if err != nil {
  739. common.SendErrorResponse(w, "User not logged in")
  740. return
  741. }
  742. //Upgrade to websocket
  743. var upgrader = websocket.Upgrader{}
  744. upgrader.CheckOrigin = func(r *http.Request) bool { return true }
  745. c, err := upgrader.Upgrade(w, r, nil)
  746. if err != nil {
  747. w.WriteHeader(http.StatusInternalServerError)
  748. w.Write([]byte("500 - " + err.Error()))
  749. log.Print("Websocket Upgrade Error:", err.Error())
  750. return
  751. }
  752. //Start Scanning
  753. scanningRoots := []*filesystem.FileSystemHandler{}
  754. //Get all roots to scan
  755. for _, storage := range userinfo.GetAllFileSystemHandler() {
  756. if storage.Hierarchy == "backup" {
  757. //Skip this fsh
  758. continue
  759. }
  760. if !storage.IsVirtual() {
  761. scanningRoots = append(scanningRoots, storage)
  762. }
  763. }
  764. for _, fsh := range scanningRoots {
  765. thisFshAbs := fsh.FileSystemAbstraction
  766. rootPath, err := thisFshAbs.VirtualPathToRealPath("", userinfo.Username)
  767. if err != nil {
  768. continue
  769. }
  770. err = thisFshAbs.Walk(rootPath, func(path string, info os.FileInfo, err error) error {
  771. oneLevelUpper := filepath.Base(filepath.Dir(path))
  772. if oneLevelUpper == ".trash" {
  773. //This is a trashbin dir.
  774. file := path
  775. //Parse the trashFile struct
  776. timestamp := filepath.Ext(file)[1:]
  777. originalName := strings.TrimSuffix(filepath.Base(file), filepath.Ext(filepath.Base(file)))
  778. originalExt := filepath.Ext(filepath.Base(originalName))
  779. virtualFilepath, _ := thisFshAbs.RealPathToVirtualPath(file, userinfo.Username)
  780. virtualOrgPath, _ := thisFshAbs.RealPathToVirtualPath(filepath.Dir(filepath.Dir(filepath.Dir(file))), userinfo.Username)
  781. rawsize := thisFshAbs.GetFileSize(file)
  782. timestampInt64, _ := common.StringToInt64(timestamp)
  783. removeTimeDate := time.Unix(timestampInt64, 0)
  784. if thisFshAbs.IsDir(file) {
  785. originalExt = ""
  786. }
  787. thisTrashFileObject := trashedFile{
  788. Filename: filepath.Base(file),
  789. Filepath: virtualFilepath,
  790. FileExt: originalExt,
  791. IsDir: thisFshAbs.IsDir(file),
  792. Filesize: int64(rawsize),
  793. RemoveTimestamp: timestampInt64,
  794. RemoveDate: removeTimeDate.Format("2006-01-02 15:04:05"),
  795. OriginalPath: virtualOrgPath,
  796. OriginalFilename: originalName,
  797. }
  798. //Send out the result as JSON string
  799. js, _ := json.Marshal(thisTrashFileObject)
  800. err := c.WriteMessage(1, js)
  801. if err != nil {
  802. //Connection already closed
  803. return err
  804. }
  805. }
  806. return nil
  807. })
  808. if err != nil {
  809. //Scan or client connection error (Connection closed?)
  810. return
  811. }
  812. }
  813. //Close connection after finished
  814. c.Close()
  815. }
  816. //Scan all the directory and get trash files within the system
  817. func system_fs_scanTrashBin(w http.ResponseWriter, r *http.Request) {
  818. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  819. if err != nil {
  820. common.SendErrorResponse(w, err.Error())
  821. return
  822. }
  823. username := userinfo.Username
  824. results := []trashedFile{}
  825. files, fshs, err := system_fs_listTrash(username)
  826. if err != nil {
  827. common.SendErrorResponse(w, err.Error())
  828. return
  829. }
  830. //Get information of each files and process it into results
  831. for c, file := range files {
  832. fsAbs := fshs[c].FileSystemAbstraction
  833. timestamp := filepath.Ext(file)[1:]
  834. originalName := strings.TrimSuffix(filepath.Base(file), filepath.Ext(filepath.Base(file)))
  835. originalExt := filepath.Ext(filepath.Base(originalName))
  836. virtualFilepath, _ := fsAbs.RealPathToVirtualPath(file, userinfo.Username)
  837. virtualOrgPath, _ := fsAbs.RealPathToVirtualPath(filepath.Dir(filepath.Dir(filepath.Dir(file))), userinfo.Username)
  838. rawsize := fsAbs.GetFileSize(file)
  839. timestampInt64, _ := common.StringToInt64(timestamp)
  840. removeTimeDate := time.Unix(timestampInt64, 0)
  841. if fsAbs.IsDir(file) {
  842. originalExt = ""
  843. }
  844. results = append(results, trashedFile{
  845. Filename: filepath.Base(file),
  846. Filepath: virtualFilepath,
  847. FileExt: originalExt,
  848. IsDir: fsAbs.IsDir(file),
  849. Filesize: int64(rawsize),
  850. RemoveTimestamp: timestampInt64,
  851. RemoveDate: removeTimeDate.Format("2006-01-02 15:04:05"),
  852. OriginalPath: virtualOrgPath,
  853. OriginalFilename: originalName,
  854. })
  855. }
  856. //Sort the results by date, latest on top
  857. sort.Slice(results[:], func(i, j int) bool {
  858. return results[i].RemoveTimestamp > results[j].RemoveTimestamp
  859. })
  860. //Format and return the json results
  861. jsonString, _ := json.Marshal(results)
  862. common.SendJSONResponse(w, string(jsonString))
  863. }
  864. //Restore a trashed file to its parent dir
  865. func system_fs_restoreFile(w http.ResponseWriter, r *http.Request) {
  866. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  867. if err != nil {
  868. common.SendErrorResponse(w, err.Error())
  869. return
  870. }
  871. targetTrashedFile, err := common.Mv(r, "src", true)
  872. if err != nil {
  873. common.SendErrorResponse(w, "Invalid src given")
  874. return
  875. }
  876. fsh, subpath, err := GetFSHandlerSubpathFromVpath(targetTrashedFile)
  877. if err != nil {
  878. common.SendErrorResponse(w, err.Error())
  879. return
  880. }
  881. fshAbs := fsh.FileSystemAbstraction
  882. //Translate it to realpath
  883. realpath, _ := fshAbs.VirtualPathToRealPath(subpath, userinfo.Username)
  884. if !fshAbs.FileExists(realpath) {
  885. common.SendErrorResponse(w, "File not exists")
  886. return
  887. }
  888. //Check if this is really a trashed file
  889. if filepath.Base(filepath.Dir(realpath)) != ".trash" {
  890. common.SendErrorResponse(w, "File not in trashbin")
  891. return
  892. }
  893. //OK to proceed.
  894. originalFilename := strings.TrimSuffix(filepath.Base(realpath), filepath.Ext(filepath.Base(realpath)))
  895. restoreFolderRoot := filepath.Dir(filepath.Dir(filepath.Dir(realpath)))
  896. targetPath := filepath.ToSlash(filepath.Join(restoreFolderRoot, originalFilename))
  897. //log.Println(targetPath)
  898. fshAbs.Rename(realpath, targetPath)
  899. //Check if the parent dir has no more fileds. If yes, remove it
  900. filescounter, _ := fshAbs.Glob(filepath.Dir(realpath) + "/*")
  901. if len(filescounter) == 0 {
  902. fshAbs.Remove(filepath.Dir(realpath))
  903. }
  904. common.SendOK(w)
  905. }
  906. //Clear all trashed file in the system
  907. func system_fs_clearTrashBin(w http.ResponseWriter, r *http.Request) {
  908. u, err := userHandler.GetUserInfoFromRequest(w, r)
  909. if err != nil {
  910. common.SendErrorResponse(w, "User not logged in")
  911. return
  912. }
  913. fileList, fshs, err := system_fs_listTrash(u.Username)
  914. if err != nil {
  915. common.SendErrorResponse(w, "Unable to clear trash: "+err.Error())
  916. return
  917. }
  918. //Get list success. Remove each of them.
  919. for c, file := range fileList {
  920. isOwner := u.IsOwnerOfFile(file)
  921. if isOwner {
  922. //This user own this system. Remove this file from his quota
  923. u.RemoveOwnershipFromFile(file)
  924. }
  925. fshAbs := fshs[c].FileSystemAbstraction
  926. fshAbs.RemoveAll(file)
  927. //Check if its parent directory have no files. If yes, remove the dir itself as well.
  928. filesInThisTrashBin, _ := fshAbs.Glob(filepath.Dir(file) + "/*")
  929. if len(filesInThisTrashBin) == 0 {
  930. fshAbs.Remove(filepath.Dir(file))
  931. }
  932. }
  933. common.SendOK(w)
  934. }
  935. //Get all trash in a string list
  936. func system_fs_listTrash(username string) ([]string, []*filesystem.FileSystemHandler, error) {
  937. userinfo, _ := userHandler.GetUserInfoFromUsername(username)
  938. scanningRoots := []*filesystem.FileSystemHandler{}
  939. //Get all roots to scan
  940. for _, storage := range userinfo.GetAllFileSystemHandler() {
  941. if storage.Hierarchy == "backup" {
  942. //Skip this fsh
  943. continue
  944. }
  945. scanningRoots = append(scanningRoots, storage)
  946. }
  947. files := []string{}
  948. fshs := []*filesystem.FileSystemHandler{}
  949. for _, thisFsh := range scanningRoots {
  950. thisFshAbs := thisFsh.FileSystemAbstraction
  951. rootPath, _ := thisFshAbs.VirtualPathToRealPath("", userinfo.Username)
  952. err := thisFshAbs.Walk(rootPath, func(path string, info os.FileInfo, err error) error {
  953. oneLevelUpper := filepath.Base(filepath.Dir(path))
  954. if oneLevelUpper == ".trash" {
  955. //This is a trashbin dir.
  956. files = append(files, path)
  957. fshs = append(fshs, thisFsh)
  958. }
  959. return nil
  960. })
  961. if err != nil {
  962. continue
  963. }
  964. }
  965. return files, fshs, nil
  966. }
  967. /*
  968. Handle new file or folder functions
  969. Required information
  970. @type {folder / file}
  971. @ext {any that is listed in the template folder}
  972. if no paramter is passed in, default listing all the supported template file
  973. */
  974. func system_fs_handleNewObjects(w http.ResponseWriter, r *http.Request) {
  975. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  976. if err != nil {
  977. common.SendErrorResponse(w, "User not logged in")
  978. return
  979. }
  980. //Validate the token
  981. tokenValid := CSRFTokenManager.HandleTokenValidation(w, r)
  982. if !tokenValid {
  983. http.Error(w, "Invalid CSRF token", http.StatusUnauthorized)
  984. return
  985. }
  986. fileType, _ := common.Mv(r, "type", true) //File creation type, {file, folder}
  987. vsrc, _ := common.Mv(r, "src", true) //Virtual file source folder, do not include filename
  988. filename, _ := common.Mv(r, "filename", true) //Filename for the new file
  989. if fileType == "" && filename == "" {
  990. //List all the supported new filetype
  991. if !fs.FileExists("system/newitem/") {
  992. os.MkdirAll("system/newitem/", 0755)
  993. }
  994. type newItemObject struct {
  995. Desc string
  996. Ext string
  997. }
  998. var newItemList []newItemObject
  999. newItemTemplate, _ := filepath.Glob("system/newitem/*")
  1000. for _, file := range newItemTemplate {
  1001. thisItem := new(newItemObject)
  1002. thisItem.Desc = strings.TrimSuffix(filepath.Base(file), filepath.Ext(file))
  1003. thisItem.Ext = filepath.Ext(file)[1:]
  1004. newItemList = append(newItemList, *thisItem)
  1005. }
  1006. jsonString, err := json.Marshal(newItemList)
  1007. if err != nil {
  1008. log.Println("*File System* Unable to parse JSON string for new item list!")
  1009. common.SendErrorResponse(w, "Unable to parse new item list. See server log for more information.")
  1010. return
  1011. }
  1012. common.SendJSONResponse(w, string(jsonString))
  1013. return
  1014. } else if fileType != "" && filename != "" {
  1015. if vsrc == "" {
  1016. common.SendErrorResponse(w, "Missing paramter: 'src'")
  1017. return
  1018. }
  1019. fsh, subpath, err := GetFSHandlerSubpathFromVpath(vsrc)
  1020. if err != nil {
  1021. common.SendErrorResponse(w, err.Error())
  1022. return
  1023. }
  1024. fshAbs := fsh.FileSystemAbstraction
  1025. //Translate the path to realpath
  1026. rpath, err := fshAbs.VirtualPathToRealPath(subpath, userinfo.Username)
  1027. if err != nil {
  1028. common.SendErrorResponse(w, "Invalid path given")
  1029. return
  1030. }
  1031. //Check if directory is readonly
  1032. accmode := userinfo.GetPathAccessPermission(vsrc)
  1033. if accmode == "readonly" {
  1034. common.SendErrorResponse(w, "This directory is Read Only")
  1035. return
  1036. } else if accmode == "denied" {
  1037. common.SendErrorResponse(w, "Access Denied")
  1038. return
  1039. }
  1040. //Check if the file already exists. If yes, fix its filename.
  1041. newfilePath := filepath.ToSlash(filepath.Join(rpath, filename))
  1042. if fileType == "file" {
  1043. for fshAbs.FileExists(newfilePath) {
  1044. common.SendErrorResponse(w, "Given filename already exists")
  1045. return
  1046. }
  1047. ext := filepath.Ext(filename)
  1048. defaultFileCotent := []byte("")
  1049. if ext != "" {
  1050. templateFile, _ := fshAbs.Glob("system/newitem/*" + ext)
  1051. if len(templateFile) > 0 {
  1052. //Copy file from templateFile[0] to current dir with the given name
  1053. input, _ := os.ReadFile(templateFile[0])
  1054. defaultFileCotent = input
  1055. }
  1056. }
  1057. err = fshAbs.WriteFile(newfilePath, defaultFileCotent, 0775)
  1058. if err != nil {
  1059. log.Println("[File System] Unable to create new file: " + err.Error())
  1060. common.SendErrorResponse(w, err.Error())
  1061. return
  1062. }
  1063. } else if fileType == "folder" {
  1064. if fshAbs.FileExists(newfilePath) {
  1065. common.SendErrorResponse(w, "Given folder already exists")
  1066. return
  1067. }
  1068. //Create the folder at target location
  1069. err := fshAbs.Mkdir(newfilePath, 0755)
  1070. if err != nil {
  1071. common.SendErrorResponse(w, err.Error())
  1072. return
  1073. }
  1074. }
  1075. common.SendOK(w)
  1076. } else {
  1077. common.SendErrorResponse(w, "Missing paramter(s).")
  1078. return
  1079. }
  1080. }
  1081. /*
  1082. Handle file operations via WebSocket
  1083. This handler only handle zip, unzip, copy and move. Not other operations.
  1084. For other operations, please use the legacy handleOpr endpoint
  1085. */
  1086. func system_fs_handleWebSocketOpr(w http.ResponseWriter, r *http.Request) {
  1087. //Get and check user permission
  1088. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  1089. if err != nil {
  1090. common.SendErrorResponse(w, "User not logged in")
  1091. return
  1092. }
  1093. operation, _ := common.Mv(r, "opr", false) //Accept copy and move
  1094. vsrcFiles, _ := common.Mv(r, "src", false)
  1095. vdestFile, _ := common.Mv(r, "dest", false)
  1096. existsOpr, _ := common.Mv(r, "existsresp", false)
  1097. if existsOpr == "" {
  1098. existsOpr = "keep"
  1099. }
  1100. //Decode the source file list
  1101. var sourceFiles []string
  1102. tmp := []string{}
  1103. decodedSourceFiles, _ := url.QueryUnescape(vsrcFiles)
  1104. err = json.Unmarshal([]byte(decodedSourceFiles), &sourceFiles)
  1105. if err != nil {
  1106. log.Println("Source file JSON parse error.", err.Error())
  1107. common.SendErrorResponse(w, "Source file JSON parse error.")
  1108. return
  1109. }
  1110. //Bugged char filtering
  1111. for _, src := range sourceFiles {
  1112. tmp = append(tmp, strings.ReplaceAll(src, "{{plug_sign}}", "+"))
  1113. }
  1114. sourceFiles = tmp
  1115. vdestFile = strings.ReplaceAll(vdestFile, "{{plug_sign}}", "+")
  1116. //Decode the target position
  1117. escapedVdest, _ := url.QueryUnescape(vdestFile)
  1118. vdestFile = escapedVdest
  1119. destFsh, subpath, err := GetFSHandlerSubpathFromVpath(vdestFile)
  1120. if err != nil {
  1121. common.SendErrorResponse(w, err.Error())
  1122. return
  1123. }
  1124. destFshAbs := destFsh.FileSystemAbstraction
  1125. rdestFile, _ := destFshAbs.VirtualPathToRealPath(subpath, userinfo.Username)
  1126. //Permission checking
  1127. if !userinfo.CanWrite(vdestFile) {
  1128. log.Println("Access denied for " + userinfo.Username + " try to access " + vdestFile)
  1129. w.WriteHeader(http.StatusForbidden)
  1130. w.Write([]byte("403 - Access Denied"))
  1131. return
  1132. }
  1133. //Check if opr is suported
  1134. if operation == "move" || operation == "copy" || operation == "zip" || operation == "unzip" {
  1135. } else {
  1136. log.Println("This file operation is not supported on WebSocket file operations endpoint. Please use the legacy endpoint instead. Received: ", operation)
  1137. w.WriteHeader(http.StatusInternalServerError)
  1138. w.Write([]byte("500 - Not supported operation"))
  1139. return
  1140. }
  1141. //Upgrade to websocket
  1142. var upgrader = websocket.Upgrader{}
  1143. upgrader.CheckOrigin = func(r *http.Request) bool { return true }
  1144. c, err := upgrader.Upgrade(w, r, nil)
  1145. if err != nil {
  1146. w.WriteHeader(http.StatusInternalServerError)
  1147. w.Write([]byte("500 - " + err.Error()))
  1148. log.Print("Websocket Upgrade Error:", err.Error())
  1149. return
  1150. }
  1151. type ProgressUpdate struct {
  1152. LatestFile string
  1153. Progress int
  1154. Error string
  1155. }
  1156. if operation == "zip" {
  1157. //Zip files
  1158. outputFilename := filepath.Join(rdestFile, filepath.Base(rdestFile)) + ".zip"
  1159. if len(sourceFiles) == 1 {
  1160. //Use the basename of the source file as zip file name
  1161. outputFilename = filepath.Join(rdestFile, filepath.Base(sourceFiles[0])) + ".zip"
  1162. }
  1163. //Translate source Files into real paths
  1164. realSourceFiles := []string{}
  1165. for _, vsrcs := range sourceFiles {
  1166. thisSrcFsh, subpath, _ := GetFSHandlerSubpathFromVpath(vsrcs)
  1167. rsrc, err := thisSrcFsh.FileSystemAbstraction.VirtualPathToRealPath(subpath, userinfo.Username)
  1168. if err != nil {
  1169. stopStatus := ProgressUpdate{
  1170. LatestFile: filepath.Base(rsrc),
  1171. Progress: -1,
  1172. Error: "File not exists",
  1173. }
  1174. js, _ := json.Marshal(stopStatus)
  1175. c.WriteMessage(1, js)
  1176. c.Close()
  1177. return
  1178. }
  1179. if thisSrcFsh.RequireBuffer {
  1180. localBufferFilepath, err := bufferRemoteFileToLocal(thisSrcFsh, rsrc, true)
  1181. if err != nil {
  1182. stopStatus := ProgressUpdate{
  1183. LatestFile: filepath.Base(rsrc),
  1184. Progress: -1,
  1185. Error: "Failed to buffer file to local disk",
  1186. }
  1187. js, _ := json.Marshal(stopStatus)
  1188. c.WriteMessage(1, js)
  1189. c.Close()
  1190. return
  1191. }
  1192. realSourceFiles = append(realSourceFiles, localBufferFilepath)
  1193. } else {
  1194. realSourceFiles = append(realSourceFiles, rsrc)
  1195. }
  1196. }
  1197. zipDestPath := outputFilename
  1198. if destFsh.RequireBuffer {
  1199. zipDestPath = getFsBufferFilepath(outputFilename, false)
  1200. }
  1201. //Create the zip file
  1202. fs.ArozZipFileWithProgress(realSourceFiles, zipDestPath, false, func(currentFilename string, _ int, _ int, progress float64) {
  1203. currentStatus := ProgressUpdate{
  1204. LatestFile: currentFilename,
  1205. Progress: int(math.Ceil(progress)),
  1206. Error: "",
  1207. }
  1208. js, _ := json.Marshal(currentStatus)
  1209. c.WriteMessage(1, js)
  1210. })
  1211. if destFsh.RequireBuffer {
  1212. //Move the buffer result to remote
  1213. f, _ := os.Open(zipDestPath)
  1214. destFshAbs.WriteStream(outputFilename, f, 0775)
  1215. f.Close()
  1216. //Clear local buffers
  1217. os.Remove(zipDestPath)
  1218. cleanFsBufferFileFromList(realSourceFiles)
  1219. }
  1220. } else if operation == "unzip" {
  1221. //Check if the target destination exists and writable
  1222. if !userinfo.CanWrite(vdestFile) {
  1223. stopStatus := ProgressUpdate{
  1224. LatestFile: filepath.Base(vdestFile),
  1225. Progress: -1,
  1226. Error: "Access Denied: No Write Permission",
  1227. }
  1228. js, _ := json.Marshal(stopStatus)
  1229. c.WriteMessage(1, js)
  1230. c.Close()
  1231. }
  1232. //Create the destination folder
  1233. destFshAbs.MkdirAll(rdestFile, 0755)
  1234. //Convert the src files into realpaths
  1235. realSourceFiles := []string{}
  1236. for _, vsrcs := range sourceFiles {
  1237. thisSrcFsh, subpath, _ := GetFSHandlerSubpathFromVpath(vsrcs)
  1238. thisSrcFshAbs := thisSrcFsh.FileSystemAbstraction
  1239. rsrc, err := thisSrcFshAbs.VirtualPathToRealPath(subpath, userinfo.Username)
  1240. if err != nil {
  1241. stopStatus := ProgressUpdate{
  1242. LatestFile: filepath.Base(rsrc),
  1243. Progress: -1,
  1244. Error: "File not exists",
  1245. }
  1246. js, _ := json.Marshal(stopStatus)
  1247. c.WriteMessage(1, js)
  1248. c.Close()
  1249. }
  1250. if thisSrcFsh.RequireBuffer {
  1251. localBufferFilepath, err := bufferRemoteFileToLocal(thisSrcFsh, rsrc, false)
  1252. if err != nil {
  1253. stopStatus := ProgressUpdate{
  1254. LatestFile: filepath.Base(rsrc),
  1255. Progress: -1,
  1256. Error: "Failed to buffer file to local disk",
  1257. }
  1258. js, _ := json.Marshal(stopStatus)
  1259. c.WriteMessage(1, js)
  1260. c.Close()
  1261. }
  1262. realSourceFiles = append(realSourceFiles, localBufferFilepath)
  1263. } else {
  1264. realSourceFiles = append(realSourceFiles, rsrc)
  1265. }
  1266. }
  1267. unzipDest := rdestFile
  1268. if destFsh.RequireBuffer {
  1269. unzipDest = getFsBufferFilepath(rdestFile, true)
  1270. }
  1271. //Unzip the files
  1272. fs.ArozUnzipFileWithProgress(realSourceFiles, unzipDest, func(currentFile string, filecount int, totalfile int, progress float64) {
  1273. //Generate the status update struct
  1274. currentStatus := ProgressUpdate{
  1275. LatestFile: filepath.Base(currentFile),
  1276. Progress: int(math.Ceil(progress)),
  1277. Error: "",
  1278. }
  1279. js, _ := json.Marshal(currentStatus)
  1280. c.WriteMessage(1, js)
  1281. })
  1282. if destFsh.RequireBuffer {
  1283. //Push the unzip results back to remote fs
  1284. fmt.Println("Unzip Remote Target:", rdestFile)
  1285. filepath.Walk(unzipDest, func(path string, info os.FileInfo, err error) error {
  1286. path = filepath.ToSlash(path)
  1287. relpath := strings.TrimPrefix(path, filepath.ToSlash(unzipDest))
  1288. if info.IsDir() {
  1289. destFshAbs.MkdirAll(filepath.Join(rdestFile, relpath), 0775)
  1290. } else {
  1291. f, _ := os.Open(path)
  1292. destFshAbs.WriteStream(filepath.Join(rdestFile, relpath), f, 0775)
  1293. f.Close()
  1294. }
  1295. return nil
  1296. })
  1297. cleanFsBufferFileFromList([]string{unzipDest})
  1298. }
  1299. cleanFsBufferFileFromList(realSourceFiles)
  1300. } else {
  1301. //Other operations that allow multiple source files to handle one by one
  1302. for i := 0; i < len(sourceFiles); i++ {
  1303. vsrcFile := sourceFiles[i]
  1304. rsrcFile, _ := userinfo.VirtualPathToRealPath(vsrcFile)
  1305. //c.WriteMessage(1, message)
  1306. if !fs.FileExists(rsrcFile) {
  1307. //This source file not exists. Report Error and Stop
  1308. stopStatus := ProgressUpdate{
  1309. LatestFile: filepath.Base(rsrcFile),
  1310. Progress: -1,
  1311. Error: "File not exists",
  1312. }
  1313. js, _ := json.Marshal(stopStatus)
  1314. c.WriteMessage(1, js)
  1315. c.Close()
  1316. return
  1317. }
  1318. if operation == "move" {
  1319. underSameRoot, _ := fs.UnderTheSameRoot(rsrcFile, rdestFile)
  1320. err := fs.FileMove(rsrcFile, rdestFile, existsOpr, underSameRoot, func(progress int, currentFile string) {
  1321. //Multply child progress to parent progress
  1322. blockRatio := float64(100) / float64(len(sourceFiles))
  1323. overallRatio := blockRatio*float64(i) + blockRatio*(float64(progress)/float64(100))
  1324. //Construct return struct
  1325. currentStatus := ProgressUpdate{
  1326. LatestFile: filepath.Base(currentFile),
  1327. Progress: int(overallRatio),
  1328. Error: "",
  1329. }
  1330. js, _ := json.Marshal(currentStatus)
  1331. c.WriteMessage(1, js)
  1332. })
  1333. //Handle move starting error
  1334. if err != nil {
  1335. stopStatus := ProgressUpdate{
  1336. LatestFile: filepath.Base(rsrcFile),
  1337. Progress: -1,
  1338. Error: err.Error(),
  1339. }
  1340. js, _ := json.Marshal(stopStatus)
  1341. c.WriteMessage(1, js)
  1342. c.Close()
  1343. return
  1344. }
  1345. //Remove the cache for the original file
  1346. metadata.RemoveCache(rsrcFile)
  1347. } else if operation == "copy" {
  1348. err := fs.FileCopy(rsrcFile, rdestFile, existsOpr, func(progress int, currentFile string) {
  1349. //Multply child progress to parent progress
  1350. blockRatio := float64(100) / float64(len(sourceFiles))
  1351. overallRatio := blockRatio*float64(i) + blockRatio*(float64(progress)/float64(100))
  1352. //Construct return struct
  1353. currentStatus := ProgressUpdate{
  1354. LatestFile: filepath.Base(currentFile),
  1355. Progress: int(overallRatio),
  1356. Error: "",
  1357. }
  1358. js, _ := json.Marshal(currentStatus)
  1359. c.WriteMessage(1, js)
  1360. })
  1361. //Handle Copy starting error
  1362. if err != nil {
  1363. stopStatus := ProgressUpdate{
  1364. LatestFile: filepath.Base(rsrcFile),
  1365. Progress: -1,
  1366. Error: err.Error(),
  1367. }
  1368. js, _ := json.Marshal(stopStatus)
  1369. c.WriteMessage(1, js)
  1370. c.Close()
  1371. return
  1372. }
  1373. }
  1374. }
  1375. }
  1376. //Close WebSocket connection after finished
  1377. time.Sleep(1 * time.Second)
  1378. c.WriteControl(8, []byte{}, time.Now().Add(time.Second))
  1379. c.Close()
  1380. }
  1381. /*
  1382. Handle file operations
  1383. Support {move, copy, delete, recycle, rename}
  1384. */
  1385. //Handle file operations.
  1386. func system_fs_handleOpr(w http.ResponseWriter, r *http.Request) {
  1387. //Check if user logged in
  1388. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  1389. if err != nil {
  1390. common.SendErrorResponse(w, "User not logged in")
  1391. return
  1392. }
  1393. //Validate the token
  1394. tokenValid := CSRFTokenManager.HandleTokenValidation(w, r)
  1395. if !tokenValid {
  1396. http.Error(w, "Invalid CSRF token", http.StatusUnauthorized)
  1397. return
  1398. }
  1399. operation, _ := common.Mv(r, "opr", true)
  1400. vsrcFiles, _ := common.Mv(r, "src", true)
  1401. vdestFile, _ := common.Mv(r, "dest", true)
  1402. vnfilenames, _ := common.Mv(r, "new", true) //Only use when rename or create new file / folder
  1403. //Check if operation valid.
  1404. if operation == "" {
  1405. //Undefined operations.
  1406. common.SendErrorResponse(w, "Undefined operations paramter: Missing 'opr' in request header.")
  1407. return
  1408. }
  1409. //As the user can pass in multiple source files at the same time, parse sourceFiles from json string
  1410. var sourceFiles []string
  1411. //This line is required in order to allow passing of special charaters
  1412. decodedSourceFiles := system_fs_specialURIDecode(vsrcFiles)
  1413. err = json.Unmarshal([]byte(decodedSourceFiles), &sourceFiles)
  1414. if err != nil {
  1415. common.SendErrorResponse(w, "Source file JSON parse error.")
  1416. return
  1417. }
  1418. //Check if new filenames are also valid. If yes, translate it into string array
  1419. var newFilenames []string
  1420. if vnfilenames != "" {
  1421. vnfilenames, _ := url.QueryUnescape(vnfilenames)
  1422. err = json.Unmarshal([]byte(vnfilenames), &newFilenames)
  1423. if err != nil {
  1424. common.SendErrorResponse(w, "Unable to parse JSON for new filenames")
  1425. return
  1426. }
  1427. }
  1428. if operation == "zip" {
  1429. //Zip operation. Parse the real filepath list
  1430. rsrcFiles := []string{}
  1431. destFsh, subpath, err := GetFSHandlerSubpathFromVpath(vdestFile)
  1432. if err != nil {
  1433. common.SendErrorResponse(w, "Unable to resolve zip destination path")
  1434. return
  1435. }
  1436. destFshAbs := destFsh.FileSystemAbstraction
  1437. rdestFile, _ := destFshAbs.VirtualPathToRealPath(subpath, userinfo.Username)
  1438. for _, vsrcFile := range sourceFiles {
  1439. vsrcFsh, vsrcSubpath, _ := GetFSHandlerSubpathFromVpath(vsrcFile)
  1440. rsrcFile, _ := vsrcFsh.FileSystemAbstraction.VirtualPathToRealPath(vsrcSubpath, userinfo.Username)
  1441. if vsrcFsh.FileSystemAbstraction.FileExists(rsrcFile) {
  1442. if vsrcFsh.RequireBuffer {
  1443. localBuffFile := getFsBufferFilepath(rsrcFile, true)
  1444. srcFileContent, err := vsrcFsh.FileSystemAbstraction.ReadFile(rsrcFile)
  1445. if err != nil {
  1446. continue
  1447. }
  1448. err = os.WriteFile(localBuffFile, srcFileContent, 0775)
  1449. if err != nil {
  1450. continue
  1451. }
  1452. rsrcFiles = append(rsrcFiles, localBuffFile)
  1453. } else {
  1454. //Push directly its local path to list
  1455. rsrcFiles = append(rsrcFiles, rsrcFile)
  1456. }
  1457. }
  1458. }
  1459. zipFilename := rdestFile
  1460. if destFshAbs.IsDir(rdestFile) {
  1461. //Append the filename to it
  1462. if len(rsrcFiles) == 1 {
  1463. zipFilename = filepath.Join(rdestFile, strings.TrimSuffix(filepath.Base(rsrcFiles[0]), filepath.Ext(filepath.Base(rsrcFiles[0])))+".zip")
  1464. } else if len(rsrcFiles) > 1 {
  1465. zipFilename = filepath.Join(rdestFile, filepath.Base(filepath.Dir(rsrcFiles[0]))+".zip")
  1466. }
  1467. }
  1468. //Create a buffer if destination fsh request buffer
  1469. zipFileTargetLocation := zipFilename
  1470. if destFsh.RequireBuffer {
  1471. zipFileTargetLocation = getFsBufferFilepath(zipFilename, false)
  1472. }
  1473. //Create a zip file at target location
  1474. err = fs.ArozZipFile(rsrcFiles, zipFileTargetLocation, false)
  1475. if err != nil {
  1476. os.Remove(zipFileTargetLocation)
  1477. common.SendErrorResponse(w, err.Error())
  1478. return
  1479. }
  1480. //Write it to final destination from buffer
  1481. if destFsh.RequireBuffer {
  1482. //Upload the finalized zip file
  1483. f, _ := destFshAbs.Open(zipFileTargetLocation)
  1484. destFshAbs.WriteStream(zipFilename, f, 0775)
  1485. f.Close()
  1486. //Remove all buff files
  1487. os.Remove(zipFileTargetLocation)
  1488. for _, rsrcFile := range rsrcFiles {
  1489. if isFsBufferFilepath(rsrcFile) {
  1490. os.Remove(rsrcFile)
  1491. }
  1492. }
  1493. }
  1494. } else {
  1495. //For operations that is handled file by file
  1496. for i, vsrcFile := range sourceFiles {
  1497. //Convert the virtual path to realpath on disk
  1498. rsrcFile, _ := userinfo.VirtualPathToRealPath(string(vsrcFile))
  1499. rdestFile, _ := userinfo.VirtualPathToRealPath(vdestFile)
  1500. //Check if the source file exists
  1501. if !fs.FileExists(rsrcFile) {
  1502. /*
  1503. Special edge case handler:
  1504. There might be edge case that files are stored in URIEncoded methods
  1505. e.g. abc def.mp3 --> abc%20cdf.mp3
  1506. In this case, this logic statement should be able to handle this
  1507. */
  1508. edgeCaseFilename := filepath.Join(filepath.Dir(rsrcFile), system_fs_specialURIEncode(filepath.Base(rsrcFile)))
  1509. if fs.FileExists(edgeCaseFilename) {
  1510. rsrcFile = edgeCaseFilename
  1511. } else {
  1512. common.SendErrorResponse(w, "Source file not exists")
  1513. return
  1514. }
  1515. }
  1516. if operation == "rename" {
  1517. //Check if the usage is correct.
  1518. if vdestFile != "" {
  1519. common.SendErrorResponse(w, "Rename only accept 'src' and 'new'. Please use move if you want to move a file.")
  1520. return
  1521. }
  1522. //Check if new name paramter is passed in.
  1523. if len(newFilenames) == 0 {
  1524. common.SendErrorResponse(w, "Missing paramter (JSON string): 'new'")
  1525. return
  1526. }
  1527. //Check if the source filenames and new filenanmes match
  1528. if len(newFilenames) != len(sourceFiles) {
  1529. common.SendErrorResponse(w, "New filenames do not match with source filename's length.")
  1530. return
  1531. }
  1532. //Check if the target dir is not readonly
  1533. accmode := userinfo.GetPathAccessPermission(string(vsrcFile))
  1534. if accmode == "readonly" {
  1535. common.SendErrorResponse(w, "This directory is Read Only")
  1536. return
  1537. } else if accmode == "denied" {
  1538. common.SendErrorResponse(w, "Access Denied")
  1539. return
  1540. }
  1541. thisFilename := filepath.Base(newFilenames[i])
  1542. //Check if the name already exists. If yes, return false
  1543. if fs.FileExists(filepath.Join(filepath.Dir(rsrcFile), thisFilename)) {
  1544. common.SendErrorResponse(w, "File already exists")
  1545. return
  1546. }
  1547. //Everything is ok. Rename the file.
  1548. targetNewName := filepath.Join(filepath.Dir(rsrcFile), thisFilename)
  1549. err = os.Rename(rsrcFile, targetNewName)
  1550. if err != nil {
  1551. common.SendErrorResponse(w, err.Error())
  1552. return
  1553. }
  1554. //Remove the cache for the original file
  1555. metadata.RemoveCache(rsrcFile)
  1556. } else if operation == "move" {
  1557. //File move operation. Check if the source file / dir and target directory exists
  1558. /*
  1559. Example usage from file explorer
  1560. $.ajax({
  1561. type: 'POST',
  1562. url: `/system/file_system/fileOpr`,
  1563. data: {opr: "move" ,src: JSON.stringify(fileList), dest: targetDir},
  1564. success: function(data){
  1565. if (data.error !== undefined){
  1566. msgbox("remove",data.error);
  1567. }else{
  1568. //OK, do something
  1569. }
  1570. }
  1571. });
  1572. */
  1573. if !fs.FileExists(rsrcFile) {
  1574. common.SendErrorResponse(w, "Source file not exists")
  1575. return
  1576. }
  1577. //Check if the source file is read only.
  1578. accmode := userinfo.GetPathAccessPermission(string(vsrcFile))
  1579. if accmode == "readonly" {
  1580. common.SendErrorResponse(w, "This source file is Read Only")
  1581. return
  1582. } else if accmode == "denied" {
  1583. common.SendErrorResponse(w, "Access Denied")
  1584. return
  1585. }
  1586. if rdestFile == "" {
  1587. common.SendErrorResponse(w, "Undefined dest location")
  1588. return
  1589. }
  1590. //Get exists overwrite mode
  1591. existsOpr, _ := common.Mv(r, "existsresp", true)
  1592. //Check if use fast move instead
  1593. //Check if the source and destination folder are under the same root. If yes, use os.Rename for faster move operations
  1594. //Check if the two files are under the same user root path
  1595. srcAbs, _ := filepath.Abs(rsrcFile)
  1596. destAbs, _ := filepath.Abs(rdestFile)
  1597. //Check other storage path and see if they are under the same root
  1598. /*
  1599. for _, rootPath := range userinfo.GetAllFileSystemHandler() {
  1600. thisRoot := rootPath.Path
  1601. thisRootAbs, err := filepath.Abs(thisRoot)
  1602. if err != nil {
  1603. continue
  1604. }
  1605. if strings.Contains(srcAbs, thisRootAbs) && strings.Contains(destAbs, thisRootAbs) {
  1606. underSameRoot = true
  1607. }
  1608. }*/
  1609. underSameRoot, _ := fs.UnderTheSameRoot(srcAbs, destAbs)
  1610. //Updates 19-10-2020: Added ownership management to file move and copy
  1611. userinfo.RemoveOwnershipFromFile(rsrcFile)
  1612. err = fs.FileMove(rsrcFile, rdestFile, existsOpr, underSameRoot, nil)
  1613. if err != nil {
  1614. common.SendErrorResponse(w, err.Error())
  1615. //Restore the ownership if remove failed
  1616. userinfo.SetOwnerOfFile(rsrcFile)
  1617. return
  1618. }
  1619. //Set user to own the new file
  1620. userinfo.SetOwnerOfFile(filepath.ToSlash(filepath.Clean(rdestFile)) + "/" + filepath.Base(rsrcFile))
  1621. //Remove cache for the original file
  1622. metadata.RemoveCache(rsrcFile)
  1623. } else if operation == "copy" {
  1624. //Copy file. See move example and change 'opr' to 'copy'
  1625. if !fs.FileExists(rsrcFile) {
  1626. common.SendErrorResponse(w, "Source file not exists")
  1627. return
  1628. }
  1629. //Check if the desintation is read only.
  1630. if !userinfo.CanWrite(vdestFile) {
  1631. common.SendErrorResponse(w, "Access Denied")
  1632. return
  1633. }
  1634. if !fs.FileExists(rdestFile) {
  1635. if fs.FileExists(filepath.Dir(rdestFile)) {
  1636. //User pass in the whole path for the folder. Report error usecase.
  1637. common.SendErrorResponse(w, "Dest location should be an existing folder instead of the full path of the copied file")
  1638. return
  1639. }
  1640. common.SendErrorResponse(w, "Dest folder not found")
  1641. return
  1642. }
  1643. existsOpr, _ := common.Mv(r, "existsresp", true)
  1644. //Check if the user have space for the extra file
  1645. if !userinfo.StorageQuota.HaveSpace(fs.GetFileSize(rdestFile)) {
  1646. common.SendErrorResponse(w, "Storage Quota Full")
  1647. return
  1648. }
  1649. err = fs.FileCopy(rsrcFile, rdestFile, existsOpr, nil)
  1650. if err != nil {
  1651. common.SendErrorResponse(w, err.Error())
  1652. return
  1653. }
  1654. //Set user to own this file
  1655. userinfo.SetOwnerOfFile(filepath.ToSlash(filepath.Clean(rdestFile)) + "/" + filepath.Base(rsrcFile))
  1656. } else if operation == "delete" {
  1657. //Delete the file permanently
  1658. if !fs.FileExists(rsrcFile) {
  1659. //Check if it is a non escapted file instead
  1660. common.SendErrorResponse(w, "Source file not exists")
  1661. return
  1662. }
  1663. if !userinfo.CanWrite(vsrcFile) {
  1664. common.SendErrorResponse(w, "Access Denied")
  1665. return
  1666. }
  1667. //Check if the user own this file
  1668. isOwner := userinfo.IsOwnerOfFile(rsrcFile)
  1669. if isOwner {
  1670. //This user own this system. Remove this file from his quota
  1671. userinfo.RemoveOwnershipFromFile(rsrcFile)
  1672. }
  1673. //Check if this file has any cached files. If yes, remove it
  1674. metadata.RemoveCache(rsrcFile)
  1675. //Clear the cache folder if there is no files inside
  1676. fc, _ := filepath.Glob(filepath.ToSlash(filepath.Dir(rsrcFile)) + "/.metadata/.cache/*")
  1677. if len(fc) == 0 {
  1678. os.Remove(filepath.ToSlash(filepath.Dir(rsrcFile)) + "/.metadata/.cache/")
  1679. }
  1680. os.RemoveAll(rsrcFile)
  1681. } else if operation == "recycle" {
  1682. //Put it into a subfolder named trash and allow it to to be removed later
  1683. if !fs.FileExists(rsrcFile) {
  1684. //Check if it is a non escapted file instead
  1685. common.SendErrorResponse(w, "Source file not exists")
  1686. return
  1687. }
  1688. //Check if the upload target is read only.
  1689. if !userinfo.CanWrite(vsrcFile) {
  1690. common.SendErrorResponse(w, "Access Denied")
  1691. return
  1692. }
  1693. //Check if this file has any cached files. If yes, remove it
  1694. metadata.RemoveCache(rsrcFile)
  1695. //Clear the cache folder if there is no files inside
  1696. fc, _ := filepath.Glob(filepath.ToSlash(filepath.Dir(rsrcFile)) + "/.metadata/.cache/*")
  1697. if len(fc) == 0 {
  1698. os.Remove(filepath.ToSlash(filepath.Dir(rsrcFile)) + "/.metadata/.cache/")
  1699. }
  1700. //Create a trash directory for this folder
  1701. trashDir := filepath.ToSlash(filepath.Dir(rsrcFile)) + "/.metadata/.trash/"
  1702. os.MkdirAll(trashDir, 0755)
  1703. hidden.HideFile(filepath.Dir(trashDir))
  1704. hidden.HideFile(trashDir)
  1705. os.Rename(rsrcFile, trashDir+filepath.Base(rsrcFile)+"."+common.Int64ToString(time.Now().Unix()))
  1706. } else if operation == "unzip" {
  1707. //Unzip the file to destination
  1708. //Check if the user can write to the target dest file
  1709. if !userinfo.CanWrite(string(vdestFile)) {
  1710. common.SendErrorResponse(w, "Access Denied")
  1711. return
  1712. }
  1713. //Make the rdest directory if not exists
  1714. if !fs.FileExists(rdestFile) {
  1715. err = os.MkdirAll(rdestFile, 0755)
  1716. if err != nil {
  1717. common.SendErrorResponse(w, err.Error())
  1718. return
  1719. }
  1720. }
  1721. //OK! Unzip to destination
  1722. err := fs.Unzip(rsrcFile, rdestFile)
  1723. if err != nil {
  1724. common.SendErrorResponse(w, err.Error())
  1725. return
  1726. }
  1727. } else {
  1728. common.SendErrorResponse(w, "Unknown file opeartion given")
  1729. return
  1730. }
  1731. }
  1732. }
  1733. common.SendOK(w)
  1734. }
  1735. //Allow systems to store key value pairs in the database as preferences.
  1736. func system_fs_handleUserPreference(w http.ResponseWriter, r *http.Request) {
  1737. username, err := authAgent.GetUserName(w, r)
  1738. if err != nil {
  1739. common.SendErrorResponse(w, "User not logged in")
  1740. return
  1741. }
  1742. key, _ := common.Mv(r, "key", false)
  1743. value, _ := common.Mv(r, "value", false)
  1744. remove, _ := common.Mv(r, "remove", false)
  1745. if key != "" && value == "" && remove == "" {
  1746. //Get mode. Read the prefernece with given key
  1747. result := ""
  1748. err := sysdb.Read("fs", "pref/"+key+"/"+username, &result)
  1749. if err != nil {
  1750. common.SendJSONResponse(w, "{\"error\":\"Key not found.\"}")
  1751. return
  1752. }
  1753. common.SendTextResponse(w, result)
  1754. } else if key != "" && value == "" && remove == "true" {
  1755. //Remove mode. Delete this key from sysdb
  1756. err := sysdb.Delete("fs", "pref/"+key+"/"+username)
  1757. if err != nil {
  1758. common.SendErrorResponse(w, err.Error())
  1759. }
  1760. common.SendOK(w)
  1761. } else if key != "" && value != "" {
  1762. //Set mode. Set the preference with given key
  1763. if len(value) > 1024 {
  1764. //Size too big. Reject storage
  1765. common.SendErrorResponse(w, "Preference value too long. Preference value can only store maximum 1024 characters.")
  1766. return
  1767. }
  1768. sysdb.Write("fs", "pref/"+key+"/"+username, value)
  1769. common.SendOK(w)
  1770. }
  1771. }
  1772. func system_fs_removeUserPreferences(username string) {
  1773. entries, err := sysdb.ListTable("fs")
  1774. if err != nil {
  1775. return
  1776. }
  1777. for _, keypairs := range entries {
  1778. if strings.Contains(string(keypairs[0]), "pref/") && strings.Contains(string(keypairs[0]), "/"+username) {
  1779. //Remove this preference
  1780. sysdb.Delete("fs", string(keypairs[0]))
  1781. }
  1782. }
  1783. }
  1784. func system_fs_listDrives(w http.ResponseWriter, r *http.Request) {
  1785. if authAgent.CheckAuth(r) == false {
  1786. common.SendErrorResponse(w, "User not logged in")
  1787. return
  1788. }
  1789. userinfo, _ := userHandler.GetUserInfoFromRequest(w, r)
  1790. type driveInfo struct {
  1791. Drivepath string
  1792. DriveFreeSpace uint64
  1793. DriveTotalSpace uint64
  1794. DriveAvailSpace uint64
  1795. }
  1796. var drives []driveInfo
  1797. if runtime.GOOS == "windows" {
  1798. //Under windows
  1799. for _, drive := range "ABCDEFGHIJKLMNOPQRSTUVWXYZ" {
  1800. f, err := os.Open(string(drive) + ":\\")
  1801. if err == nil {
  1802. thisdrive := new(driveInfo)
  1803. thisdrive.Drivepath = string(drive) + ":\\"
  1804. free, total, avail := storage.GetDriveCapacity(string(drive) + ":\\")
  1805. thisdrive.DriveFreeSpace = free
  1806. thisdrive.DriveTotalSpace = total
  1807. thisdrive.DriveAvailSpace = avail
  1808. drives = append(drives, *thisdrive)
  1809. f.Close()
  1810. }
  1811. }
  1812. } else {
  1813. //Under linux environment
  1814. //Append all the virtual directories root as root instead
  1815. storageDevices := []string{}
  1816. for _, fshandler := range userinfo.GetAllFileSystemHandler() {
  1817. storageDevices = append(storageDevices, fshandler.Path)
  1818. }
  1819. //List all storage information of each devices
  1820. for _, dev := range storageDevices {
  1821. thisdrive := new(driveInfo)
  1822. thisdrive.Drivepath = filepath.Base(dev)
  1823. free, total, avail := storage.GetDriveCapacity(string(dev))
  1824. thisdrive.DriveFreeSpace = free
  1825. thisdrive.DriveTotalSpace = total
  1826. thisdrive.DriveAvailSpace = avail
  1827. drives = append(drives, *thisdrive)
  1828. }
  1829. }
  1830. jsonString, _ := json.Marshal(drives)
  1831. common.SendJSONResponse(w, string(jsonString))
  1832. }
  1833. func system_fs_listRoot(w http.ResponseWriter, r *http.Request) {
  1834. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  1835. if err != nil {
  1836. common.SendErrorResponse(w, err.Error())
  1837. return
  1838. }
  1839. username := userinfo.Username
  1840. userRoot, _ := common.Mv(r, "user", false)
  1841. if userRoot == "true" {
  1842. type fileObject struct {
  1843. Filename string
  1844. Filepath string
  1845. IsDir bool
  1846. }
  1847. //List the root media folders under user:/
  1848. filesInUserRoot := []fileObject{}
  1849. filesInRoot, _ := filepath.Glob(filepath.ToSlash(filepath.Clean(*root_directory)) + "/users/" + username + "/*")
  1850. for _, file := range filesInRoot {
  1851. //Check if this is a hidden file
  1852. if len(filepath.Base(file)) > 0 && filepath.Base(file)[:1] == "." {
  1853. continue
  1854. }
  1855. thisFile := new(fileObject)
  1856. thisFile.Filename = filepath.Base(file)
  1857. thisFile.Filepath, _ = userinfo.RealPathToVirtualPath(file)
  1858. thisFile.IsDir = fs.IsDir(file)
  1859. filesInUserRoot = append(filesInUserRoot, *thisFile)
  1860. }
  1861. jsonString, _ := json.Marshal(filesInUserRoot)
  1862. common.SendJSONResponse(w, string(jsonString))
  1863. } else {
  1864. type rootObject struct {
  1865. rootID string //The vroot id
  1866. RootName string //The name of this vroot
  1867. RootPath string //The path of this vroot
  1868. RootBackups bool //If there are backup for this vroot
  1869. }
  1870. roots := []*rootObject{}
  1871. backupRoots := []string{}
  1872. for _, store := range userinfo.GetAllFileSystemHandler() {
  1873. if store.Hierarchy == "user" || store.Hierarchy == "public" {
  1874. //Normal drives
  1875. var thisDevice = new(rootObject)
  1876. thisDevice.RootName = store.Name
  1877. thisDevice.RootPath = store.UUID + ":/"
  1878. thisDevice.rootID = store.UUID
  1879. roots = append(roots, thisDevice)
  1880. } else if store.Hierarchy == "backup" {
  1881. //Backup drive.
  1882. backupRoots = append(backupRoots, store.HierarchyConfig.(hybridBackup.BackupTask).ParentUID)
  1883. } else if store.Hierarchy == "share" {
  1884. //Share emulated drive
  1885. var thisDevice = new(rootObject)
  1886. thisDevice.RootName = store.Name
  1887. thisDevice.RootPath = store.UUID + ":/"
  1888. thisDevice.rootID = store.UUID
  1889. roots = append(roots, thisDevice)
  1890. }
  1891. }
  1892. //Update root configs for backup roots
  1893. for _, backupRoot := range backupRoots {
  1894. //For this backup root, check if the parent root mounted
  1895. for _, root := range roots {
  1896. if root.rootID == backupRoot {
  1897. //Parent root mounted. Label the parent root as "have backup"
  1898. root.RootBackups = true
  1899. }
  1900. }
  1901. }
  1902. jsonString, _ := json.Marshal(roots)
  1903. common.SendJSONResponse(w, string(jsonString))
  1904. }
  1905. }
  1906. /*
  1907. Special Glob for handling path with [ or ] inside.
  1908. You can also pass in normal path for globing if you are not sure.
  1909. */
  1910. func system_fs_specialGlob(path string) ([]string, error) {
  1911. //Quick fix for foldername containing -] issue
  1912. path = strings.ReplaceAll(path, "[", "[[]")
  1913. files, err := filepath.Glob(path)
  1914. if err != nil {
  1915. return []string{}, err
  1916. }
  1917. if strings.Contains(path, "[") == true || strings.Contains(path, "]") == true {
  1918. if len(files) == 0 {
  1919. //Handle reverse check. Replace all [ and ] with *
  1920. newSearchPath := strings.ReplaceAll(path, "[", "?")
  1921. newSearchPath = strings.ReplaceAll(newSearchPath, "]", "?")
  1922. //Scan with all the similar structure except [ and ]
  1923. tmpFilelist, _ := filepath.Glob(newSearchPath)
  1924. for _, file := range tmpFilelist {
  1925. file = filepath.ToSlash(file)
  1926. if strings.Contains(file, filepath.ToSlash(filepath.Dir(path))) {
  1927. files = append(files, file)
  1928. }
  1929. }
  1930. }
  1931. }
  1932. //Convert all filepaths to slash
  1933. for i := 0; i < len(files); i++ {
  1934. files[i] = filepath.ToSlash(files[i])
  1935. }
  1936. return files, nil
  1937. }
  1938. func system_fs_specialURIDecode(inputPath string) string {
  1939. inputPath = strings.ReplaceAll(inputPath, "+", "{{plus_sign}}")
  1940. inputPath, _ = url.QueryUnescape(inputPath)
  1941. inputPath = strings.ReplaceAll(inputPath, "{{plus_sign}}", "+")
  1942. return inputPath
  1943. }
  1944. func system_fs_specialURIEncode(inputPath string) string {
  1945. inputPath = strings.ReplaceAll(inputPath, " ", "{{space_sign}}")
  1946. inputPath, _ = url.QueryUnescape(inputPath)
  1947. inputPath = strings.ReplaceAll(inputPath, "{{space_sign}}", "%20")
  1948. return inputPath
  1949. }
  1950. //Handle file properties request
  1951. func system_fs_getFileProperties(w http.ResponseWriter, r *http.Request) {
  1952. type fileProperties struct {
  1953. VirtualPath string
  1954. StoragePath string
  1955. Basename string
  1956. VirtualDirname string
  1957. StorageDirname string
  1958. Ext string
  1959. MimeType string
  1960. Filesize int64
  1961. Permission string
  1962. LastModTime string
  1963. LastModUnix int64
  1964. IsDirectory bool
  1965. Owner string
  1966. }
  1967. result := fileProperties{}
  1968. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  1969. if err != nil {
  1970. common.SendErrorResponse(w, err.Error())
  1971. return
  1972. }
  1973. vpath, err := common.Mv(r, "path", true)
  1974. if err != nil {
  1975. common.SendErrorResponse(w, "path not defined")
  1976. return
  1977. }
  1978. vrootID, subpath, _ := filesystem.GetIDFromVirtualPath(vpath)
  1979. if vrootID == "share" && subpath == "" {
  1980. result = fileProperties{
  1981. VirtualPath: vpath,
  1982. StoragePath: "(Emulated File System)",
  1983. Basename: "Share",
  1984. VirtualDirname: filepath.ToSlash(filepath.Dir(vpath)),
  1985. StorageDirname: "N/A",
  1986. Ext: "N/A",
  1987. MimeType: "emulated/fs",
  1988. Filesize: -1,
  1989. Permission: "N/A",
  1990. LastModTime: "N/A",
  1991. LastModUnix: 0,
  1992. IsDirectory: true,
  1993. Owner: "system",
  1994. }
  1995. } else {
  1996. rpath, err := userinfo.VirtualPathToRealPath(vpath)
  1997. if err != nil {
  1998. common.SendErrorResponse(w, err.Error())
  1999. return
  2000. }
  2001. fileStat, err := os.Stat(rpath)
  2002. if err != nil {
  2003. common.SendErrorResponse(w, err.Error())
  2004. return
  2005. }
  2006. mime := "text/directory"
  2007. if !fileStat.IsDir() {
  2008. m, _, err := fs.GetMime(rpath)
  2009. if err != nil {
  2010. mime = ""
  2011. }
  2012. mime = m
  2013. }
  2014. filesize := fileStat.Size()
  2015. //Get file overall size if this is folder
  2016. if fileStat.IsDir() {
  2017. var size int64
  2018. filepath.Walk(rpath, func(_ string, info os.FileInfo, err error) error {
  2019. if err != nil {
  2020. return err
  2021. }
  2022. if !info.IsDir() {
  2023. size += info.Size()
  2024. }
  2025. return err
  2026. })
  2027. filesize = size
  2028. }
  2029. //Get file owner
  2030. owner := userinfo.GetFileOwner(rpath)
  2031. if owner == "" {
  2032. //Handle special virtual roots
  2033. vrootID, subpath, _ := filesystem.GetIDFromVirtualPath(vpath)
  2034. if vrootID == "share" {
  2035. //Share objects
  2036. shareOption, _ := shareEntryTable.ResolveShareOptionFromShareSubpath(subpath)
  2037. if shareOption != nil {
  2038. owner = shareOption.Owner
  2039. } else {
  2040. owner = "Unknown"
  2041. }
  2042. } else {
  2043. owner = "Unknown"
  2044. }
  2045. }
  2046. result = fileProperties{
  2047. VirtualPath: vpath,
  2048. StoragePath: filepath.Clean(rpath),
  2049. Basename: filepath.Base(rpath),
  2050. VirtualDirname: filepath.ToSlash(filepath.Dir(vpath)),
  2051. StorageDirname: filepath.ToSlash(filepath.Dir(rpath)),
  2052. Ext: filepath.Ext(rpath),
  2053. MimeType: mime,
  2054. Filesize: filesize,
  2055. Permission: fileStat.Mode().Perm().String(),
  2056. LastModTime: fileStat.ModTime().Format("2006-01-02 15:04:05"),
  2057. LastModUnix: fileStat.ModTime().Unix(),
  2058. IsDirectory: fileStat.IsDir(),
  2059. Owner: owner,
  2060. }
  2061. }
  2062. jsonString, _ := json.Marshal(result)
  2063. common.SendJSONResponse(w, string(jsonString))
  2064. }
  2065. /*
  2066. List directory in the given path
  2067. Usage: Pass in dir like the following examples:
  2068. AOR:/Desktop <= Open /user/{username}/Desktop
  2069. S1:/ <= Open {uuid=S1}/
  2070. */
  2071. func system_fs_handleList(w http.ResponseWriter, r *http.Request) {
  2072. currentDir, _ := common.Mv(r, "dir", true)
  2073. //Commented this line to handle dirname that contains "+" sign
  2074. //currentDir, _ = url.QueryUnescape(currentDir)
  2075. sortMode, _ := common.Mv(r, "sort", true)
  2076. showHidden, _ := common.Mv(r, "showHidden", true)
  2077. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  2078. if err != nil {
  2079. //user not logged in. Redirect to login page.
  2080. common.SendErrorResponse(w, "User not logged in")
  2081. return
  2082. }
  2083. if currentDir == "" {
  2084. common.SendErrorResponse(w, "Invalid dir given.")
  2085. return
  2086. }
  2087. //Pad a slash at the end of currentDir if not exists
  2088. if currentDir[len(currentDir)-1:] != "/" {
  2089. currentDir = currentDir + "/"
  2090. }
  2091. fsh, subpath, err := GetFSHandlerSubpathFromVpath(currentDir)
  2092. if err != nil {
  2093. common.SendErrorResponse(w, err.Error())
  2094. return
  2095. }
  2096. fshAbs := fsh.FileSystemAbstraction
  2097. var parsedFilelist []fs.FileData
  2098. //Handle some special virtual file systems / mount points
  2099. if fsh.UUID == "share" && subpath == "" {
  2100. userpgs := userinfo.GetUserPermissionGroupNames()
  2101. files := shareEntryTable.ListRootForUser(userinfo.Username, userpgs)
  2102. parsedFilelist = files
  2103. } else {
  2104. //Normal file systems
  2105. realpath, err := fshAbs.VirtualPathToRealPath(subpath, userinfo.Username)
  2106. if err != nil {
  2107. common.SendErrorResponse(w, err.Error())
  2108. return
  2109. }
  2110. if !fshAbs.FileExists(realpath) {
  2111. //Path not exists
  2112. userRoot, _ := fshAbs.VirtualPathToRealPath("", userinfo.Username)
  2113. if filepath.Clean(realpath) == filepath.Clean(userRoot) {
  2114. //Initiate user folder (Initiaed in user object)
  2115. userinfo.GetHomeDirectory()
  2116. } else if !strings.Contains(filepath.ToSlash(filepath.Clean(currentDir)), "/") {
  2117. //User root not created. Create the root folder
  2118. os.MkdirAll(filepath.Clean(realpath), 0775)
  2119. } else {
  2120. //Folder not exists
  2121. log.Println("[File Explorer] Requested path: ", realpath, " does not exists!")
  2122. common.SendErrorResponse(w, "Folder not exists")
  2123. return
  2124. }
  2125. }
  2126. /*
  2127. //Convert the virutal path to realpath
  2128. realpath, err = userinfo.VirtualPathToRealPath(currentDir)
  2129. if err != nil {
  2130. common.SendErrorResponse(w, err.Error())
  2131. return
  2132. }
  2133. if !fs.FileExists(realpath) {
  2134. //Path not exists
  2135. userRoot, _ := userinfo.VirtualPathToRealPath("user:/")
  2136. if filepath.Clean(realpath) == filepath.Clean(userRoot) {
  2137. //Initiate user folder (Initiaed in user object)
  2138. userinfo.GetHomeDirectory()
  2139. } else if !strings.Contains(filepath.ToSlash(filepath.Clean(currentDir)), "/") {
  2140. //User root not created. Create the root folder
  2141. os.MkdirAll(filepath.Clean(realpath), 0775)
  2142. } else {
  2143. //Folder not exists
  2144. log.Println("[File Explorer] Requested path: ", realpath, " does not exists!")
  2145. common.SendErrorResponse(w, "Folder not exists")
  2146. return
  2147. }
  2148. }
  2149. */
  2150. if sortMode == "" {
  2151. sortMode = "default"
  2152. }
  2153. //Check for really special exception in where the path contains [ or ] which cannot be handled via Golang Glob function
  2154. files, err := fshAbs.Glob(realpath + "/*")
  2155. if err != nil {
  2156. log.Println("[File System] Unable to list dir: " + err.Error())
  2157. }
  2158. var shortCutInfo *shortcut.ShortcutData = nil
  2159. for _, v := range files {
  2160. //Check if it is hidden file
  2161. isHidden, _ := hidden.IsHidden(v, false)
  2162. if showHidden != "true" && isHidden {
  2163. //Skipping hidden files
  2164. continue
  2165. }
  2166. //Check if this is an aodb file
  2167. if filepath.Base(v) == "aofs.db" || filepath.Base(v) == "aofs.db.lock" {
  2168. //Database file (reserved)
  2169. continue
  2170. }
  2171. //Check if it is shortcut file. If yes, render a shortcut data struct
  2172. if filepath.Ext(v) == ".shortcut" {
  2173. //This is a shortcut file
  2174. shorcutData, err := shortcut.ReadShortcut(v)
  2175. if err == nil {
  2176. shortCutInfo = shorcutData
  2177. }
  2178. }
  2179. rawsize := fshAbs.GetFileSize(v)
  2180. modtime, _ := fshAbs.GetModTime(v)
  2181. thisFile := fs.FileData{
  2182. Filename: filepath.Base(v),
  2183. Filepath: currentDir + filepath.Base(v),
  2184. Realpath: v,
  2185. IsDir: fsh.FileSystemAbstraction.IsDir(v),
  2186. Filesize: rawsize,
  2187. Displaysize: fs.GetFileDisplaySize(rawsize, 2),
  2188. ModTime: modtime,
  2189. IsShared: shareManager.FileIsShared(v),
  2190. Shortcut: shortCutInfo,
  2191. }
  2192. parsedFilelist = append(parsedFilelist, thisFile)
  2193. }
  2194. }
  2195. //Sort the filelist
  2196. if sortMode == "default" {
  2197. //Sort by name, convert filename to window sorting methods
  2198. sort.Slice(parsedFilelist, func(i, j int) bool {
  2199. return strings.ToLower(parsedFilelist[i].Filename) < strings.ToLower(parsedFilelist[j].Filename)
  2200. })
  2201. } else if sortMode == "reverse" {
  2202. //Sort by reverse name
  2203. sort.Slice(parsedFilelist, func(i, j int) bool {
  2204. return strings.ToLower(parsedFilelist[i].Filename) > strings.ToLower(parsedFilelist[j].Filename)
  2205. })
  2206. } else if sortMode == "smallToLarge" {
  2207. sort.Slice(parsedFilelist, func(i, j int) bool { return parsedFilelist[i].Filesize < parsedFilelist[j].Filesize })
  2208. } else if sortMode == "largeToSmall" {
  2209. sort.Slice(parsedFilelist, func(i, j int) bool { return parsedFilelist[i].Filesize > parsedFilelist[j].Filesize })
  2210. } else if sortMode == "mostRecent" {
  2211. sort.Slice(parsedFilelist, func(i, j int) bool { return parsedFilelist[i].ModTime > parsedFilelist[j].ModTime })
  2212. } else if sortMode == "leastRecent" {
  2213. sort.Slice(parsedFilelist, func(i, j int) bool { return parsedFilelist[i].ModTime < parsedFilelist[j].ModTime })
  2214. }
  2215. jsonString, _ := json.Marshal(parsedFilelist)
  2216. common.SendJSONResponse(w, string(jsonString))
  2217. }
  2218. //Handle getting a hash from a given contents in the given path
  2219. func system_fs_handleDirHash(w http.ResponseWriter, r *http.Request) {
  2220. currentDir, err := common.Mv(r, "dir", true)
  2221. if err != nil {
  2222. common.SendErrorResponse(w, "Invalid dir given")
  2223. return
  2224. }
  2225. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  2226. if err != nil {
  2227. common.SendErrorResponse(w, "User not logged in")
  2228. return
  2229. }
  2230. fsh, subpath, _ := GetFSHandlerSubpathFromVpath(currentDir)
  2231. if fsh.UUID == "share" && subpath == "" {
  2232. common.SendTextResponse(w, hex.EncodeToString([]byte("0")))
  2233. return
  2234. }
  2235. fshAbs := fsh.FileSystemAbstraction
  2236. rpath, err := fshAbs.VirtualPathToRealPath(currentDir, userinfo.Username)
  2237. if err != nil {
  2238. common.SendErrorResponse(w, "Invalid dir given")
  2239. return
  2240. }
  2241. //Get a list of files in this directory
  2242. currentDir = filepath.ToSlash(filepath.Clean(rpath)) + "/"
  2243. filesInDir, err := fshAbs.Glob(currentDir + "*")
  2244. if err != nil {
  2245. common.SendErrorResponse(w, err.Error())
  2246. return
  2247. }
  2248. filenames := []string{}
  2249. for _, file := range filesInDir {
  2250. if len(filepath.Base(file)) > 0 && string([]rune(filepath.Base(file))[0]) != "." {
  2251. //Ignore hidden files
  2252. filenames = append(filenames, filepath.Base(file))
  2253. }
  2254. }
  2255. sort.Strings(filenames)
  2256. //Build a hash base on the filelist
  2257. h := sha256.New()
  2258. h.Write([]byte(strings.Join(filenames, ",")))
  2259. common.SendTextResponse(w, hex.EncodeToString((h.Sum(nil))))
  2260. }
  2261. /*
  2262. File zipping and unzipping functions
  2263. */
  2264. //Handle all zip related API
  2265. func system_fs_zipHandler(w http.ResponseWriter, r *http.Request) {
  2266. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  2267. if err != nil {
  2268. common.SendErrorResponse(w, err.Error())
  2269. return
  2270. }
  2271. opr, err := common.Mv(r, "opr", true)
  2272. if err != nil {
  2273. common.SendErrorResponse(w, "Invalid opr or opr not defined")
  2274. return
  2275. }
  2276. vsrc, _ := common.Mv(r, "src", true)
  2277. if vsrc == "" {
  2278. common.SendErrorResponse(w, "Invalid src paramter")
  2279. return
  2280. }
  2281. vdest, _ := common.Mv(r, "dest", true)
  2282. rdest := ""
  2283. //Convert source path from JSON string to object
  2284. virtualSourcePaths := []string{}
  2285. err = json.Unmarshal([]byte(vsrc), &virtualSourcePaths)
  2286. if err != nil {
  2287. common.SendErrorResponse(w, err.Error())
  2288. return
  2289. }
  2290. //Check each of the path
  2291. realSourcePaths := []string{}
  2292. for _, vpath := range virtualSourcePaths {
  2293. thisrpath, err := userinfo.VirtualPathToRealPath(vpath)
  2294. if err != nil || !fs.FileExists(thisrpath) {
  2295. common.SendErrorResponse(w, "File not exists: "+vpath)
  2296. return
  2297. }
  2298. realSourcePaths = append(realSourcePaths, thisrpath)
  2299. }
  2300. ///Convert dest to real if given
  2301. if vdest != "" {
  2302. realdest, _ := userinfo.VirtualPathToRealPath(vdest)
  2303. rdest = realdest
  2304. }
  2305. //This function will be deprecate soon in ArozOS 1.120
  2306. log.Println("*DEPRECATE* zipHandler will be deprecating soon! Please use fileOpr endpoint")
  2307. if opr == "zip" {
  2308. //Check if destination location exists
  2309. if rdest == "" || !fs.FileExists(filepath.Dir(rdest)) {
  2310. common.SendErrorResponse(w, "Invalid dest location")
  2311. return
  2312. }
  2313. //OK. Create the zip at the desired location
  2314. err := fs.ArozZipFile(realSourcePaths, rdest, false)
  2315. if err != nil {
  2316. common.SendErrorResponse(w, err.Error())
  2317. return
  2318. }
  2319. common.SendOK(w)
  2320. } else if opr == "tmpzip" {
  2321. //Zip to tmp folder
  2322. userTmpFolder, _ := userinfo.VirtualPathToRealPath("tmp:/")
  2323. filename := common.Int64ToString(time.Now().Unix()) + ".zip"
  2324. rdest := filepath.ToSlash(filepath.Clean(userTmpFolder)) + "/" + filename
  2325. log.Println(realSourcePaths, rdest)
  2326. err := fs.ArozZipFile(realSourcePaths, rdest, false)
  2327. if err != nil {
  2328. common.SendErrorResponse(w, err.Error())
  2329. return
  2330. }
  2331. //Send the tmp filename to the user
  2332. common.SendTextResponse(w, "tmp:/"+filename)
  2333. } else if opr == "inspect" {
  2334. } else if opr == "unzip" {
  2335. }
  2336. }
  2337. //Manage file version history
  2338. func system_fs_FileVersionHistory(w http.ResponseWriter, r *http.Request) {
  2339. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  2340. if err != nil {
  2341. common.SendErrorResponse(w, err.Error())
  2342. return
  2343. }
  2344. path, err := common.Mv(r, "path", true)
  2345. if err != nil {
  2346. common.SendErrorResponse(w, "Invalid path given")
  2347. return
  2348. }
  2349. opr, _ := common.Mv(r, "opr", true)
  2350. rpath, err := userinfo.VirtualPathToRealPath(path)
  2351. if err != nil {
  2352. common.SendErrorResponse(w, "Unable to translate virtual path")
  2353. return
  2354. }
  2355. if opr == "" {
  2356. //List file history
  2357. fileVersionData, err := localversion.GetFileVersionData(rpath)
  2358. if err != nil {
  2359. common.SendErrorResponse(w, "Unable to load version information: "+err.Error())
  2360. return
  2361. }
  2362. js, _ := json.Marshal(fileVersionData)
  2363. common.SendJSONResponse(w, string(js))
  2364. } else if opr == "delete" {
  2365. //Delete file history of given history ID
  2366. historyID, err := common.Mv(r, "histid", true)
  2367. if err != nil {
  2368. common.SendErrorResponse(w, "Invalid history id given")
  2369. return
  2370. }
  2371. err = localversion.RemoveFileHistory(rpath, historyID)
  2372. if err != nil {
  2373. common.SendErrorResponse(w, err.Error())
  2374. return
  2375. }
  2376. common.SendOK(w)
  2377. } else if opr == "deleteAll" {
  2378. //Delete all file history of given vpath
  2379. err = localversion.RemoveAllRelatedFileHistory(rpath)
  2380. if err != nil {
  2381. common.SendErrorResponse(w, err.Error())
  2382. return
  2383. }
  2384. common.SendOK(w)
  2385. } else if opr == "restore" {
  2386. //Restore file history of given history ID
  2387. historyID, err := common.Mv(r, "histid", true)
  2388. if err != nil {
  2389. common.SendErrorResponse(w, "Invalid history id given")
  2390. return
  2391. }
  2392. err = localversion.RestoreFileHistory(rpath, historyID)
  2393. if err != nil {
  2394. common.SendErrorResponse(w, err.Error())
  2395. return
  2396. }
  2397. common.SendOK(w)
  2398. } else if opr == "new" {
  2399. //Create a new snapshot of this file
  2400. err = localversion.CreateFileSnapshot(rpath)
  2401. if err != nil {
  2402. common.SendErrorResponse(w, err.Error())
  2403. return
  2404. }
  2405. common.SendOK(w)
  2406. } else {
  2407. common.SendErrorResponse(w, "Unknown opr")
  2408. }
  2409. }
  2410. func system_fs_clearVersionHistories() {
  2411. for _, fsh := range fsHandlers {
  2412. if !fsh.IsVirtual() && !fsh.ReadOnly {
  2413. localversion.CleanExpiredVersionBackups(fsh.Path, 30*86400)
  2414. }
  2415. }
  2416. }
  2417. //Translate path from and to virtual and realpath
  2418. func system_fs_handlePathTranslate(w http.ResponseWriter, r *http.Request) {
  2419. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  2420. if err != nil {
  2421. common.SendErrorResponse(w, err.Error())
  2422. return
  2423. }
  2424. path, err := common.Mv(r, "path", false)
  2425. if err != nil {
  2426. common.SendErrorResponse(w, "Invalid path given")
  2427. return
  2428. }
  2429. rpath, err := userinfo.VirtualPathToRealPath(path)
  2430. if err != nil {
  2431. //Try to convert it to virtualPath
  2432. vpath, err := userinfo.RealPathToVirtualPath(path)
  2433. if err != nil {
  2434. common.SendErrorResponse(w, "Unknown path given")
  2435. } else {
  2436. jsonstring, _ := json.Marshal(vpath)
  2437. common.SendJSONResponse(w, string(jsonstring))
  2438. }
  2439. } else {
  2440. abrpath, _ := filepath.Abs(rpath)
  2441. jsonstring, _ := json.Marshal([]string{rpath, filepath.ToSlash(abrpath)})
  2442. common.SendJSONResponse(w, string(jsonstring))
  2443. }
  2444. }
  2445. //Handle cache rendering with websocket pipeline
  2446. func system_fs_handleCacheRender(w http.ResponseWriter, r *http.Request) {
  2447. userinfo, _ := userHandler.GetUserInfoFromRequest(w, r)
  2448. vpath, err := common.Mv(r, "folder", false)
  2449. if err != nil {
  2450. common.SendErrorResponse(w, "Invalid folder paramter")
  2451. return
  2452. }
  2453. //Convert vpath to realpath
  2454. rpath, err := userinfo.VirtualPathToRealPath(vpath)
  2455. if err != nil {
  2456. common.SendErrorResponse(w, err.Error())
  2457. return
  2458. }
  2459. //Get folder sort mode
  2460. sortMode := "default"
  2461. folder := filepath.ToSlash(filepath.Clean(vpath))
  2462. if sysdb.KeyExists("fs-sortpref", userinfo.Username+"/"+folder) {
  2463. sysdb.Read("fs-sortpref", userinfo.Username+"/"+folder, &sortMode)
  2464. }
  2465. //Perform cache rendering
  2466. thumbRenderHandler.HandleLoadCache(w, r, rpath, sortMode)
  2467. }
  2468. //Handle loading of one thumbnail
  2469. func system_fs_handleThumbnailLoad(w http.ResponseWriter, r *http.Request) {
  2470. userinfo, _ := userHandler.GetUserInfoFromRequest(w, r)
  2471. vpath, err := common.Mv(r, "vpath", false)
  2472. if err != nil {
  2473. common.SendErrorResponse(w, "vpath not defined")
  2474. return
  2475. }
  2476. rpath, err := userinfo.VirtualPathToRealPath(vpath)
  2477. if err != nil {
  2478. common.SendErrorResponse(w, err.Error())
  2479. return
  2480. }
  2481. byteMode, _ := common.Mv(r, "bytes", false)
  2482. if byteMode == "true" {
  2483. thumbnailBytes, err := thumbRenderHandler.LoadCacheAsBytes(rpath, false)
  2484. if err != nil {
  2485. common.SendErrorResponse(w, err.Error())
  2486. return
  2487. }
  2488. filetype := http.DetectContentType(thumbnailBytes)
  2489. w.Header().Add("Content-Type", filetype)
  2490. w.Write(thumbnailBytes)
  2491. } else {
  2492. thumbnailPath, err := thumbRenderHandler.LoadCache(rpath, false)
  2493. if err != nil {
  2494. common.SendErrorResponse(w, err.Error())
  2495. return
  2496. }
  2497. js, _ := json.Marshal(thumbnailPath)
  2498. common.SendJSONResponse(w, string(js))
  2499. }
  2500. }
  2501. //Handle file thumbnail caching
  2502. func system_fs_handleFolderCache(w http.ResponseWriter, r *http.Request) {
  2503. userinfo, _ := userHandler.GetUserInfoFromRequest(w, r)
  2504. vfolderpath, err := common.Mv(r, "folder", false)
  2505. if err != nil {
  2506. common.SendErrorResponse(w, "folder not defined")
  2507. return
  2508. }
  2509. rpath, err := userinfo.VirtualPathToRealPath(vfolderpath)
  2510. if err != nil {
  2511. common.SendErrorResponse(w, err.Error())
  2512. return
  2513. }
  2514. thumbRenderHandler.BuildCacheForFolder(rpath)
  2515. common.SendOK(w)
  2516. }
  2517. //Handle the get and set of sort mode of a particular folder
  2518. func system_fs_handleFolderSortModePreference(w http.ResponseWriter, r *http.Request) {
  2519. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  2520. if err != nil {
  2521. common.SendErrorResponse(w, "User not logged in")
  2522. return
  2523. }
  2524. folder, err := common.Mv(r, "folder", true)
  2525. if err != nil {
  2526. common.SendErrorResponse(w, "Invalid folder given")
  2527. return
  2528. }
  2529. opr, _ := common.Mv(r, "opr", true)
  2530. folder = filepath.ToSlash(filepath.Clean(folder))
  2531. if opr == "" || opr == "get" {
  2532. sortMode := "default"
  2533. if sysdb.KeyExists("fs-sortpref", userinfo.Username+"/"+folder) {
  2534. sysdb.Read("fs-sortpref", userinfo.Username+"/"+folder, &sortMode)
  2535. }
  2536. js, err := json.Marshal(sortMode)
  2537. if err != nil {
  2538. common.SendErrorResponse(w, err.Error())
  2539. return
  2540. }
  2541. common.SendJSONResponse(w, string(js))
  2542. } else if opr == "set" {
  2543. sortMode, err := common.Mv(r, "mode", true)
  2544. if err != nil {
  2545. common.SendErrorResponse(w, "Invalid sort mode given")
  2546. return
  2547. }
  2548. if !common.StringInArray([]string{"default", "reverse", "smallToLarge", "largeToSmall", "mostRecent", "leastRecent"}, sortMode) {
  2549. common.SendErrorResponse(w, "Not supported sort mode: "+sortMode)
  2550. return
  2551. }
  2552. sysdb.Write("fs-sortpref", userinfo.Username+"/"+folder, sortMode)
  2553. common.SendOK(w)
  2554. } else {
  2555. common.SendErrorResponse(w, "Invalid opr mode")
  2556. return
  2557. }
  2558. }
  2559. //Handle setting and loading of file permission on Linux
  2560. func system_fs_handleFilePermission(w http.ResponseWriter, r *http.Request) {
  2561. file, err := common.Mv(r, "file", true)
  2562. if err != nil {
  2563. common.SendErrorResponse(w, "Invalid file")
  2564. return
  2565. }
  2566. //Translate the file to real path
  2567. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  2568. if err != nil {
  2569. common.SendErrorResponse(w, "User not logged in")
  2570. return
  2571. }
  2572. rpath, err := userinfo.VirtualPathToRealPath(file)
  2573. if err != nil {
  2574. common.SendErrorResponse(w, err.Error())
  2575. return
  2576. }
  2577. newMode, _ := common.Mv(r, "mode", true)
  2578. if newMode == "" {
  2579. //Read the file mode
  2580. //Check if the file exists
  2581. if !fs.FileExists(rpath) {
  2582. common.SendErrorResponse(w, "File not exists!")
  2583. return
  2584. }
  2585. //Read the file permission
  2586. filePermission, err := fsp.GetFilePermissions(rpath)
  2587. if err != nil {
  2588. common.SendErrorResponse(w, err.Error())
  2589. return
  2590. }
  2591. //Send the file permission to client
  2592. js, _ := json.Marshal(filePermission)
  2593. common.SendJSONResponse(w, string(js))
  2594. } else {
  2595. //Set the file mode
  2596. //Check if the file exists
  2597. if !fs.FileExists(rpath) {
  2598. common.SendErrorResponse(w, "File not exists!")
  2599. return
  2600. }
  2601. //Check if windows. If yes, ignore this request
  2602. if runtime.GOOS == "windows" {
  2603. common.SendErrorResponse(w, "Windows host not supported")
  2604. return
  2605. }
  2606. //Check if this user has permission to change the file permission
  2607. //Aka user must be 1. This is his own folder or 2. Admin
  2608. fsh, _ := userinfo.GetFileSystemHandlerFromVirtualPath(file)
  2609. if fsh.Hierarchy == "user" {
  2610. //Always ok as this is owned by the user
  2611. } else if fsh.Hierarchy == "public" {
  2612. //Require admin
  2613. if userinfo.IsAdmin() == false {
  2614. common.SendErrorResponse(w, "Permission Denied")
  2615. return
  2616. }
  2617. } else {
  2618. //Not implemeneted. Require admin
  2619. if userinfo.IsAdmin() == false {
  2620. common.SendErrorResponse(w, "Permission Denied")
  2621. return
  2622. }
  2623. }
  2624. //Be noted that if the system is not running in sudo mode,
  2625. //File permission change might not works.
  2626. err := fsp.SetFilePermisson(rpath, newMode)
  2627. if err != nil {
  2628. common.SendErrorResponse(w, err.Error())
  2629. return
  2630. } else {
  2631. common.SendOK(w)
  2632. }
  2633. }
  2634. }
  2635. //Check if the given filepath is and must inside the given directory path.
  2636. //You can pass both as relative
  2637. func system_fs_checkFileInDirectory(filesourcepath string, directory string) bool {
  2638. filepathAbs, err := filepath.Abs(filesourcepath)
  2639. if err != nil {
  2640. return false
  2641. }
  2642. directoryAbs, err := filepath.Abs(directory)
  2643. if err != nil {
  2644. return false
  2645. }
  2646. //Check if the filepathabs contain directoryAbs
  2647. if strings.Contains(filepathAbs, directoryAbs) {
  2648. return true
  2649. } else {
  2650. return false
  2651. }
  2652. }
  2653. //Clear the old files inside the tmp file
  2654. func system_fs_clearOldTmpFiles() {
  2655. filesToBeDelete := []string{}
  2656. tmpAbs, _ := filepath.Abs(*tmp_directory)
  2657. filepath.Walk(*tmp_directory, func(path string, info os.FileInfo, err error) error {
  2658. if filepath.Base(path) != "aofs.db" && filepath.Base(path) != "aofs.db.lock" {
  2659. //Check if root folders. Do not delete root folders
  2660. parentAbs, _ := filepath.Abs(filepath.Dir(path))
  2661. if tmpAbs == parentAbs {
  2662. //Root folder. Do not remove
  2663. return nil
  2664. }
  2665. //Get its modification time
  2666. modTime, err := fs.GetModTime(path)
  2667. if err != nil {
  2668. return nil
  2669. }
  2670. //Check if mod time is more than 24 hours ago
  2671. if time.Now().Unix()-modTime > int64(*maxTempFileKeepTime) {
  2672. //Delete OK
  2673. filesToBeDelete = append(filesToBeDelete, path)
  2674. }
  2675. }
  2676. return nil
  2677. })
  2678. //Remove all files from the delete list
  2679. for _, fileToBeDelete := range filesToBeDelete {
  2680. os.RemoveAll(fileToBeDelete)
  2681. }
  2682. }
  2683. /*
  2684. File System Utilities for Buffered type FS
  2685. These functions help create a local representation of file
  2686. buffer from remote file systems like webdav or SMB
  2687. **REMEMBER TO CLEAR THE BUFFER FILES YOURSELF**
  2688. Example Usage
  2689. //Replace a destination path (for file create) with local buffer filepath
  2690. if destFsh.RequireBuffer {
  2691. dest = getFsBufferFilepath(outputFilename)
  2692. }
  2693. //Buffer a remote file to local first before doing any advance file operations
  2694. if thisSrcFsh.RequireBuffer {
  2695. localBufferFilepath, err := bufferRemoteFileToLocal(fsh, remoteRealSrc)
  2696. if err != nil{
  2697. //Handle Error
  2698. }
  2699. }
  2700. //Clean a list of source files that contains local buffer files
  2701. clearnFsBufferFileFromList(realSourceFiles)
  2702. */
  2703. //Generate a random buffer filepath. Remember to delete file after usage
  2704. func getFsBufferFilepath(originalFilename string, keepOriginalName bool) string {
  2705. thisBuffFilename := uuid.NewV4().String()
  2706. tmpDir := filepath.Join(*tmp_directory, "fsBuff")
  2707. targetFile := filepath.Join(tmpDir, thisBuffFilename+filepath.Ext(originalFilename))
  2708. if keepOriginalName {
  2709. targetFile = filepath.Join(tmpDir, thisBuffFilename, filepath.Base(originalFilename))
  2710. }
  2711. os.MkdirAll(filepath.Dir(targetFile), 0775)
  2712. return filepath.ToSlash(targetFile)
  2713. }
  2714. //Generate a buffer filepath and buffer the remote file to local. Remember to remove file after done.
  2715. func bufferRemoteFileToLocal(targetFsh *filesystem.FileSystemHandler, rpath string, keepOriginalName bool) (string, error) {
  2716. newBufferFilename := getFsBufferFilepath(rpath, keepOriginalName)
  2717. src, err := targetFsh.FileSystemAbstraction.ReadStream(rpath)
  2718. if err != nil {
  2719. fmt.Println(err.Error())
  2720. return "", err
  2721. }
  2722. dest, err := os.OpenFile(newBufferFilename, os.O_CREATE|os.O_WRONLY, 0775)
  2723. if err != nil {
  2724. fmt.Println(err.Error())
  2725. return "", err
  2726. }
  2727. io.Copy(dest, src)
  2728. dest.Close()
  2729. src.Close()
  2730. return newBufferFilename, nil
  2731. }
  2732. //Check if a file is buffer filepath
  2733. func isFsBufferFilepath(filename string) bool {
  2734. tmpDir := filepath.Join(*tmp_directory, "fsBuff")
  2735. filenameAbs, _ := filepath.Abs(filename)
  2736. filenameAbs = filepath.ToSlash(filenameAbs)
  2737. tmpDirAbs, _ := filepath.Abs(tmpDir)
  2738. tmpDirAbs = filepath.ToSlash(tmpDirAbs)
  2739. return strings.HasPrefix(filenameAbs, tmpDirAbs)
  2740. }
  2741. func cleanFsBufferFileFromList(filelist []string) {
  2742. for _, thisFilepath := range filelist {
  2743. if isFsBufferFilepath(thisFilepath) {
  2744. os.RemoveAll(thisFilepath)
  2745. folderContent, _ := os.ReadDir(filepath.Dir(thisFilepath))
  2746. if len(folderContent) == 0 {
  2747. //Nothing in this folder. Remove it
  2748. os.Remove(filepath.Dir(thisFilepath))
  2749. }
  2750. }
  2751. }
  2752. }