share.go 38 KB

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