share.go 43 KB

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