share.go 38 KB

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