share.go 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301
  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 filesystem.FileExists(fileRuntimeAbsPath) {
  610. //This file exists in local file system. Serve it directly
  611. http.ServeFile(w, r, fileRuntimeAbsPath)
  612. } else {
  613. //This file is not on local file system. Use streaming
  614. f, _ := targetFshAbs.ReadStream(fileRuntimeAbsPath)
  615. io.Copy(w, f)
  616. f.Close()
  617. }
  618. } else if directServe {
  619. w.Header().Set("Access-Control-Allow-Origin", "*")
  620. w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
  621. w.Header().Set("Content-Type", contentType)
  622. f, _ := targetFshAbs.ReadStream(fileRuntimeAbsPath)
  623. io.Copy(w, f)
  624. f.Close()
  625. } else {
  626. //Serve the download page
  627. content, err := ioutil.ReadFile("./system/share/downloadPage.html")
  628. if err != nil {
  629. http.NotFound(w, r)
  630. return
  631. }
  632. //Get file mime type
  633. mime, ext, err := filesystem.GetMime(fileRuntimeAbsPath)
  634. if err != nil {
  635. mime = "Unknown"
  636. }
  637. //Load the preview template
  638. templateRoot := "./system/share/"
  639. previewTemplate := ""
  640. if ext == ".mp4" || ext == ".webm" {
  641. previewTemplate = filepath.Join(templateRoot, "video.html")
  642. } else if ext == ".mp3" || ext == ".wav" || ext == ".flac" || ext == ".ogg" {
  643. previewTemplate = filepath.Join(templateRoot, "audio.html")
  644. } else if ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".webp" {
  645. previewTemplate = filepath.Join(templateRoot, "image.html")
  646. } else if ext == ".pdf" {
  647. previewTemplate = filepath.Join(templateRoot, "iframe.html")
  648. } else {
  649. //Format do not support preview. Use the default.html
  650. previewTemplate = filepath.Join(templateRoot, "default.html")
  651. }
  652. tp, err := ioutil.ReadFile(previewTemplate)
  653. if err != nil {
  654. tp = []byte("")
  655. }
  656. //Merge two templates
  657. content = []byte(strings.ReplaceAll(string(content), "{{previewer}}", string(tp)))
  658. //Get file size
  659. fsize := targetFshAbs.GetFileSize(fileRuntimeAbsPath)
  660. //Get modification time
  661. fmodtime, _ := targetFshAbs.GetModTime(fileRuntimeAbsPath)
  662. timeString := time.Unix(fmodtime, 0).Format("02-01-2006 15:04:05")
  663. //Check if ext match with filepath ext
  664. displayExt := ext
  665. if ext != filepath.Ext(fileRuntimeAbsPath) {
  666. displayExt = filepath.Ext(fileRuntimeAbsPath) + " (" + ext + ")"
  667. }
  668. t := fasttemplate.New(string(content), "{{", "}}")
  669. s := t.ExecuteString(map[string]interface{}{
  670. "hostname": s.options.HostName,
  671. "host": r.Host,
  672. "reqid": id,
  673. "requri": "//" + r.Host + r.URL.Path,
  674. "mime": mime,
  675. "ext": displayExt,
  676. "size": filesystem.GetFileDisplaySize(fsize, 2),
  677. "modtime": timeString,
  678. "downloadurl": "/share/download/" + id + "/" + filepath.Base(fileRuntimeAbsPath),
  679. "preview_url": "/share/preview/" + id + "/",
  680. "filename": filepath.Base(fileRuntimeAbsPath),
  681. "opg_image": "/share/opg/" + strconv.Itoa(int(time.Now().Unix())) + "/" + id,
  682. "reqtime": strconv.Itoa(int(time.Now().Unix())),
  683. })
  684. w.Write([]byte(s))
  685. return
  686. }
  687. }
  688. } else {
  689. //This share not exists
  690. if directDownload {
  691. //Send 404 header
  692. http.NotFound(w, r)
  693. return
  694. } else {
  695. //Send not found page
  696. content, err := ioutil.ReadFile("./system/share/notfound.html")
  697. if err != nil {
  698. http.NotFound(w, r)
  699. return
  700. }
  701. t := fasttemplate.New(string(content), "{{", "}}")
  702. s := t.ExecuteString(map[string]interface{}{
  703. "hostname": s.options.HostName,
  704. "reqid": id,
  705. "reqtime": strconv.Itoa(int(time.Now().Unix())),
  706. })
  707. w.Write([]byte(s))
  708. return
  709. }
  710. }
  711. }
  712. //Check if a file is shared
  713. func (s *Manager) HandleShareCheck(w http.ResponseWriter, r *http.Request) {
  714. //Get the vpath from paramters
  715. vpath, err := mv(r, "path", true)
  716. if err != nil {
  717. sendErrorResponse(w, "Invalid path given")
  718. return
  719. }
  720. //Get userinfo
  721. userinfo, err := s.options.UserHandler.GetUserInfoFromRequest(w, r)
  722. if err != nil {
  723. sendErrorResponse(w, "User not logged in")
  724. return
  725. }
  726. fsh, _ := userinfo.GetFileSystemHandlerFromVirtualPath(vpath)
  727. pathHash := shareEntry.GetPathHash(fsh, vpath, userinfo.Username)
  728. type Result struct {
  729. IsShared bool
  730. ShareUUID *shareEntry.ShareOption
  731. }
  732. //Check if share exists
  733. shareExists := s.options.ShareEntryTable.FileIsShared(pathHash)
  734. if !shareExists {
  735. //Share not exists
  736. js, _ := json.Marshal(Result{
  737. IsShared: false,
  738. ShareUUID: &shareEntry.ShareOption{},
  739. })
  740. sendJSONResponse(w, string(js))
  741. } else {
  742. //Share exists
  743. thisSharedInfo := s.options.ShareEntryTable.GetShareObjectFromPathHash(pathHash)
  744. js, _ := json.Marshal(Result{
  745. IsShared: true,
  746. ShareUUID: thisSharedInfo,
  747. })
  748. sendJSONResponse(w, string(js))
  749. }
  750. }
  751. //Create new share from the given path
  752. func (s *Manager) HandleCreateNewShare(w http.ResponseWriter, r *http.Request) {
  753. //Get the vpath from paramters
  754. vpath, err := mv(r, "path", true)
  755. if err != nil {
  756. sendErrorResponse(w, "Invalid path given")
  757. return
  758. }
  759. //Get userinfo
  760. userinfo, err := s.options.UserHandler.GetUserInfoFromRequest(w, r)
  761. if err != nil {
  762. sendErrorResponse(w, "User not logged in")
  763. return
  764. }
  765. //Get the target fsh that this vpath come from
  766. vpathSourceFsh := userinfo.GetRootFSHFromVpathInUserScope(vpath)
  767. if vpathSourceFsh == nil {
  768. sendErrorResponse(w, "Invalid vpath given")
  769. return
  770. }
  771. share, err := s.CreateNewShare(userinfo, vpathSourceFsh, vpath)
  772. if err != nil {
  773. sendErrorResponse(w, err.Error())
  774. return
  775. }
  776. js, _ := json.Marshal(share)
  777. sendJSONResponse(w, string(js))
  778. }
  779. // Handle Share Edit.
  780. // For allowing groups / users, use the following syntax
  781. // groups:group1,group2,group3
  782. // users:user1,user2,user3
  783. // For basic modes, use the following keywords
  784. // anyone / signedin / samegroup
  785. // anyone: Anyone who has the link
  786. // signedin: Anyone logged in to this system
  787. // samegroup: The requesting user has the same (or more) user group as the share owner
  788. func (s *Manager) HandleEditShare(w http.ResponseWriter, r *http.Request) {
  789. userinfo, err := s.options.UserHandler.GetUserInfoFromRequest(w, r)
  790. if err != nil {
  791. sendErrorResponse(w, "User not logged in")
  792. return
  793. }
  794. uuid, err := mv(r, "uuid", true)
  795. if err != nil {
  796. sendErrorResponse(w, "Invalid path given")
  797. return
  798. }
  799. shareMode, _ := mv(r, "mode", true)
  800. if shareMode == "" {
  801. shareMode = "signedin"
  802. }
  803. //Check if share exists
  804. so := s.options.ShareEntryTable.GetShareObjectFromUUID(uuid)
  805. if so == nil {
  806. //This share url not exists
  807. sendErrorResponse(w, "Share UUID not exists")
  808. return
  809. }
  810. //Check if the user has permission to edit this share
  811. if !s.CanModifyShareEntry(userinfo, so.FileVirtualPath) {
  812. common.SendErrorResponse(w, "Permission Denied")
  813. return
  814. }
  815. //Validate and extract the storage mode
  816. ok, sharetype, settings := validateShareModes(shareMode)
  817. if !ok {
  818. sendErrorResponse(w, "Invalid share setting")
  819. return
  820. }
  821. //Analysis the sharetype
  822. if sharetype == "anyone" || sharetype == "signedin" || sharetype == "samegroup" {
  823. //Basic types.
  824. so.Permission = sharetype
  825. if sharetype == "samegroup" {
  826. //Write user groups into accessible (Must be all match inorder to allow access)
  827. userpg := []string{}
  828. for _, pg := range userinfo.PermissionGroup {
  829. userpg = append(userpg, pg.Name)
  830. }
  831. so.Accessibles = userpg
  832. }
  833. //Write changes to database
  834. s.options.ShareEntryTable.Database.Write("share", uuid, so)
  835. } else if sharetype == "groups" || sharetype == "users" {
  836. //Username or group is listed = ok
  837. so.Permission = sharetype
  838. so.Accessibles = settings
  839. //Write changes to database
  840. s.options.ShareEntryTable.Database.Write("share", uuid, so)
  841. }
  842. sendOK(w)
  843. }
  844. func (s *Manager) HandleDeleteShare(w http.ResponseWriter, r *http.Request) {
  845. //Get the vpath from paramters
  846. uuid, err := mv(r, "uuid", true)
  847. if err != nil {
  848. sendErrorResponse(w, "Invalid uuid given")
  849. return
  850. }
  851. //Get userinfo
  852. userinfo, err := s.options.UserHandler.GetUserInfoFromRequest(w, r)
  853. if err != nil {
  854. sendErrorResponse(w, "User not logged in")
  855. return
  856. }
  857. //Delete the share setting
  858. err = s.DeleteShareByUUID(userinfo, uuid)
  859. if err != nil {
  860. sendErrorResponse(w, err.Error())
  861. } else {
  862. sendOK(w)
  863. }
  864. }
  865. func (s *Manager) HandleListAllShares(w http.ResponseWriter, r *http.Request) {
  866. userinfo, err := s.options.UserHandler.GetUserInfoFromRequest(w, r)
  867. if err != nil {
  868. common.SendErrorResponse(w, "User not logged in")
  869. return
  870. }
  871. fshId, _ := common.Mv(r, "fsh", false)
  872. results := []*shareEntry.ShareOption{}
  873. if fshId == "" {
  874. //List all
  875. allFsh := userinfo.GetAllFileSystemHandler()
  876. for _, thisFsh := range allFsh {
  877. allShares := s.ListAllShareByFshId(thisFsh.UUID, userinfo)
  878. for _, thisShare := range allShares {
  879. if s.ShareIsValid(thisShare) {
  880. results = append(results, thisShare)
  881. }
  882. }
  883. }
  884. } else {
  885. //List fsh only
  886. targetFsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(fshId)
  887. if err != nil {
  888. common.SendErrorResponse(w, err.Error())
  889. return
  890. }
  891. sharesInThisFsh := s.ListAllShareByFshId(targetFsh.UUID, userinfo)
  892. for _, thisShare := range sharesInThisFsh {
  893. if s.ShareIsValid(thisShare) {
  894. results = append(results, thisShare)
  895. }
  896. }
  897. }
  898. //Reduce the data
  899. type Share struct {
  900. UUID string
  901. FileVirtualPath string
  902. Owner string
  903. Permission string
  904. IsFolder bool
  905. IsOwnerOfShare bool
  906. CanAccess bool
  907. CanOpenInFileManager bool
  908. CanDelete bool
  909. }
  910. reducedResult := []*Share{}
  911. for _, result := range results {
  912. permissionText := result.Permission
  913. if result.Permission == "groups" || result.Permission == "users" {
  914. permissionText = permissionText + " (" + strings.Join(result.Accessibles, ", ") + ")"
  915. }
  916. thisShareInfo := Share{
  917. UUID: result.UUID,
  918. FileVirtualPath: result.FileVirtualPath,
  919. Owner: result.Owner,
  920. Permission: permissionText,
  921. IsFolder: result.IsFolder,
  922. IsOwnerOfShare: userinfo.Username == result.Owner,
  923. CanAccess: result.IsAccessibleBy(userinfo.Username, userinfo.GetUserPermissionGroupNames()),
  924. CanOpenInFileManager: s.UserCanOpenShareInFileManager(result, userinfo),
  925. CanDelete: s.CanModifyShareEntry(userinfo, result.FileVirtualPath),
  926. }
  927. reducedResult = append(reducedResult, &thisShareInfo)
  928. }
  929. js, _ := json.Marshal(reducedResult)
  930. common.SendJSONResponse(w, string(js))
  931. }
  932. /*
  933. Check if the user can open the share in File Manager
  934. There are two conditions where the user can open the file in file manager
  935. 1. If the user is the owner of the file
  936. 2. If the user is NOT the owner of the file but the target fsh is public accessible and in user's fsh list
  937. */
  938. func (s *Manager) UserCanOpenShareInFileManager(share *shareEntry.ShareOption, userinfo *user.User) bool {
  939. if share.Owner == userinfo.Username {
  940. return true
  941. }
  942. fsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(share.FileVirtualPath)
  943. if err != nil {
  944. //User do not have permission to access this fsh
  945. return false
  946. }
  947. rpath, _ := fsh.FileSystemAbstraction.VirtualPathToRealPath(share.FileVirtualPath, userinfo.Username)
  948. if fsh.Hierarchy == "public" && fsh.FileSystemAbstraction.FileExists(rpath) {
  949. return true
  950. }
  951. return false
  952. }
  953. //Craete a new file or folder share
  954. func (s *Manager) CreateNewShare(userinfo *user.User, srcFsh *filesystem.FileSystemHandler, vpath string) (*shareEntry.ShareOption, error) {
  955. //Translate the vpath to realpath
  956. return s.options.ShareEntryTable.CreateNewShare(srcFsh, vpath, userinfo.Username, userinfo.GetUserPermissionGroupNames())
  957. }
  958. func ServePermissionDeniedPage(w http.ResponseWriter) {
  959. w.WriteHeader(http.StatusForbidden)
  960. pageContent := []byte("Permissioned Denied")
  961. if fileExists("system/share/permissionDenied.html") {
  962. content, err := ioutil.ReadFile("system/share/permissionDenied.html")
  963. if err == nil {
  964. pageContent = content
  965. }
  966. }
  967. w.Write([]byte(pageContent))
  968. }
  969. /*
  970. Validate Share Mode string
  971. will return
  972. 1. bool => Is valid
  973. 2. permission type: {basic / groups / users}
  974. 3. mode string
  975. */
  976. func validateShareModes(mode string) (bool, string, []string) {
  977. // user:a,b,c,d
  978. validModes := []string{"anyone", "signedin", "samegroup"}
  979. if inArray(validModes, mode) {
  980. //Standard modes
  981. return true, mode, []string{}
  982. } else if len(mode) > 7 && mode[:7] == "groups:" {
  983. //Handle custom group case like groups:a,b,c,d
  984. groupList := mode[7:]
  985. if len(groupList) > 0 {
  986. groups := strings.Split(groupList, ",")
  987. return true, "groups", groups
  988. } else {
  989. //Invalid configuration
  990. return false, "groups", []string{}
  991. }
  992. } else if len(mode) > 6 && mode[:6] == "users:" {
  993. //Handle custom usersname like users:a,b,c,d
  994. userList := mode[6:]
  995. if len(userList) > 0 {
  996. users := strings.Split(userList, ",")
  997. return true, "users", users
  998. } else {
  999. //Invalid configuration
  1000. return false, "users", []string{}
  1001. }
  1002. }
  1003. return false, "", []string{}
  1004. }
  1005. func (s *Manager) ListAllShareByFshId(fshId string, userinfo *user.User) []*shareEntry.ShareOption {
  1006. results := []*shareEntry.ShareOption{}
  1007. s.options.ShareEntryTable.FileToUrlMap.Range(func(k, v interface{}) bool {
  1008. thisShareOption := v.(*shareEntry.ShareOption)
  1009. if (!userinfo.IsAdmin() && thisShareOption.IsAccessibleBy(userinfo.Username, userinfo.GetUserPermissionGroupNames())) || userinfo.IsAdmin() {
  1010. id, _, _ := filesystem.GetIDFromVirtualPath(thisShareOption.FileVirtualPath)
  1011. if id == fshId {
  1012. results = append(results, thisShareOption)
  1013. }
  1014. }
  1015. return true
  1016. })
  1017. sort.Slice(results, func(i, j int) bool {
  1018. return results[i].UUID < results[j].UUID
  1019. })
  1020. return results
  1021. }
  1022. func (s *Manager) ShareIsValid(thisShareOption *shareEntry.ShareOption) bool {
  1023. vpath := thisShareOption.FileVirtualPath
  1024. userinfo, _ := s.options.UserHandler.GetUserInfoFromUsername(thisShareOption.Owner)
  1025. fsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(vpath)
  1026. if err != nil {
  1027. return false
  1028. }
  1029. fshAbs := fsh.FileSystemAbstraction
  1030. rpath, _ := fshAbs.VirtualPathToRealPath(vpath, userinfo.Username)
  1031. if !fshAbs.FileExists(rpath) {
  1032. return false
  1033. }
  1034. return true
  1035. }
  1036. func (s *Manager) GetPathHashFromShare(thisShareOption *shareEntry.ShareOption) (string, error) {
  1037. vpath := thisShareOption.FileVirtualPath
  1038. userinfo, _ := s.options.UserHandler.GetUserInfoFromUsername(thisShareOption.Owner)
  1039. fsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(vpath)
  1040. if err != nil {
  1041. return "", err
  1042. }
  1043. pathHash := shareEntry.GetPathHash(fsh, vpath, userinfo.Username)
  1044. return pathHash, nil
  1045. }
  1046. //Check and clear shares that its pointinf files no longe exists
  1047. func (s *Manager) ValidateAndClearShares() {
  1048. //Iterate through all shares within the system
  1049. s.options.ShareEntryTable.FileToUrlMap.Range(func(k, v interface{}) bool {
  1050. thisShareOption := v.(*shareEntry.ShareOption)
  1051. pathHash, err := s.GetPathHashFromShare(thisShareOption)
  1052. if err != nil {
  1053. //Unable to resolve path hash. Filesystem handler is gone?
  1054. //s.options.ShareEntryTable.RemoveShareByUUID(thisShareOption.UUID)
  1055. return true
  1056. }
  1057. if !s.ShareIsValid(thisShareOption) {
  1058. //This share source file don't exists anymore. Remove it
  1059. s.options.ShareEntryTable.RemoveShareByPathHash(pathHash)
  1060. log.Println("*Share* Removing share to file: " + thisShareOption.FileRealPath + " as it no longer exists")
  1061. }
  1062. return true
  1063. })
  1064. }
  1065. //Check if the user has the permission to modify this share entry
  1066. func (s *Manager) CanModifyShareEntry(userinfo *user.User, vpath string) bool {
  1067. shareEntry := s.GetShareObjectFromUserAndVpath(userinfo, vpath)
  1068. if shareEntry == nil {
  1069. //Share entry not found
  1070. return false
  1071. }
  1072. //Check if the user is the share owner or the user is admin
  1073. if userinfo.IsAdmin() {
  1074. return true
  1075. } else if userinfo.Username == shareEntry.Owner {
  1076. return true
  1077. }
  1078. //Public fsh where the user and owner both can access
  1079. fsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(vpath)
  1080. if err != nil {
  1081. return false
  1082. }
  1083. rpath, _ := fsh.FileSystemAbstraction.VirtualPathToRealPath(vpath, userinfo.Username)
  1084. if fsh.Hierarchy == "public" && fsh.FileSystemAbstraction.FileExists(rpath) {
  1085. return true
  1086. }
  1087. return false
  1088. }
  1089. func (s *Manager) DeleteShareByVpath(userinfo *user.User, vpath string) error {
  1090. ps := getPathHashFromUsernameAndVpath(userinfo, vpath)
  1091. if !s.CanModifyShareEntry(userinfo, vpath) {
  1092. return errors.New("Permission denied")
  1093. }
  1094. return s.options.ShareEntryTable.DeleteShareByPathHash(ps)
  1095. }
  1096. func (s *Manager) DeleteShareByUUID(userinfo *user.User, uuid string) error {
  1097. so := s.GetShareObjectFromUUID(uuid)
  1098. if so == nil {
  1099. return errors.New("Invalid share uuid")
  1100. }
  1101. if !s.CanModifyShareEntry(userinfo, so.FileVirtualPath) {
  1102. return errors.New("Permission denied")
  1103. }
  1104. return s.options.ShareEntryTable.DeleteShareByUUID(uuid)
  1105. }
  1106. func (s *Manager) GetShareUUIDFromUserAndVpath(userinfo *user.User, vpath string) string {
  1107. ps := getPathHashFromUsernameAndVpath(userinfo, vpath)
  1108. return s.options.ShareEntryTable.GetShareUUIDFromPathHash(ps)
  1109. }
  1110. func (s *Manager) GetShareObjectFromUserAndVpath(userinfo *user.User, vpath string) *shareEntry.ShareOption {
  1111. ps := getPathHashFromUsernameAndVpath(userinfo, vpath)
  1112. return s.options.ShareEntryTable.GetShareObjectFromPathHash(ps)
  1113. }
  1114. func (s *Manager) GetShareObjectFromUUID(uuid string) *shareEntry.ShareOption {
  1115. return s.options.ShareEntryTable.GetShareObjectFromUUID(uuid)
  1116. }
  1117. func (s *Manager) FileIsShared(userinfo *user.User, vpath string) bool {
  1118. ps := getPathHashFromUsernameAndVpath(userinfo, vpath)
  1119. return s.options.ShareEntryTable.FileIsShared(ps)
  1120. }
  1121. func (s *Manager) RemoveShareByUUID(userinfo *user.User, uuid string) error {
  1122. shareObject := s.GetShareObjectFromUUID(uuid)
  1123. if shareObject == nil {
  1124. return errors.New("Share entry not found")
  1125. }
  1126. if !s.CanModifyShareEntry(userinfo, shareObject.FileVirtualPath) {
  1127. return errors.New("Permission denied")
  1128. }
  1129. return s.options.ShareEntryTable.RemoveShareByUUID(uuid)
  1130. }
  1131. func getPathHashFromUsernameAndVpath(userinfo *user.User, vpath string) string {
  1132. fsh, _ := userinfo.GetFileSystemHandlerFromVirtualPath(vpath)
  1133. return shareEntry.GetPathHash(fsh, vpath, userinfo.Username)
  1134. }