share.go 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384
  1. package share
  2. /*
  3. Arozos File Share Manager
  4. author: tobychui
  5. This module handle file share request and other stuffs
  6. */
  7. import (
  8. "encoding/json"
  9. "errors"
  10. "fmt"
  11. "image"
  12. "image/color"
  13. "image/draw"
  14. "image/jpeg"
  15. "io"
  16. "io/fs"
  17. "io/ioutil"
  18. "log"
  19. "math"
  20. "mime"
  21. "net/http"
  22. "net/url"
  23. "os"
  24. "path/filepath"
  25. "sort"
  26. "strconv"
  27. "strings"
  28. "time"
  29. "github.com/golang/freetype"
  30. "github.com/nfnt/resize"
  31. uuid "github.com/satori/go.uuid"
  32. "github.com/valyala/fasttemplate"
  33. "imuslab.com/arozos/mod/auth"
  34. "imuslab.com/arozos/mod/common"
  35. filesystem "imuslab.com/arozos/mod/filesystem"
  36. "imuslab.com/arozos/mod/filesystem/arozfs"
  37. "imuslab.com/arozos/mod/filesystem/metadata"
  38. "imuslab.com/arozos/mod/share/shareEntry"
  39. "imuslab.com/arozos/mod/user"
  40. )
  41. type Options struct {
  42. AuthAgent *auth.AuthAgent
  43. UserHandler *user.UserHandler
  44. ShareEntryTable *shareEntry.ShareEntryTable
  45. HostName string
  46. TmpFolder string
  47. }
  48. type Manager struct {
  49. options Options
  50. }
  51. //Create a new Share Manager
  52. func NewShareManager(options Options) *Manager {
  53. //Return a new manager object
  54. return &Manager{
  55. options: options,
  56. }
  57. }
  58. func (s *Manager) HandleOPGServing(w http.ResponseWriter, r *http.Request, shareID string) {
  59. shareEntry := s.GetShareObjectFromUUID(shareID)
  60. if shareEntry == nil {
  61. //This share is not valid
  62. http.NotFound(w, r)
  63. return
  64. }
  65. //Overlap and generate opg
  66. //Load in base template
  67. baseTemplate, err := os.Open("./system/share/default_opg.png")
  68. if err != nil {
  69. fmt.Println("[share/opg] " + err.Error())
  70. http.NotFound(w, r)
  71. return
  72. }
  73. base, _, err := image.Decode(baseTemplate)
  74. if err != nil {
  75. fmt.Println("[share/opg] " + err.Error())
  76. http.NotFound(w, r)
  77. return
  78. }
  79. //Create base canvas
  80. rx := image.Rectangle{image.Point{0, 0}, base.Bounds().Size()}
  81. resultopg := image.NewRGBA(rx)
  82. draw.Draw(resultopg, base.Bounds(), base, image.Point{0, 0}, draw.Src)
  83. //Append filename to the image
  84. fontBytes, err := ioutil.ReadFile("./system/share/fonts/TaipeiSansTCBeta-Light.ttf")
  85. if err != nil {
  86. fmt.Println("[share/opg] " + err.Error())
  87. http.NotFound(w, r)
  88. return
  89. }
  90. utf8Font, err := freetype.ParseFont(fontBytes)
  91. if err != nil {
  92. fmt.Println("[share/opg] " + err.Error())
  93. http.NotFound(w, r)
  94. return
  95. }
  96. fontSize := float64(42)
  97. ctx := freetype.NewContext()
  98. ctx.SetDPI(72)
  99. ctx.SetFont(utf8Font)
  100. ctx.SetFontSize(fontSize)
  101. ctx.SetClip(resultopg.Bounds())
  102. ctx.SetDst(resultopg)
  103. ctx.SetSrc(image.NewUniform(color.RGBA{255, 255, 255, 255}))
  104. //Check if we need to split the filename into two lines
  105. filename := filepath.Base(shareEntry.FileRealPath)
  106. filenameOnly := strings.TrimSuffix(filename, filepath.Ext(filename))
  107. fs := filesystem.GetFileSize(shareEntry.FileRealPath)
  108. shareMeta := filepath.Ext(shareEntry.FileRealPath) + " / " + filesystem.GetFileDisplaySize(fs, 2)
  109. if isDir(shareEntry.FileRealPath) {
  110. fs, fc := filesystem.GetDirctorySize(shareEntry.FileRealPath, false)
  111. shareMeta = strconv.Itoa(fc) + " items / " + filesystem.GetFileDisplaySize(fs, 2)
  112. }
  113. if len([]rune(filename)) > 20 {
  114. //Split into lines
  115. lines := []string{}
  116. for i := 0; i < len([]rune(filenameOnly)); i += 20 {
  117. endPos := int(math.Min(float64(len([]rune(filenameOnly))), float64(i+20)))
  118. lines = append(lines, string([]rune(filenameOnly)[i:endPos]))
  119. }
  120. for j, line := range lines {
  121. pt := freetype.Pt(100, (j+1)*60+int(ctx.PointToFixed(fontSize)>>6))
  122. _, err = ctx.DrawString(line, pt)
  123. if err != nil {
  124. fmt.Println("[share/opg] " + err.Error())
  125. return
  126. }
  127. }
  128. fontSize = 36
  129. ctx.SetFontSize(fontSize)
  130. pt := freetype.Pt(100, (len(lines)+1)*60+int(ctx.PointToFixed(fontSize)>>6))
  131. _, err = ctx.DrawString(shareMeta, pt)
  132. if err != nil {
  133. fmt.Println("[share/opg] " + err.Error())
  134. http.NotFound(w, r)
  135. return
  136. }
  137. } else {
  138. //One liner
  139. pt := freetype.Pt(100, 60+int(ctx.PointToFixed(fontSize)>>6))
  140. _, err = ctx.DrawString(filenameOnly, pt)
  141. if err != nil {
  142. fmt.Println("[share/opg] " + err.Error())
  143. http.NotFound(w, r)
  144. return
  145. }
  146. fontSize = 36
  147. ctx.SetFontSize(fontSize)
  148. pt = freetype.Pt(100, 120+int(ctx.PointToFixed(fontSize)>>6))
  149. _, err = ctx.DrawString(shareMeta, pt)
  150. if err != nil {
  151. fmt.Println("[share/opg] " + err.Error())
  152. http.NotFound(w, r)
  153. return
  154. }
  155. }
  156. //Get thumbnail
  157. ownerinfo, err := s.options.UserHandler.GetUserInfoFromUsername(shareEntry.Owner)
  158. if err != nil {
  159. fmt.Println("[share/opg] " + err.Error())
  160. http.NotFound(w, r)
  161. return
  162. }
  163. fsh, err := ownerinfo.GetFileSystemHandlerFromVirtualPath(shareEntry.FileVirtualPath)
  164. if err != nil {
  165. fmt.Println("[share/opg] " + err.Error())
  166. http.NotFound(w, r)
  167. return
  168. }
  169. rpath, _ := fsh.FileSystemAbstraction.VirtualPathToRealPath(shareEntry.FileVirtualPath, shareEntry.Owner)
  170. cacheFileImagePath, err := metadata.GetCacheFilePath(fsh, rpath)
  171. if err == nil {
  172. //We got a thumbnail for this file. Render it as well
  173. thumbnailFile, err := os.Open(cacheFileImagePath)
  174. if err != nil {
  175. fmt.Println("[share/opg] " + err.Error())
  176. http.NotFound(w, r)
  177. return
  178. }
  179. thumb, _, err := image.Decode(thumbnailFile)
  180. if err != nil {
  181. fmt.Println("[share/opg] " + err.Error())
  182. http.NotFound(w, r)
  183. return
  184. }
  185. resizedThumb := resize.Resize(250, 0, thumb, resize.Lanczos3)
  186. draw.Draw(resultopg, resultopg.Bounds(), resizedThumb, image.Point{-(resultopg.Bounds().Dx() - resizedThumb.Bounds().Dx() - 90), -60}, draw.Over)
  187. } else if isDir(shareEntry.FileRealPath) {
  188. //Is directory but no thumbnail. Use default foldr share thumbnail
  189. thumbnailFile, err := os.Open("./system/share/folder.png")
  190. if err != nil {
  191. fmt.Println("[share/opg] " + err.Error())
  192. http.NotFound(w, r)
  193. return
  194. }
  195. thumb, _, err := image.Decode(thumbnailFile)
  196. if err != nil {
  197. fmt.Println("[share/opg] " + err.Error())
  198. http.NotFound(w, r)
  199. return
  200. }
  201. resizedThumb := resize.Resize(250, 0, thumb, resize.Lanczos3)
  202. draw.Draw(resultopg, resultopg.Bounds(), resizedThumb, image.Point{-(resultopg.Bounds().Dx() - resizedThumb.Bounds().Dx() - 90), -60}, draw.Over)
  203. }
  204. w.Header().Set("Content-Type", "image/jpeg")
  205. jpeg.Encode(w, resultopg, nil)
  206. }
  207. //Main function for handle share. Must be called with http.HandleFunc (No auth)
  208. func (s *Manager) HandleShareAccess(w http.ResponseWriter, r *http.Request) {
  209. //New download method variables
  210. subpathElements := []string{}
  211. directDownload := false
  212. directServe := false
  213. relpath := ""
  214. id, err := mv(r, "id", false)
  215. if err != nil {
  216. //ID is not defined in the URL paramter. New ID defination is based on the subpath content
  217. requestURI := filepath.ToSlash(filepath.Clean(r.URL.Path))
  218. subpathElements = strings.Split(requestURI[1:], "/")
  219. if len(subpathElements) == 2 {
  220. //E.g. /share/{id} => Show the download page
  221. id = subpathElements[1]
  222. //Check if there is missing / at the end. Redirect if true
  223. if r.URL.Path[len(r.URL.Path)-1:] != "/" {
  224. http.Redirect(w, r, r.URL.Path+"/", http.StatusTemporaryRedirect)
  225. return
  226. }
  227. } else if len(subpathElements) >= 3 {
  228. //E.g. /share/download/{uuid} or /share/preview/{uuid}
  229. id = subpathElements[2]
  230. if subpathElements[1] == "download" {
  231. directDownload = true
  232. //Check if this contain a subpath
  233. if len(subpathElements) > 3 {
  234. relpath = strings.Join(subpathElements[3:], "/")
  235. }
  236. } else if subpathElements[1] == "preview" {
  237. directServe = true
  238. } else if len(subpathElements) == 3 {
  239. //Check if the last element is the filename
  240. if strings.Contains(subpathElements[2], ".") {
  241. //Share link contain filename. Redirect to share interface
  242. http.Redirect(w, r, "./", http.StatusTemporaryRedirect)
  243. return
  244. } else {
  245. //Incorrect operation type
  246. w.WriteHeader(http.StatusBadRequest)
  247. w.Header().Set("Content-Type", "text/plain") // this
  248. w.Write([]byte("400 - Operation type not supported: " + subpathElements[1]))
  249. return
  250. }
  251. } else if len(subpathElements) >= 4 {
  252. if subpathElements[1] == "opg" {
  253. //Handle serving opg preview image, usually with
  254. // /share/opg/{req.timestamp}/{uuid}
  255. s.HandleOPGServing(w, r, subpathElements[3])
  256. return
  257. }
  258. //Invalid operation type
  259. w.WriteHeader(http.StatusBadRequest)
  260. w.Header().Set("Content-Type", "text/plain") // this
  261. w.Write([]byte("400 - Operation type not supported: " + subpathElements[1]))
  262. return
  263. }
  264. } else if len(subpathElements) == 1 {
  265. //ID is missing. Serve the id input page
  266. content, err := ioutil.ReadFile("system/share/index.html")
  267. if err != nil {
  268. //Handling index not found. Is server updated correctly?
  269. w.WriteHeader(http.StatusInternalServerError)
  270. w.Write([]byte("500 - Internal Server Error"))
  271. return
  272. }
  273. t := fasttemplate.New(string(content), "{{", "}}")
  274. s := t.ExecuteString(map[string]interface{}{
  275. "hostname": s.options.HostName,
  276. })
  277. w.Write([]byte(s))
  278. return
  279. } else {
  280. http.NotFound(w, r)
  281. return
  282. }
  283. } else {
  284. //Parse and redirect to new share path
  285. download, _ := mv(r, "download", false)
  286. if download == "true" {
  287. directDownload = true
  288. }
  289. serve, _ := mv(r, "serve", false)
  290. if serve == "true" {
  291. directServe = true
  292. }
  293. relpath, _ = mv(r, "rel", false)
  294. redirectURL := "./" + id + "/"
  295. if directDownload == true {
  296. redirectURL = "./download/" + id + "/"
  297. }
  298. http.Redirect(w, r, redirectURL, http.StatusTemporaryRedirect)
  299. return
  300. }
  301. //Check if id exists
  302. val, ok := s.options.ShareEntryTable.UrlToFileMap.Load(id)
  303. if ok {
  304. //Parse the option structure
  305. shareOption := val.(*shareEntry.ShareOption)
  306. //Check for permission
  307. if shareOption.Permission == "anyone" {
  308. //OK to proceed
  309. } else if shareOption.Permission == "signedin" {
  310. if !s.options.AuthAgent.CheckAuth(r) {
  311. //Redirect to login page
  312. if directDownload || directServe {
  313. w.WriteHeader(http.StatusUnauthorized)
  314. w.Write([]byte("401 - Unauthorized"))
  315. } else {
  316. http.Redirect(w, r, common.ConstructRelativePathFromRequestURL(r.RequestURI, "login.system")+"?redirect=/share/"+id, 307)
  317. }
  318. return
  319. } else {
  320. //Ok to proccedd
  321. }
  322. } else if shareOption.Permission == "samegroup" {
  323. thisuserinfo, err := s.options.UserHandler.GetUserInfoFromRequest(w, r)
  324. if err != nil {
  325. if directDownload || directServe {
  326. w.WriteHeader(http.StatusUnauthorized)
  327. w.Write([]byte("401 - Unauthorized"))
  328. } else {
  329. http.Redirect(w, r, common.ConstructRelativePathFromRequestURL(r.RequestURI, "login.system")+"?redirect=/share/"+id, 307)
  330. }
  331. return
  332. }
  333. //Check if all the user groups are inside the share owner groups
  334. valid := true
  335. thisUsersGroupByName := []string{}
  336. for _, pg := range thisuserinfo.PermissionGroup {
  337. thisUsersGroupByName = append(thisUsersGroupByName, pg.Name)
  338. }
  339. for _, allowedpg := range shareOption.Accessibles {
  340. if inArray(thisUsersGroupByName, allowedpg) {
  341. //This required group is inside this user's group. OK
  342. } else {
  343. //This required group is not inside user's group. Reject
  344. valid = false
  345. }
  346. }
  347. if !valid {
  348. //Serve permission denied page
  349. if directDownload || directServe {
  350. w.WriteHeader(http.StatusForbidden)
  351. w.Write([]byte("401 - Forbidden"))
  352. } else {
  353. ServePermissionDeniedPage(w)
  354. }
  355. return
  356. }
  357. } else if shareOption.Permission == "users" {
  358. thisuserinfo, err := s.options.UserHandler.GetUserInfoFromRequest(w, r)
  359. if err != nil {
  360. //User not logged in. Redirect to login page
  361. if directDownload || directServe {
  362. w.WriteHeader(http.StatusUnauthorized)
  363. w.Write([]byte("401 - Unauthorized"))
  364. } else {
  365. http.Redirect(w, r, common.ConstructRelativePathFromRequestURL(r.RequestURI, "login.system")+"?redirect=/share/"+id, 307)
  366. }
  367. return
  368. }
  369. //Check if username in the allowed user list
  370. if !inArray(shareOption.Accessibles, thisuserinfo.Username) && shareOption.Owner != thisuserinfo.Username {
  371. //Serve permission denied page
  372. if directDownload || directServe {
  373. w.WriteHeader(http.StatusForbidden)
  374. w.Write([]byte("401 - Forbidden"))
  375. } else {
  376. ServePermissionDeniedPage(w)
  377. }
  378. return
  379. }
  380. } else if shareOption.Permission == "groups" {
  381. thisuserinfo, err := s.options.UserHandler.GetUserInfoFromRequest(w, r)
  382. if err != nil {
  383. //User not logged in. Redirect to login page
  384. if directDownload || directServe {
  385. w.WriteHeader(http.StatusUnauthorized)
  386. w.Write([]byte("401 - Unauthorized"))
  387. } else {
  388. http.Redirect(w, r, common.ConstructRelativePathFromRequestURL(r.RequestURI, "login.system")+"?redirect=/share/"+id, 307)
  389. }
  390. return
  391. }
  392. allowAccess := false
  393. thisUsersGroupByName := []string{}
  394. for _, pg := range thisuserinfo.PermissionGroup {
  395. thisUsersGroupByName = append(thisUsersGroupByName, pg.Name)
  396. }
  397. for _, thisUserPg := range thisUsersGroupByName {
  398. if inArray(shareOption.Accessibles, thisUserPg) {
  399. allowAccess = true
  400. }
  401. }
  402. if !allowAccess {
  403. //Serve permission denied page
  404. if directDownload || directServe {
  405. w.WriteHeader(http.StatusForbidden)
  406. w.Write([]byte("401 - Forbidden"))
  407. } else {
  408. ServePermissionDeniedPage(w)
  409. }
  410. return
  411. }
  412. } else {
  413. //Unsupported mode. Show notfound
  414. http.NotFound(w, r)
  415. return
  416. }
  417. //Resolve the fsh from the entry
  418. owner, err := s.options.UserHandler.GetUserInfoFromUsername(shareOption.Owner)
  419. if err != nil {
  420. w.WriteHeader(http.StatusForbidden)
  421. w.Write([]byte("401 - Share account not exists"))
  422. return
  423. }
  424. targetFsh, err := owner.GetFileSystemHandlerFromVirtualPath(shareOption.FileVirtualPath)
  425. if err != nil {
  426. w.WriteHeader(http.StatusInternalServerError)
  427. w.Write([]byte("500 - Unable to load Shared File"))
  428. return
  429. }
  430. targetFshAbs := targetFsh.FileSystemAbstraction
  431. fileRuntimeAbsPath, _ := targetFshAbs.VirtualPathToRealPath(shareOption.FileVirtualPath, owner.Username)
  432. if !targetFshAbs.FileExists(fileRuntimeAbsPath) {
  433. http.NotFound(w, r)
  434. return
  435. }
  436. //Serve the download page
  437. if targetFshAbs.IsDir(fileRuntimeAbsPath) {
  438. //This share is a folder
  439. type File struct {
  440. Filename string
  441. RelPath string
  442. Filesize string
  443. IsDir bool
  444. }
  445. if directDownload {
  446. if relpath != "" {
  447. //User specified a specific file within the directory. Escape the relpath
  448. targetFilepath := filepath.Join(fileRuntimeAbsPath, relpath)
  449. //Check if file exists
  450. if !targetFshAbs.FileExists(targetFilepath) {
  451. http.NotFound(w, r)
  452. return
  453. }
  454. //Validate the absolute path to prevent path escape
  455. reqPath := filepath.ToSlash(filepath.Clean(targetFilepath))
  456. rootPath, _ := targetFshAbs.VirtualPathToRealPath(shareOption.FileVirtualPath, shareOption.Owner)
  457. if !strings.HasPrefix(reqPath, rootPath) {
  458. //Directory escape detected
  459. w.WriteHeader(http.StatusBadRequest)
  460. w.Write([]byte("400 - Bad Request: Invalid relative path"))
  461. return
  462. }
  463. //Serve the target file
  464. w.Header().Set("Content-Disposition", "attachment; filename*=UTF-8''"+strings.ReplaceAll(url.QueryEscape(filepath.Base(targetFilepath)), "+", "%20"))
  465. w.Header().Set("Content-Type", r.Header.Get("Content-Type"))
  466. //http.ServeFile(w, r, targetFilepath)
  467. if targetFsh.RequireBuffer {
  468. f, err := targetFshAbs.ReadStream(targetFilepath)
  469. if err != nil {
  470. w.WriteHeader(http.StatusInternalServerError)
  471. w.Write([]byte("500 - Internal Server Error: " + err.Error()))
  472. return
  473. }
  474. defer f.Close()
  475. io.Copy(w, f)
  476. } else {
  477. f, err := targetFshAbs.Open(targetFilepath)
  478. if err != nil {
  479. w.WriteHeader(http.StatusInternalServerError)
  480. w.Write([]byte("500 - Internal Server Error: " + err.Error()))
  481. return
  482. }
  483. defer f.Close()
  484. fi, _ := f.Stat()
  485. http.ServeContent(w, r, arozfs.Base(targetFilepath), fi.ModTime(), f)
  486. }
  487. } else {
  488. //Download this folder as zip
  489. //Create a zip using ArOZ Zipper, tmp zip files are located under tmp/share-cache/*.zip
  490. tmpFolder := s.options.TmpFolder
  491. tmpFolder = filepath.Join(tmpFolder, "share-cache")
  492. os.MkdirAll(tmpFolder, 0755)
  493. targetZipFilename := filepath.Join(tmpFolder, filepath.Base(fileRuntimeAbsPath)) + ".zip"
  494. //Check if the target fs require buffer
  495. zippingSource := shareOption.FileRealPath
  496. localBuff := ""
  497. zippingSourceFsh := targetFsh
  498. if targetFsh.RequireBuffer {
  499. //Buffer all the required files for zipping
  500. localBuff = filepath.Join(tmpFolder, uuid.NewV4().String(), filepath.Base(fileRuntimeAbsPath))
  501. os.MkdirAll(localBuff, 0755)
  502. //Buffer all files into tmp folder
  503. targetFshAbs.Walk(fileRuntimeAbsPath, func(path string, info fs.FileInfo, err error) error {
  504. relPath := strings.TrimPrefix(filepath.ToSlash(path), filepath.ToSlash(fileRuntimeAbsPath))
  505. localPath := filepath.Join(localBuff, relPath)
  506. if info.IsDir() {
  507. os.MkdirAll(localPath, 0755)
  508. } else {
  509. f, err := targetFshAbs.ReadStream(path)
  510. if err != nil {
  511. log.Println("[Share] Buffer and zip download operation failed: ", err)
  512. }
  513. defer f.Close()
  514. dest, err := os.OpenFile(localPath, os.O_CREATE|os.O_WRONLY, 0775)
  515. if err != nil {
  516. log.Println("[Share] Buffer and zip download operation failed: ", err)
  517. }
  518. defer dest.Close()
  519. _, err = io.Copy(dest, f)
  520. if err != nil {
  521. log.Println("[Share] Buffer and zip download operation failed: ", err)
  522. }
  523. }
  524. return nil
  525. })
  526. zippingSource = localBuff
  527. zippingSourceFsh = nil
  528. }
  529. //Build a filelist
  530. err := filesystem.ArozZipFile([]*filesystem.FileSystemHandler{zippingSourceFsh}, []string{zippingSource}, nil, targetZipFilename, false)
  531. if err != nil {
  532. //Failed to create zip file
  533. w.WriteHeader(http.StatusInternalServerError)
  534. w.Write([]byte("500 - Internal Server Error: Zip file creation failed"))
  535. log.Println("Failed to create zip file for share download: " + err.Error())
  536. return
  537. }
  538. //Serve thje zip file
  539. w.Header().Set("Content-Disposition", "attachment; filename*=UTF-8''"+strings.ReplaceAll(url.QueryEscape(filepath.Base(shareOption.FileRealPath)), "+", "%20")+".zip")
  540. w.Header().Set("Content-Type", r.Header.Get("Content-Type"))
  541. http.ServeFile(w, r, targetZipFilename)
  542. //Remove the buffer file if exists
  543. if targetFsh.RequireBuffer {
  544. os.RemoveAll(filepath.Dir(localBuff))
  545. }
  546. }
  547. } else if directServe {
  548. //Folder provide no direct serve method.
  549. w.WriteHeader(http.StatusBadRequest)
  550. w.Write([]byte("400 - Cannot preview folder type shares"))
  551. return
  552. } else {
  553. //Show download page. Do not allow serving
  554. content, err := ioutil.ReadFile("./system/share/downloadPageFolder.html")
  555. if err != nil {
  556. http.NotFound(w, r)
  557. return
  558. }
  559. //Get file size
  560. fsize, fcount := targetFsh.GetDirctorySizeFromRealPath(fileRuntimeAbsPath, false)
  561. //Build the tree list of the folder
  562. treeList := map[string][]File{}
  563. err = targetFshAbs.Walk(filepath.Clean(fileRuntimeAbsPath), func(file string, info os.FileInfo, err error) error {
  564. if err != nil {
  565. //If error skip this
  566. return nil
  567. }
  568. if filepath.Base(file)[:1] != "." {
  569. fileSize := targetFshAbs.GetFileSize(file)
  570. if targetFshAbs.IsDir(file) {
  571. fileSize, _ = targetFsh.GetDirctorySizeFromRealPath(file, false)
  572. }
  573. relPath := strings.TrimPrefix(filepath.ToSlash(file), filepath.ToSlash(fileRuntimeAbsPath))
  574. relDir := strings.TrimPrefix(filepath.ToSlash(filepath.Dir(file)), filepath.ToSlash(fileRuntimeAbsPath))
  575. if relPath == "." || relPath == "" {
  576. //The root file object. Skip this
  577. return nil
  578. }
  579. if relDir == "" {
  580. relDir = "."
  581. }
  582. treeList[relDir] = append(treeList[relDir], File{
  583. Filename: filepath.Base(file),
  584. RelPath: filepath.ToSlash(relPath),
  585. Filesize: filesystem.GetFileDisplaySize(fileSize, 2),
  586. IsDir: targetFshAbs.IsDir(file),
  587. })
  588. }
  589. return nil
  590. })
  591. if err != nil {
  592. w.WriteHeader(http.StatusInternalServerError)
  593. w.Write([]byte("500 - Internal Server Error"))
  594. return
  595. }
  596. tl, _ := json.Marshal(treeList)
  597. //Get modification time
  598. fmodtime, _ := targetFshAbs.GetModTime(fileRuntimeAbsPath)
  599. timeString := time.Unix(fmodtime, 0).Format("02-01-2006 15:04:05")
  600. t := fasttemplate.New(string(content), "{{", "}}")
  601. s := t.ExecuteString(map[string]interface{}{
  602. "hostname": s.options.HostName,
  603. "host": r.Host,
  604. "reqid": id,
  605. "mime": "application/x-directory",
  606. "size": filesystem.GetFileDisplaySize(fsize, 2),
  607. "filecount": strconv.Itoa(fcount),
  608. "modtime": timeString,
  609. "downloadurl": "../../share/download/" + id,
  610. "filename": filepath.Base(fileRuntimeAbsPath),
  611. "reqtime": strconv.Itoa(int(time.Now().Unix())),
  612. "requri": "//" + r.Host + r.URL.Path,
  613. "opg_image": "/share/opg/" + strconv.Itoa(int(time.Now().Unix())) + "/" + id,
  614. "treelist": tl,
  615. "downloaduuid": id,
  616. })
  617. w.Write([]byte(s))
  618. return
  619. }
  620. } else {
  621. //This share is a file
  622. contentType := mime.TypeByExtension(filepath.Ext(fileRuntimeAbsPath))
  623. if directDownload {
  624. //Serve the file directly
  625. w.Header().Set("Content-Disposition", "attachment; filename=\""+filepath.Base(shareOption.FileVirtualPath)+"\"")
  626. w.Header().Set("Content-Type", contentType)
  627. w.Header().Set("Content-Length", strconv.Itoa(int(targetFshAbs.GetFileSize(fileRuntimeAbsPath))))
  628. if filesystem.FileExists(fileRuntimeAbsPath) {
  629. //This file exists in local file system. Serve it directly
  630. http.ServeFile(w, r, fileRuntimeAbsPath)
  631. } else {
  632. if targetFsh.RequireBuffer {
  633. f, err := targetFshAbs.ReadStream(fileRuntimeAbsPath)
  634. if err != nil {
  635. w.WriteHeader(http.StatusInternalServerError)
  636. w.Write([]byte("500 - Internal Server Error: " + err.Error()))
  637. return
  638. }
  639. defer f.Close()
  640. io.Copy(w, f)
  641. } else {
  642. f, err := targetFshAbs.Open(fileRuntimeAbsPath)
  643. if err != nil {
  644. w.WriteHeader(http.StatusInternalServerError)
  645. w.Write([]byte("500 - Internal Server Error: " + err.Error()))
  646. return
  647. }
  648. defer f.Close()
  649. fi, _ := f.Stat()
  650. http.ServeContent(w, r, arozfs.Base(fileRuntimeAbsPath), fi.ModTime(), f)
  651. }
  652. }
  653. } else if directServe {
  654. w.Header().Set("Access-Control-Allow-Origin", "*")
  655. w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
  656. w.Header().Set("Content-Type", contentType)
  657. if targetFsh.RequireBuffer {
  658. f, err := targetFshAbs.ReadStream(fileRuntimeAbsPath)
  659. if err != nil {
  660. w.WriteHeader(http.StatusInternalServerError)
  661. w.Write([]byte("500 - Internal Server Error: " + err.Error()))
  662. return
  663. }
  664. defer f.Close()
  665. io.Copy(w, f)
  666. } else {
  667. f, err := targetFshAbs.Open(fileRuntimeAbsPath)
  668. if err != nil {
  669. w.WriteHeader(http.StatusInternalServerError)
  670. w.Write([]byte("500 - Internal Server Error: " + err.Error()))
  671. return
  672. }
  673. defer f.Close()
  674. fi, _ := f.Stat()
  675. http.ServeContent(w, r, arozfs.Base(fileRuntimeAbsPath), fi.ModTime(), f)
  676. }
  677. } else {
  678. //Serve the download page
  679. content, err := ioutil.ReadFile("./system/share/downloadPage.html")
  680. if err != nil {
  681. http.NotFound(w, r)
  682. return
  683. }
  684. //Get file mime type
  685. mime, ext, err := filesystem.GetMime(fileRuntimeAbsPath)
  686. if err != nil {
  687. mime = "Unknown"
  688. }
  689. //Load the preview template
  690. templateRoot := "./system/share/"
  691. previewTemplate := ""
  692. if ext == ".mp4" || ext == ".webm" {
  693. previewTemplate = filepath.Join(templateRoot, "video.html")
  694. } else if ext == ".mp3" || ext == ".wav" || ext == ".flac" || ext == ".ogg" {
  695. previewTemplate = filepath.Join(templateRoot, "audio.html")
  696. } else if ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".webp" {
  697. previewTemplate = filepath.Join(templateRoot, "image.html")
  698. } else if ext == ".pdf" {
  699. previewTemplate = filepath.Join(templateRoot, "iframe.html")
  700. } else {
  701. //Format do not support preview. Use the default.html
  702. previewTemplate = filepath.Join(templateRoot, "default.html")
  703. }
  704. tp, err := ioutil.ReadFile(previewTemplate)
  705. if err != nil {
  706. tp = []byte("")
  707. }
  708. //Merge two templates
  709. content = []byte(strings.ReplaceAll(string(content), "{{previewer}}", string(tp)))
  710. //Get file size
  711. fsize := targetFshAbs.GetFileSize(fileRuntimeAbsPath)
  712. //Get modification time
  713. fmodtime, _ := targetFshAbs.GetModTime(fileRuntimeAbsPath)
  714. timeString := time.Unix(fmodtime, 0).Format("02-01-2006 15:04:05")
  715. //Check if ext match with filepath ext
  716. displayExt := ext
  717. if ext != filepath.Ext(fileRuntimeAbsPath) {
  718. displayExt = filepath.Ext(fileRuntimeAbsPath) + " (" + ext + ")"
  719. }
  720. t := fasttemplate.New(string(content), "{{", "}}")
  721. s := t.ExecuteString(map[string]interface{}{
  722. "hostname": s.options.HostName,
  723. "host": r.Host,
  724. "reqid": id,
  725. "requri": "//" + r.Host + r.URL.Path,
  726. "mime": mime,
  727. "ext": displayExt,
  728. "size": filesystem.GetFileDisplaySize(fsize, 2),
  729. "modtime": timeString,
  730. "downloadurl": "/share/download/" + id + "/" + filepath.Base(fileRuntimeAbsPath),
  731. "preview_url": "/share/preview/" + id + "/",
  732. "filename": filepath.Base(fileRuntimeAbsPath),
  733. "opg_image": "/share/opg/" + strconv.Itoa(int(time.Now().Unix())) + "/" + id,
  734. "reqtime": strconv.Itoa(int(time.Now().Unix())),
  735. })
  736. w.Write([]byte(s))
  737. return
  738. }
  739. }
  740. } else {
  741. //This share not exists
  742. if directDownload {
  743. //Send 404 header
  744. http.NotFound(w, r)
  745. return
  746. } else {
  747. //Send not found page
  748. content, err := ioutil.ReadFile("./system/share/notfound.html")
  749. if err != nil {
  750. http.NotFound(w, r)
  751. return
  752. }
  753. t := fasttemplate.New(string(content), "{{", "}}")
  754. s := t.ExecuteString(map[string]interface{}{
  755. "hostname": s.options.HostName,
  756. "reqid": id,
  757. "reqtime": strconv.Itoa(int(time.Now().Unix())),
  758. })
  759. w.Write([]byte(s))
  760. return
  761. }
  762. }
  763. }
  764. //Check if a file is shared
  765. func (s *Manager) HandleShareCheck(w http.ResponseWriter, r *http.Request) {
  766. //Get the vpath from paramters
  767. vpath, err := mv(r, "path", true)
  768. if err != nil {
  769. sendErrorResponse(w, "Invalid path given")
  770. return
  771. }
  772. //Get userinfo
  773. userinfo, err := s.options.UserHandler.GetUserInfoFromRequest(w, r)
  774. if err != nil {
  775. sendErrorResponse(w, "User not logged in")
  776. return
  777. }
  778. fsh, _ := userinfo.GetFileSystemHandlerFromVirtualPath(vpath)
  779. pathHash, err := shareEntry.GetPathHash(fsh, vpath, userinfo.Username)
  780. if err != nil {
  781. sendErrorResponse(w, "Unable to get share from given path")
  782. return
  783. }
  784. type Result struct {
  785. IsShared bool
  786. ShareUUID *shareEntry.ShareOption
  787. }
  788. //Check if share exists
  789. shareExists := s.options.ShareEntryTable.FileIsShared(pathHash)
  790. if !shareExists {
  791. //Share not exists
  792. js, _ := json.Marshal(Result{
  793. IsShared: false,
  794. ShareUUID: &shareEntry.ShareOption{},
  795. })
  796. sendJSONResponse(w, string(js))
  797. } else {
  798. //Share exists
  799. thisSharedInfo := s.options.ShareEntryTable.GetShareObjectFromPathHash(pathHash)
  800. js, _ := json.Marshal(Result{
  801. IsShared: true,
  802. ShareUUID: thisSharedInfo,
  803. })
  804. sendJSONResponse(w, string(js))
  805. }
  806. }
  807. //Create new share from the given path
  808. func (s *Manager) HandleCreateNewShare(w http.ResponseWriter, r *http.Request) {
  809. //Get the vpath from paramters
  810. vpath, err := mv(r, "path", true)
  811. if err != nil {
  812. sendErrorResponse(w, "Invalid path given")
  813. return
  814. }
  815. //Get userinfo
  816. userinfo, err := s.options.UserHandler.GetUserInfoFromRequest(w, r)
  817. if err != nil {
  818. sendErrorResponse(w, "User not logged in")
  819. return
  820. }
  821. //Get the target fsh that this vpath come from
  822. vpathSourceFsh := userinfo.GetRootFSHFromVpathInUserScope(vpath)
  823. if vpathSourceFsh == nil {
  824. sendErrorResponse(w, "Invalid vpath given")
  825. return
  826. }
  827. share, err := s.CreateNewShare(userinfo, vpathSourceFsh, vpath)
  828. if err != nil {
  829. sendErrorResponse(w, err.Error())
  830. return
  831. }
  832. js, _ := json.Marshal(share)
  833. sendJSONResponse(w, string(js))
  834. }
  835. // Handle Share Edit.
  836. // For allowing groups / users, use the following syntax
  837. // groups:group1,group2,group3
  838. // users:user1,user2,user3
  839. // For basic modes, use the following keywords
  840. // anyone / signedin / samegroup
  841. // anyone: Anyone who has the link
  842. // signedin: Anyone logged in to this system
  843. // samegroup: The requesting user has the same (or more) user group as the share owner
  844. func (s *Manager) HandleEditShare(w http.ResponseWriter, r *http.Request) {
  845. userinfo, err := s.options.UserHandler.GetUserInfoFromRequest(w, r)
  846. if err != nil {
  847. sendErrorResponse(w, "User not logged in")
  848. return
  849. }
  850. uuid, err := mv(r, "uuid", true)
  851. if err != nil {
  852. sendErrorResponse(w, "Invalid path given")
  853. return
  854. }
  855. shareMode, _ := mv(r, "mode", true)
  856. if shareMode == "" {
  857. shareMode = "signedin"
  858. }
  859. //Check if share exists
  860. so := s.options.ShareEntryTable.GetShareObjectFromUUID(uuid)
  861. if so == nil {
  862. //This share url not exists
  863. sendErrorResponse(w, "Share UUID not exists")
  864. return
  865. }
  866. //Check if the user has permission to edit this share
  867. if !s.CanModifyShareEntry(userinfo, so.FileVirtualPath) {
  868. common.SendErrorResponse(w, "Permission Denied")
  869. return
  870. }
  871. //Validate and extract the storage mode
  872. ok, sharetype, settings := validateShareModes(shareMode)
  873. if !ok {
  874. sendErrorResponse(w, "Invalid share setting")
  875. return
  876. }
  877. //Analysis the sharetype
  878. if sharetype == "anyone" || sharetype == "signedin" || sharetype == "samegroup" {
  879. //Basic types.
  880. so.Permission = sharetype
  881. if sharetype == "samegroup" {
  882. //Write user groups into accessible (Must be all match inorder to allow access)
  883. userpg := []string{}
  884. for _, pg := range userinfo.PermissionGroup {
  885. userpg = append(userpg, pg.Name)
  886. }
  887. so.Accessibles = userpg
  888. }
  889. //Write changes to database
  890. s.options.ShareEntryTable.Database.Write("share", uuid, so)
  891. } else if sharetype == "groups" || sharetype == "users" {
  892. //Username or group is listed = ok
  893. so.Permission = sharetype
  894. so.Accessibles = settings
  895. //Write changes to database
  896. s.options.ShareEntryTable.Database.Write("share", uuid, so)
  897. }
  898. sendOK(w)
  899. }
  900. func (s *Manager) HandleDeleteShare(w http.ResponseWriter, r *http.Request) {
  901. //Get userinfo
  902. userinfo, err := s.options.UserHandler.GetUserInfoFromRequest(w, r)
  903. if err != nil {
  904. sendErrorResponse(w, "User not logged in")
  905. return
  906. }
  907. //Get the vpath from paramters
  908. uuid, err := mv(r, "uuid", true)
  909. if err != nil {
  910. //Try to get it from vpath
  911. vpath, err := mv(r, "vpath", true)
  912. if err != nil {
  913. sendErrorResponse(w, "Invalid uuid or vpath given")
  914. return
  915. }
  916. targetSa := s.GetShareObjectFromUserAndVpath(userinfo, vpath)
  917. if targetSa == nil {
  918. sendErrorResponse(w, "Invalid uuid or vpath given")
  919. return
  920. }
  921. uuid = targetSa.UUID
  922. }
  923. //Delete the share setting
  924. err = s.DeleteShareByUUID(userinfo, uuid)
  925. if err != nil {
  926. sendErrorResponse(w, err.Error())
  927. } else {
  928. sendOK(w)
  929. }
  930. }
  931. func (s *Manager) HandleListAllShares(w http.ResponseWriter, r *http.Request) {
  932. userinfo, err := s.options.UserHandler.GetUserInfoFromRequest(w, r)
  933. if err != nil {
  934. common.SendErrorResponse(w, "User not logged in")
  935. return
  936. }
  937. fshId, _ := common.Mv(r, "fsh", false)
  938. results := []*shareEntry.ShareOption{}
  939. if fshId == "" {
  940. //List all
  941. allFsh := userinfo.GetAllFileSystemHandler()
  942. for _, thisFsh := range allFsh {
  943. allShares := s.ListAllShareByFshId(thisFsh.UUID, userinfo)
  944. for _, thisShare := range allShares {
  945. if s.ShareIsValid(thisShare) {
  946. results = append(results, thisShare)
  947. }
  948. }
  949. }
  950. } else {
  951. //List fsh only
  952. targetFsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(fshId)
  953. if err != nil {
  954. common.SendErrorResponse(w, err.Error())
  955. return
  956. }
  957. sharesInThisFsh := s.ListAllShareByFshId(targetFsh.UUID, userinfo)
  958. for _, thisShare := range sharesInThisFsh {
  959. if s.ShareIsValid(thisShare) {
  960. results = append(results, thisShare)
  961. }
  962. }
  963. }
  964. //Reduce the data
  965. type Share struct {
  966. UUID string
  967. FileVirtualPath string
  968. Owner string
  969. Permission string
  970. IsFolder bool
  971. IsOwnerOfShare bool
  972. CanAccess bool
  973. CanOpenInFileManager bool
  974. CanDelete bool
  975. }
  976. reducedResult := []*Share{}
  977. for _, result := range results {
  978. permissionText := result.Permission
  979. if result.Permission == "groups" || result.Permission == "users" {
  980. permissionText = permissionText + " (" + strings.Join(result.Accessibles, ", ") + ")"
  981. }
  982. thisShareInfo := Share{
  983. UUID: result.UUID,
  984. FileVirtualPath: result.FileVirtualPath,
  985. Owner: result.Owner,
  986. Permission: permissionText,
  987. IsFolder: result.IsFolder,
  988. IsOwnerOfShare: userinfo.Username == result.Owner,
  989. CanAccess: result.IsAccessibleBy(userinfo.Username, userinfo.GetUserPermissionGroupNames()),
  990. CanOpenInFileManager: s.UserCanOpenShareInFileManager(result, userinfo),
  991. CanDelete: s.CanModifyShareEntry(userinfo, result.FileVirtualPath),
  992. }
  993. reducedResult = append(reducedResult, &thisShareInfo)
  994. }
  995. js, _ := json.Marshal(reducedResult)
  996. common.SendJSONResponse(w, string(js))
  997. }
  998. /*
  999. Check if the user can open the share in File Manager
  1000. There are two conditions where the user can open the file in file manager
  1001. 1. If the user is the owner of the file
  1002. 2. If the user is NOT the owner of the file but the target fsh is public accessible and in user's fsh list
  1003. */
  1004. func (s *Manager) UserCanOpenShareInFileManager(share *shareEntry.ShareOption, userinfo *user.User) bool {
  1005. if share.Owner == userinfo.Username {
  1006. return true
  1007. }
  1008. fsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(share.FileVirtualPath)
  1009. if err != nil {
  1010. //User do not have permission to access this fsh
  1011. return false
  1012. }
  1013. rpath, _ := fsh.FileSystemAbstraction.VirtualPathToRealPath(share.FileVirtualPath, userinfo.Username)
  1014. if fsh.Hierarchy == "public" && fsh.FileSystemAbstraction.FileExists(rpath) {
  1015. return true
  1016. }
  1017. return false
  1018. }
  1019. //Craete a new file or folder share
  1020. func (s *Manager) CreateNewShare(userinfo *user.User, srcFsh *filesystem.FileSystemHandler, vpath string) (*shareEntry.ShareOption, error) {
  1021. //Translate the vpath to realpath
  1022. return s.options.ShareEntryTable.CreateNewShare(srcFsh, vpath, userinfo.Username, userinfo.GetUserPermissionGroupNames())
  1023. }
  1024. func ServePermissionDeniedPage(w http.ResponseWriter) {
  1025. w.WriteHeader(http.StatusForbidden)
  1026. pageContent := []byte("Permissioned Denied")
  1027. if fileExists("system/share/permissionDenied.html") {
  1028. content, err := ioutil.ReadFile("system/share/permissionDenied.html")
  1029. if err == nil {
  1030. pageContent = content
  1031. }
  1032. }
  1033. w.Write([]byte(pageContent))
  1034. }
  1035. /*
  1036. Validate Share Mode string
  1037. will return
  1038. 1. bool => Is valid
  1039. 2. permission type: {basic / groups / users}
  1040. 3. mode string
  1041. */
  1042. func validateShareModes(mode string) (bool, string, []string) {
  1043. // user:a,b,c,d
  1044. validModes := []string{"anyone", "signedin", "samegroup"}
  1045. if inArray(validModes, mode) {
  1046. //Standard modes
  1047. return true, mode, []string{}
  1048. } else if len(mode) > 7 && mode[:7] == "groups:" {
  1049. //Handle custom group case like groups:a,b,c,d
  1050. groupList := mode[7:]
  1051. if len(groupList) > 0 {
  1052. groups := strings.Split(groupList, ",")
  1053. return true, "groups", groups
  1054. } else {
  1055. //Invalid configuration
  1056. return false, "groups", []string{}
  1057. }
  1058. } else if len(mode) > 6 && mode[:6] == "users:" {
  1059. //Handle custom usersname like users:a,b,c,d
  1060. userList := mode[6:]
  1061. if len(userList) > 0 {
  1062. users := strings.Split(userList, ",")
  1063. return true, "users", users
  1064. } else {
  1065. //Invalid configuration
  1066. return false, "users", []string{}
  1067. }
  1068. }
  1069. return false, "", []string{}
  1070. }
  1071. func (s *Manager) ListAllShareByFshId(fshId string, userinfo *user.User) []*shareEntry.ShareOption {
  1072. results := []*shareEntry.ShareOption{}
  1073. s.options.ShareEntryTable.FileToUrlMap.Range(func(k, v interface{}) bool {
  1074. thisShareOption := v.(*shareEntry.ShareOption)
  1075. if (!userinfo.IsAdmin() && thisShareOption.IsAccessibleBy(userinfo.Username, userinfo.GetUserPermissionGroupNames())) || userinfo.IsAdmin() {
  1076. id, _, _ := filesystem.GetIDFromVirtualPath(thisShareOption.FileVirtualPath)
  1077. if id == fshId {
  1078. results = append(results, thisShareOption)
  1079. }
  1080. }
  1081. return true
  1082. })
  1083. sort.Slice(results, func(i, j int) bool {
  1084. return results[i].UUID < results[j].UUID
  1085. })
  1086. return results
  1087. }
  1088. func (s *Manager) ShareIsValid(thisShareOption *shareEntry.ShareOption) bool {
  1089. vpath := thisShareOption.FileVirtualPath
  1090. userinfo, _ := s.options.UserHandler.GetUserInfoFromUsername(thisShareOption.Owner)
  1091. fsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(vpath)
  1092. if err != nil {
  1093. return false
  1094. }
  1095. fshAbs := fsh.FileSystemAbstraction
  1096. rpath, _ := fshAbs.VirtualPathToRealPath(vpath, userinfo.Username)
  1097. if !fshAbs.FileExists(rpath) {
  1098. return false
  1099. }
  1100. return true
  1101. }
  1102. func (s *Manager) GetPathHashFromShare(thisShareOption *shareEntry.ShareOption) (string, error) {
  1103. vpath := thisShareOption.FileVirtualPath
  1104. userinfo, _ := s.options.UserHandler.GetUserInfoFromUsername(thisShareOption.Owner)
  1105. fsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(vpath)
  1106. if err != nil {
  1107. return "", err
  1108. }
  1109. return shareEntry.GetPathHash(fsh, vpath, userinfo.Username)
  1110. }
  1111. //Check and clear shares that its pointinf files no longe exists
  1112. func (s *Manager) ValidateAndClearShares() {
  1113. //Iterate through all shares within the system
  1114. s.options.ShareEntryTable.FileToUrlMap.Range(func(k, v interface{}) bool {
  1115. thisShareOption := v.(*shareEntry.ShareOption)
  1116. pathHash, err := s.GetPathHashFromShare(thisShareOption)
  1117. if err != nil {
  1118. //Unable to resolve path hash. Filesystem handler is gone?
  1119. //s.options.ShareEntryTable.RemoveShareByUUID(thisShareOption.UUID)
  1120. return true
  1121. }
  1122. if !s.ShareIsValid(thisShareOption) {
  1123. //This share source file don't exists anymore. Remove it
  1124. err = s.options.ShareEntryTable.RemoveShareByPathHash(pathHash)
  1125. if err != nil {
  1126. log.Println("[Share] Failed to remove share", err)
  1127. }
  1128. log.Println("[Share] Removing share to file: " + thisShareOption.FileRealPath + " as it no longer exists")
  1129. }
  1130. return true
  1131. })
  1132. }
  1133. //Check if the user has the permission to modify this share entry
  1134. func (s *Manager) CanModifyShareEntry(userinfo *user.User, vpath string) bool {
  1135. shareEntry := s.GetShareObjectFromUserAndVpath(userinfo, vpath)
  1136. if shareEntry == nil {
  1137. //Share entry not found
  1138. return false
  1139. }
  1140. //Check if the user is the share owner or the user is admin
  1141. if userinfo.IsAdmin() {
  1142. return true
  1143. } else if userinfo.Username == shareEntry.Owner {
  1144. return true
  1145. }
  1146. //Public fsh where the user and owner both can access
  1147. fsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(vpath)
  1148. if err != nil {
  1149. return false
  1150. }
  1151. rpath, _ := fsh.FileSystemAbstraction.VirtualPathToRealPath(vpath, userinfo.Username)
  1152. if userinfo.CanWrite(vpath) && fsh.Hierarchy == "public" && fsh.FileSystemAbstraction.FileExists(rpath) {
  1153. return true
  1154. }
  1155. return false
  1156. }
  1157. func (s *Manager) DeleteShareByVpath(userinfo *user.User, vpath string) error {
  1158. ps, err := getPathHashFromUsernameAndVpath(userinfo, vpath)
  1159. if err != nil {
  1160. return err
  1161. }
  1162. if !s.CanModifyShareEntry(userinfo, vpath) {
  1163. return errors.New("Permission denied")
  1164. }
  1165. return s.options.ShareEntryTable.DeleteShareByPathHash(ps)
  1166. }
  1167. func (s *Manager) DeleteShareByUUID(userinfo *user.User, uuid string) error {
  1168. so := s.GetShareObjectFromUUID(uuid)
  1169. if so == nil {
  1170. return errors.New("Invalid share uuid")
  1171. }
  1172. if !s.CanModifyShareEntry(userinfo, so.FileVirtualPath) {
  1173. return errors.New("Permission denied")
  1174. }
  1175. return s.options.ShareEntryTable.DeleteShareByUUID(uuid)
  1176. }
  1177. func (s *Manager) GetShareUUIDFromUserAndVpath(userinfo *user.User, vpath string) string {
  1178. ps, err := getPathHashFromUsernameAndVpath(userinfo, vpath)
  1179. if err != nil {
  1180. return ""
  1181. }
  1182. return s.options.ShareEntryTable.GetShareUUIDFromPathHash(ps)
  1183. }
  1184. func (s *Manager) GetShareObjectFromUserAndVpath(userinfo *user.User, vpath string) *shareEntry.ShareOption {
  1185. ps, err := getPathHashFromUsernameAndVpath(userinfo, vpath)
  1186. if err != nil {
  1187. return nil
  1188. }
  1189. return s.options.ShareEntryTable.GetShareObjectFromPathHash(ps)
  1190. }
  1191. func (s *Manager) GetShareObjectFromUUID(uuid string) *shareEntry.ShareOption {
  1192. return s.options.ShareEntryTable.GetShareObjectFromUUID(uuid)
  1193. }
  1194. func (s *Manager) FileIsShared(userinfo *user.User, vpath string) bool {
  1195. ps, err := getPathHashFromUsernameAndVpath(userinfo, vpath)
  1196. if err != nil {
  1197. return false
  1198. }
  1199. return s.options.ShareEntryTable.FileIsShared(ps)
  1200. }
  1201. func (s *Manager) RemoveShareByUUID(userinfo *user.User, uuid string) error {
  1202. shareObject := s.GetShareObjectFromUUID(uuid)
  1203. if shareObject == nil {
  1204. return errors.New("Share entry not found")
  1205. }
  1206. if !s.CanModifyShareEntry(userinfo, shareObject.FileVirtualPath) {
  1207. return errors.New("Permission denied")
  1208. }
  1209. return s.options.ShareEntryTable.RemoveShareByUUID(uuid)
  1210. }
  1211. func getPathHashFromUsernameAndVpath(userinfo *user.User, vpath string) (string, error) {
  1212. fsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(vpath)
  1213. if err != nil {
  1214. return "", err
  1215. }
  1216. return shareEntry.GetPathHash(fsh, vpath, userinfo.Username)
  1217. }