share.go 39 KB

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