share.go 38 KB

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