share.go 43 KB

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