share.go 43 KB

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