share.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885
  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. "io/ioutil"
  11. "log"
  12. "net/http"
  13. "net/url"
  14. "os"
  15. "path/filepath"
  16. "strconv"
  17. "strings"
  18. "sync"
  19. "time"
  20. "github.com/valyala/fasttemplate"
  21. uuid "github.com/satori/go.uuid"
  22. "imuslab.com/arozos/mod/auth"
  23. "imuslab.com/arozos/mod/common"
  24. "imuslab.com/arozos/mod/database"
  25. filesystem "imuslab.com/arozos/mod/filesystem"
  26. "imuslab.com/arozos/mod/user"
  27. )
  28. type Options struct {
  29. AuthAgent *auth.AuthAgent
  30. Database *database.Database
  31. UserHandler *user.UserHandler
  32. HostName string
  33. TmpFolder string
  34. }
  35. type ShareOption struct {
  36. UUID string
  37. FileRealPath string
  38. Owner string
  39. Accessibles []string //Use to store username or group names if permission is groups or users
  40. Permission string //Access permission, allow {anyone / signedin / samegroup / groups / users}
  41. AllowLivePreview bool
  42. }
  43. type Manager struct {
  44. fileToUrlMap *sync.Map
  45. urlToFileMap *sync.Map
  46. options Options
  47. }
  48. //Create a new Share Manager
  49. func NewShareManager(options Options) *Manager {
  50. //Create the share table if not exists
  51. db := options.Database
  52. db.NewTable("share")
  53. fileToUrlMap := sync.Map{}
  54. urlToFileMap := sync.Map{}
  55. //Load the old share links
  56. entries, _ := db.ListTable("share")
  57. for _, keypairs := range entries {
  58. shareObject := new(ShareOption)
  59. json.Unmarshal(keypairs[1], &shareObject)
  60. if shareObject != nil {
  61. //Append this to the maps
  62. fileToUrlMap.Store(shareObject.FileRealPath, shareObject)
  63. urlToFileMap.Store(shareObject.UUID, shareObject)
  64. }
  65. }
  66. //Return a new manager object
  67. return &Manager{
  68. options: options,
  69. fileToUrlMap: &fileToUrlMap,
  70. urlToFileMap: &urlToFileMap,
  71. }
  72. }
  73. //Main function for handle share. Must be called with http.HandleFunc (No auth)
  74. func (s *Manager) HandleShareAccess(w http.ResponseWriter, r *http.Request) {
  75. id, err := mv(r, "id", false)
  76. if err != nil {
  77. http.NotFound(w, r)
  78. return
  79. }
  80. directDownload := false
  81. directServe := false
  82. download, _ := mv(r, "download", false)
  83. if download == "true" {
  84. directDownload = true
  85. }
  86. serve, _ := mv(r, "serve", false)
  87. if serve == "true" {
  88. directServe = true
  89. }
  90. relpath, _ := mv(r, "rel", false)
  91. //Check if id exists
  92. val, ok := s.urlToFileMap.Load(id)
  93. if ok {
  94. //Parse the option structure
  95. shareOption := val.(*ShareOption)
  96. //Check for permission
  97. if shareOption.Permission == "anyone" {
  98. //OK to proceed
  99. } else if shareOption.Permission == "signedin" {
  100. if s.options.AuthAgent.CheckAuth(r) == false {
  101. //Redirect to login page
  102. if directDownload || directServe {
  103. w.WriteHeader(http.StatusUnauthorized)
  104. w.Write([]byte("401 - Unauthorized"))
  105. } else {
  106. http.Redirect(w, r, common.ConstructRelativePathFromRequestURL(r.RequestURI, "login.system")+"?redirect=/share?id="+id, 307)
  107. }
  108. return
  109. } else {
  110. //Ok to proccedd
  111. }
  112. } else if shareOption.Permission == "samegroup" {
  113. thisuserinfo, err := s.options.UserHandler.GetUserInfoFromRequest(w, r)
  114. if err != nil {
  115. if directDownload || directServe {
  116. w.WriteHeader(http.StatusUnauthorized)
  117. w.Write([]byte("401 - Unauthorized"))
  118. } else {
  119. http.Redirect(w, r, common.ConstructRelativePathFromRequestURL(r.RequestURI, "login.system")+"?redirect=/share?id="+id, 307)
  120. }
  121. return
  122. }
  123. //Check if all the user groups are inside the share owner groups
  124. valid := true
  125. thisUsersGroupByName := []string{}
  126. for _, pg := range thisuserinfo.PermissionGroup {
  127. thisUsersGroupByName = append(thisUsersGroupByName, pg.Name)
  128. }
  129. for _, allowedpg := range shareOption.Accessibles {
  130. if inArray(thisUsersGroupByName, allowedpg) {
  131. //This required group is inside this user's group. OK
  132. } else {
  133. //This required group is not inside user's group. Reject
  134. valid = false
  135. }
  136. }
  137. if !valid {
  138. //Serve permission denied page
  139. if directDownload || directServe {
  140. w.WriteHeader(http.StatusForbidden)
  141. w.Write([]byte("401 - Forbidden"))
  142. } else {
  143. ServePermissionDeniedPage(w)
  144. }
  145. return
  146. }
  147. } else if shareOption.Permission == "users" {
  148. thisuserinfo, err := s.options.UserHandler.GetUserInfoFromRequest(w, r)
  149. if err != nil {
  150. //User not logged in. Redirect to login page
  151. if directDownload || directServe {
  152. w.WriteHeader(http.StatusUnauthorized)
  153. w.Write([]byte("401 - Unauthorized"))
  154. } else {
  155. http.Redirect(w, r, common.ConstructRelativePathFromRequestURL(r.RequestURI, "login.system")+"?redirect=/share?id="+id, 307)
  156. }
  157. return
  158. }
  159. //Check if username in the allowed user list
  160. if !inArray(shareOption.Accessibles, thisuserinfo.Username) {
  161. //Serve permission denied page
  162. //Serve permission denied page
  163. if directDownload || directServe {
  164. w.WriteHeader(http.StatusForbidden)
  165. w.Write([]byte("401 - Forbidden"))
  166. } else {
  167. ServePermissionDeniedPage(w)
  168. }
  169. return
  170. }
  171. } else if shareOption.Permission == "groups" {
  172. thisuserinfo, err := s.options.UserHandler.GetUserInfoFromRequest(w, r)
  173. if err != nil {
  174. //User not logged in. Redirect to login page
  175. if directDownload || directServe {
  176. w.WriteHeader(http.StatusUnauthorized)
  177. w.Write([]byte("401 - Unauthorized"))
  178. } else {
  179. http.Redirect(w, r, common.ConstructRelativePathFromRequestURL(r.RequestURI, "login.system")+"?redirect=/share?id="+id, 307)
  180. }
  181. return
  182. }
  183. allowAccess := false
  184. thisUsersGroupByName := []string{}
  185. for _, pg := range thisuserinfo.PermissionGroup {
  186. thisUsersGroupByName = append(thisUsersGroupByName, pg.Name)
  187. }
  188. for _, thisUserPg := range thisUsersGroupByName {
  189. if inArray(shareOption.Accessibles, thisUserPg) {
  190. allowAccess = true
  191. }
  192. }
  193. if !allowAccess {
  194. //Serve permission denied page
  195. if directDownload || directServe {
  196. w.WriteHeader(http.StatusForbidden)
  197. w.Write([]byte("401 - Forbidden"))
  198. } else {
  199. ServePermissionDeniedPage(w)
  200. }
  201. return
  202. }
  203. } else {
  204. //Unsupported mode. Show notfound
  205. http.NotFound(w, r)
  206. return
  207. }
  208. //Serve the download page
  209. if isDir(shareOption.FileRealPath) {
  210. type File struct {
  211. Filename string
  212. RelPath string
  213. Filesize string
  214. IsDir bool
  215. }
  216. if directDownload == true {
  217. if relpath != "" {
  218. //User specified a specific file within the directory. Escape the relpath
  219. targetFilepath := filepath.Join(shareOption.FileRealPath, relpath)
  220. //Check if file exists
  221. if !fileExists(targetFilepath) {
  222. http.NotFound(w, r)
  223. return
  224. }
  225. //Validate the absolute path to prevent path escape
  226. absroot, _ := filepath.Abs(shareOption.FileRealPath)
  227. abstarget, _ := filepath.Abs(targetFilepath)
  228. if len(abstarget) <= len(absroot) || abstarget[:len(absroot)] != absroot {
  229. //Directory escape detected
  230. w.WriteHeader(http.StatusBadRequest)
  231. w.Write([]byte("400 - Bad Request: Invalid relative path"))
  232. return
  233. }
  234. //Serve the target file
  235. w.Header().Set("Content-Disposition", "attachment; filename*=UTF-8''"+strings.ReplaceAll(url.QueryEscape(filepath.Base(targetFilepath)), "+", "%20"))
  236. w.Header().Set("Content-Type", r.Header.Get("Content-Type"))
  237. http.ServeFile(w, r, targetFilepath)
  238. sendOK(w)
  239. } else {
  240. //Download this folder as zip
  241. //Build the filelist to download
  242. //Create a zip using ArOZ Zipper, tmp zip files are located under tmp/share-cache/*.zip
  243. tmpFolder := s.options.TmpFolder
  244. tmpFolder = filepath.Join(tmpFolder, "share-cache")
  245. os.MkdirAll(tmpFolder, 0755)
  246. targetZipFilename := filepath.Join(tmpFolder, filepath.Base(shareOption.FileRealPath)) + ".zip"
  247. //Build a filelist
  248. err := filesystem.ArozZipFile([]string{shareOption.FileRealPath}, targetZipFilename, false)
  249. if err != nil {
  250. //Failed to create zip file
  251. w.WriteHeader(http.StatusInternalServerError)
  252. w.Write([]byte("500 - Internal Server Error: Zip file creation failed"))
  253. log.Println("Failed to create zip file for share download: " + err.Error())
  254. return
  255. }
  256. //Serve thje zip file
  257. w.Header().Set("Content-Disposition", "attachment; filename*=UTF-8''"+strings.ReplaceAll(url.QueryEscape(filepath.Base(shareOption.FileRealPath)), "+", "%20")+".zip")
  258. w.Header().Set("Content-Type", r.Header.Get("Content-Type"))
  259. http.ServeFile(w, r, targetZipFilename)
  260. }
  261. } else {
  262. //Show download page. Do not allow serving
  263. content, err := ioutil.ReadFile("./system/share/downloadPageFolder.html")
  264. if err != nil {
  265. http.NotFound(w, r)
  266. return
  267. }
  268. //Get file size
  269. fsize, fcount := filesystem.GetDirctorySize(shareOption.FileRealPath, false)
  270. //Build the tree list of the folder
  271. treeList := map[string][]File{}
  272. err = filepath.Walk(filepath.Clean(shareOption.FileRealPath), func(file string, info os.FileInfo, err error) error {
  273. if err != nil {
  274. //If error skip this
  275. return nil
  276. }
  277. if filepath.Base(file)[:1] != "." {
  278. fileSize := filesystem.GetFileSize(file)
  279. if filesystem.IsDir(file) {
  280. fileSize, _ = filesystem.GetDirctorySize(file, false)
  281. }
  282. relPath, err := filepath.Rel(shareOption.FileRealPath, file)
  283. if err != nil {
  284. relPath = ""
  285. }
  286. relPath = filepath.ToSlash(filepath.Clean(relPath))
  287. relDir := filepath.ToSlash(filepath.Dir(relPath))
  288. if relPath == "." {
  289. //The root file object. Skip this
  290. return nil
  291. }
  292. treeList[relDir] = append(treeList[relDir], File{
  293. Filename: filepath.Base(file),
  294. RelPath: filepath.ToSlash(relPath),
  295. Filesize: filesystem.GetFileDisplaySize(fileSize, 2),
  296. IsDir: filesystem.IsDir(file),
  297. })
  298. }
  299. return nil
  300. })
  301. tl, _ := json.Marshal(treeList)
  302. //Get modification time
  303. fmodtime, _ := filesystem.GetModTime(shareOption.FileRealPath)
  304. timeString := time.Unix(fmodtime, 0).Format("02-01-2006 15:04:05")
  305. t := fasttemplate.New(string(content), "{{", "}}")
  306. s := t.ExecuteString(map[string]interface{}{
  307. "hostname": s.options.HostName,
  308. "reqid": id,
  309. "mime": "application/x-directory",
  310. "size": filesystem.GetFileDisplaySize(fsize, 2),
  311. "filecount": strconv.Itoa(fcount),
  312. "modtime": timeString,
  313. "downloadurl": "./share?id=" + id + "&download=true",
  314. "filename": filepath.Base(shareOption.FileRealPath),
  315. "reqtime": strconv.Itoa(int(time.Now().Unix())),
  316. "treelist": tl,
  317. "downloaduuid": id,
  318. })
  319. w.Write([]byte(s))
  320. return
  321. }
  322. } else {
  323. if directDownload {
  324. //Serve the file directly
  325. w.Header().Set("Content-Disposition", "attachment; filename*=UTF-8''"+strings.ReplaceAll(url.QueryEscape(filepath.Base(shareOption.FileRealPath)), "+", "%20"))
  326. w.Header().Set("Content-Type", r.Header.Get("Content-Type"))
  327. http.ServeFile(w, r, shareOption.FileRealPath)
  328. } else if directServe {
  329. w.Header().Set("Access-Control-Allow-Origin", "*")
  330. w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
  331. w.Header().Set("Content-Type", r.Header.Get("Content-Type"))
  332. http.ServeFile(w, r, shareOption.FileRealPath)
  333. } else {
  334. //Serve the download page
  335. content, err := ioutil.ReadFile("./system/share/downloadPage.html")
  336. if err != nil {
  337. http.NotFound(w, r)
  338. return
  339. }
  340. //Get file mime type
  341. mime, ext, err := filesystem.GetMime(shareOption.FileRealPath)
  342. if err != nil {
  343. mime = "Unknown"
  344. }
  345. //Load the preview template
  346. templateRoot := "./system/share/"
  347. previewTemplate := filepath.Join(templateRoot, "defaultTemplate.html")
  348. if ext == ".mp4" || ext == ".webm" {
  349. previewTemplate = filepath.Join(templateRoot, "video.html")
  350. } else if ext == ".mp3" || ext == ".wav" || ext == ".flac" || ext == ".ogg" {
  351. previewTemplate = filepath.Join(templateRoot, "audio.html")
  352. } else if ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".webp" {
  353. previewTemplate = filepath.Join(templateRoot, "image.html")
  354. } else if ext == ".pdf" {
  355. previewTemplate = filepath.Join(templateRoot, "iframe.html")
  356. } else {
  357. //Format do not support preview. Use the default.html
  358. previewTemplate = filepath.Join(templateRoot, "default.html")
  359. }
  360. tp, err := ioutil.ReadFile(previewTemplate)
  361. if err != nil {
  362. tp = []byte("")
  363. }
  364. //Merge two templates
  365. content = []byte(strings.ReplaceAll(string(content), "{{previewer}}", string(tp)))
  366. //Get file size
  367. fsize := filesystem.GetFileSize(shareOption.FileRealPath)
  368. //Get modification time
  369. fmodtime, _ := filesystem.GetModTime(shareOption.FileRealPath)
  370. timeString := time.Unix(fmodtime, 0).Format("02-01-2006 15:04:05")
  371. t := fasttemplate.New(string(content), "{{", "}}")
  372. s := t.ExecuteString(map[string]interface{}{
  373. "hostname": s.options.HostName,
  374. "reqid": id,
  375. "mime": mime,
  376. "ext": ext,
  377. "size": filesystem.GetFileDisplaySize(fsize, 2),
  378. "modtime": timeString,
  379. "downloadurl": "/share?id=" + id + "&download=true",
  380. "preview_url": "/share?id=" + id + "&serve=true",
  381. "filename": filepath.Base(shareOption.FileRealPath),
  382. "reqtime": strconv.Itoa(int(time.Now().Unix())),
  383. })
  384. w.Write([]byte(s))
  385. return
  386. }
  387. }
  388. } else {
  389. //This share not exists
  390. if err != nil {
  391. //Template not found. Just send a 404 Not Found
  392. http.NotFound(w, r)
  393. return
  394. }
  395. if directDownload == true {
  396. //Send 404 header
  397. http.NotFound(w, r)
  398. return
  399. } else {
  400. //Send not found page
  401. content, err := ioutil.ReadFile("./system/share/notfound.html")
  402. if err != nil {
  403. http.NotFound(w, r)
  404. return
  405. }
  406. t := fasttemplate.New(string(content), "{{", "}}")
  407. s := t.ExecuteString(map[string]interface{}{
  408. "hostname": s.options.HostName,
  409. "reqid": id,
  410. "reqtime": strconv.Itoa(int(time.Now().Unix())),
  411. })
  412. w.Write([]byte(s))
  413. return
  414. }
  415. }
  416. }
  417. //Check if a file is shared
  418. func (s *Manager) HandleShareCheck(w http.ResponseWriter, r *http.Request) {
  419. //Get the vpath from paramters
  420. vpath, err := mv(r, "path", true)
  421. if err != nil {
  422. sendErrorResponse(w, "Invalid path given")
  423. return
  424. }
  425. //Get userinfo
  426. userinfo, err := s.options.UserHandler.GetUserInfoFromRequest(w, r)
  427. if err != nil {
  428. sendErrorResponse(w, "User not logged in")
  429. return
  430. }
  431. //Get realpath from userinfo
  432. rpath, err := userinfo.VirtualPathToRealPath(vpath)
  433. if err != nil {
  434. sendErrorResponse(w, "Unable to resolve realpath")
  435. return
  436. }
  437. type Result struct {
  438. IsShared bool
  439. ShareUUID *ShareOption
  440. }
  441. //Check if share exists
  442. shareExists := s.FileIsShared(rpath)
  443. if !shareExists {
  444. //Share not exists
  445. js, _ := json.Marshal(Result{
  446. IsShared: false,
  447. ShareUUID: &ShareOption{},
  448. })
  449. sendJSONResponse(w, string(js))
  450. } else {
  451. //Share exists
  452. thisSharedInfo := s.GetShareObjectFromRealPath(rpath)
  453. js, _ := json.Marshal(Result{
  454. IsShared: true,
  455. ShareUUID: thisSharedInfo,
  456. })
  457. sendJSONResponse(w, string(js))
  458. }
  459. }
  460. //Create new share from the given path
  461. func (s *Manager) HandleCreateNewShare(w http.ResponseWriter, r *http.Request) {
  462. //Get the vpath from paramters
  463. vpath, err := mv(r, "path", true)
  464. if err != nil {
  465. sendErrorResponse(w, "Invalid path given")
  466. return
  467. }
  468. //Get userinfo
  469. userinfo, err := s.options.UserHandler.GetUserInfoFromRequest(w, r)
  470. if err != nil {
  471. sendErrorResponse(w, "User not logged in")
  472. return
  473. }
  474. share, err := s.CreateNewShare(userinfo, vpath)
  475. if err != nil {
  476. sendErrorResponse(w, err.Error())
  477. return
  478. }
  479. js, _ := json.Marshal(share)
  480. sendJSONResponse(w, string(js))
  481. }
  482. // Handle Share Edit.
  483. // For allowing groups / users, use the following syntax
  484. // groups:group1,group2,group3
  485. // users:user1,user2,user3
  486. // For basic modes, use the following keywords
  487. // anyone / signedin / samegroup
  488. // anyone: Anyone who has the link
  489. // signedin: Anyone logged in to this system
  490. // samegroup: The requesting user has the same (or more) user group as the share owner
  491. func (s *Manager) HandleEditShare(w http.ResponseWriter, r *http.Request) {
  492. userinfo, err := s.options.UserHandler.GetUserInfoFromRequest(w, r)
  493. if err != nil {
  494. sendErrorResponse(w, "User not logged in")
  495. return
  496. }
  497. uuid, err := mv(r, "uuid", true)
  498. if err != nil {
  499. sendErrorResponse(w, "Invalid path given")
  500. return
  501. }
  502. shareMode, _ := mv(r, "mode", true)
  503. if shareMode == "" {
  504. shareMode = "signedin"
  505. }
  506. //Check if share exists
  507. so := s.GetShareObjectFromUUID(uuid)
  508. if so == nil {
  509. //This share url not exists
  510. sendErrorResponse(w, "Share UUID not exists")
  511. return
  512. }
  513. //Check if the user has permission to edit this share
  514. if so.Owner != userinfo.Username && userinfo.IsAdmin() == false {
  515. //This file is not shared by this user and this user is not admin. Block this request
  516. sendErrorResponse(w, "Permission denied")
  517. return
  518. }
  519. //Validate and extract the storage mode
  520. ok, sharetype, settings := validateShareModes(shareMode)
  521. if !ok {
  522. sendErrorResponse(w, "Invalid share setting")
  523. return
  524. }
  525. //Analysis the sharetype
  526. if sharetype == "anyone" || sharetype == "signedin" || sharetype == "samegroup" {
  527. //Basic types.
  528. so.Permission = sharetype
  529. if sharetype == "samegroup" {
  530. //Write user groups into accessible (Must be all match inorder to allow access)
  531. userpg := []string{}
  532. for _, pg := range userinfo.PermissionGroup {
  533. userpg = append(userpg, pg.Name)
  534. }
  535. so.Accessibles = userpg
  536. }
  537. //Write changes to database
  538. s.options.Database.Write("share", uuid, so)
  539. } else if sharetype == "groups" || sharetype == "users" {
  540. //Username or group is listed = ok
  541. so.Permission = sharetype
  542. so.Accessibles = settings
  543. //Write changes to database
  544. s.options.Database.Write("share", uuid, so)
  545. }
  546. sendOK(w)
  547. }
  548. func (s *Manager) HandleDeleteShare(w http.ResponseWriter, r *http.Request) {
  549. //Get the vpath from paramters
  550. vpath, err := mv(r, "path", true)
  551. if err != nil {
  552. sendErrorResponse(w, "Invalid path given")
  553. return
  554. }
  555. //Get userinfo
  556. userinfo, err := s.options.UserHandler.GetUserInfoFromRequest(w, r)
  557. if err != nil {
  558. sendErrorResponse(w, "User not logged in")
  559. return
  560. }
  561. //Delete the share setting
  562. err = s.DeleteShare(userinfo, vpath)
  563. if err != nil {
  564. sendErrorResponse(w, err.Error())
  565. } else {
  566. sendOK(w)
  567. }
  568. }
  569. //Craete a new file or folder share
  570. func (s *Manager) CreateNewShare(userinfo *user.User, vpath string) (*ShareOption, error) {
  571. //Translate the vpath to realpath
  572. rpath, err := userinfo.VirtualPathToRealPath(vpath)
  573. if err != nil {
  574. return nil, errors.New("Unable to find the file on disk")
  575. }
  576. rpath = filepath.ToSlash(filepath.Clean(rpath))
  577. //Check if source file exists
  578. if !fileExists(rpath) {
  579. return nil, errors.New("Unable to find the file on disk")
  580. }
  581. //Check if the share already exists. If yes, use the previous link
  582. val, ok := s.fileToUrlMap.Load(rpath)
  583. if ok {
  584. //Exists. Send back the old share url
  585. ShareOption := val.(*ShareOption)
  586. return ShareOption, nil
  587. } else {
  588. //Create new link for this file
  589. shareUUID := uuid.NewV4().String()
  590. //user groups when share
  591. groups := []string{}
  592. for _, pg := range userinfo.GetUserPermissionGroup() {
  593. groups = append(groups, pg.Name)
  594. }
  595. //Create a share object
  596. shareOption := ShareOption{
  597. UUID: shareUUID,
  598. FileRealPath: rpath,
  599. Owner: userinfo.Username,
  600. Accessibles: groups,
  601. Permission: "anyone",
  602. AllowLivePreview: true,
  603. }
  604. //Store results on two map to make sure O(1) Lookup time
  605. s.fileToUrlMap.Store(rpath, &shareOption)
  606. s.urlToFileMap.Store(shareUUID, &shareOption)
  607. //Write object to database
  608. s.options.Database.Write("share", shareUUID, shareOption)
  609. return &shareOption, nil
  610. }
  611. }
  612. //Delete the share on this vpath
  613. func (s *Manager) DeleteShare(userinfo *user.User, vpath string) error {
  614. //Translate the vpath to realpath
  615. rpath, err := userinfo.VirtualPathToRealPath(vpath)
  616. if err != nil {
  617. return errors.New("Unable to find the file on disk")
  618. }
  619. //Check if the share already exists. If yes, use the previous link
  620. val, ok := s.fileToUrlMap.Load(rpath)
  621. if ok {
  622. //Exists. Send back the old share url
  623. uuid := val.(*ShareOption).UUID
  624. //Remove this from the database
  625. err = s.options.Database.Delete("share", uuid)
  626. if err != nil {
  627. return err
  628. }
  629. //Remove this form the current sync map
  630. s.urlToFileMap.Delete(uuid)
  631. s.fileToUrlMap.Delete(rpath)
  632. return nil
  633. } else {
  634. //Already deleted from buffered record.
  635. return nil
  636. }
  637. }
  638. func (s *Manager) GetShareUUIDFromPath(rpath string) string {
  639. targetShareObject := s.GetShareObjectFromRealPath(rpath)
  640. if (targetShareObject) != nil {
  641. return targetShareObject.UUID
  642. }
  643. return ""
  644. }
  645. func (s *Manager) GetShareObjectFromRealPath(rpath string) *ShareOption {
  646. rpath = filepath.ToSlash(filepath.Clean(rpath))
  647. var targetShareOption *ShareOption
  648. s.fileToUrlMap.Range(func(k, v interface{}) bool {
  649. filePath := k.(string)
  650. shareObject := v.(*ShareOption)
  651. if filepath.ToSlash(filepath.Clean(filePath)) == rpath {
  652. targetShareOption = shareObject
  653. }
  654. return true
  655. })
  656. return targetShareOption
  657. }
  658. func (s *Manager) GetShareObjectFromUUID(uuid string) *ShareOption {
  659. var targetShareOption *ShareOption
  660. s.urlToFileMap.Range(func(k, v interface{}) bool {
  661. thisUuid := k.(string)
  662. shareObject := v.(*ShareOption)
  663. if thisUuid == uuid {
  664. targetShareOption = shareObject
  665. }
  666. return true
  667. })
  668. return targetShareOption
  669. }
  670. func (s *Manager) FileIsShared(rpath string) bool {
  671. shareUUID := s.GetShareUUIDFromPath(rpath)
  672. return shareUUID != ""
  673. }
  674. func ServePermissionDeniedPage(w http.ResponseWriter) {
  675. w.WriteHeader(http.StatusForbidden)
  676. pageContent := []byte("Permissioned Denied")
  677. if fileExists("system/share/permissionDenied.html") {
  678. content, err := ioutil.ReadFile("system/share/permissionDenied.html")
  679. if err == nil {
  680. pageContent = content
  681. }
  682. }
  683. w.Write([]byte(pageContent))
  684. }
  685. /*
  686. Validate Share Mode string
  687. will return
  688. 1. bool => Is valid
  689. 2. permission type: {basic / groups / users}
  690. 3. mode string
  691. */
  692. func validateShareModes(mode string) (bool, string, []string) {
  693. // user:a,b,c,d
  694. validModes := []string{"anyone", "signedin", "samegroup"}
  695. if inArray(validModes, mode) {
  696. //Standard modes
  697. return true, mode, []string{}
  698. } else if len(mode) > 7 && mode[:7] == "groups:" {
  699. //Handle custom group case like groups:a,b,c,d
  700. groupList := mode[7:]
  701. if len(groupList) > 0 {
  702. groups := strings.Split(groupList, ",")
  703. return true, "groups", groups
  704. } else {
  705. //Invalid configuration
  706. return false, "groups", []string{}
  707. }
  708. } else if len(mode) > 6 && mode[:6] == "users:" {
  709. //Handle custom usersname like users:a,b,c,d
  710. userList := mode[6:]
  711. if len(userList) > 0 {
  712. users := strings.Split(userList, ",")
  713. return true, "users", users
  714. } else {
  715. //Invalid configuration
  716. return false, "users", []string{}
  717. }
  718. }
  719. return false, "", []string{}
  720. }
  721. func (s *Manager) RemoveShareByRealpath(rpath string) error {
  722. _, ok := s.fileToUrlMap.Load(rpath)
  723. if ok {
  724. s.fileToUrlMap.Delete(rpath)
  725. } else {
  726. return errors.New("Share with given realpath not exists")
  727. }
  728. return nil
  729. }
  730. func (s *Manager) RemoveShareByUUID(uuid string) error {
  731. _, ok := s.urlToFileMap.Load(uuid)
  732. if ok {
  733. s.urlToFileMap.Delete(uuid)
  734. } else {
  735. return errors.New("Share with given uuid not exists")
  736. }
  737. return nil
  738. }
  739. //Check and clear shares that its pointinf files no longe exists
  740. func (s *Manager) ValidateAndClearShares() {
  741. //Iterate through all shares within the system
  742. s.fileToUrlMap.Range(func(k, v interface{}) bool {
  743. thisRealPath := k.(string)
  744. if !fileExists(thisRealPath) {
  745. //This share source file don't exists anymore. Remove it
  746. thisFileShareOption := v.(*ShareOption)
  747. //Delete this task from both sync map
  748. s.RemoveShareByRealpath(thisRealPath)
  749. s.RemoveShareByUUID(thisFileShareOption.UUID)
  750. //Remove share from database
  751. s.options.Database.Delete("share", thisFileShareOption.UUID)
  752. log.Println("*Share* Removing share to file: " + thisRealPath + " as it no longer exists")
  753. }
  754. return true
  755. })
  756. }