share.go 43 KB

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