desktop.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. package main
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "io/ioutil"
  6. "log"
  7. "net/http"
  8. "os"
  9. "path/filepath"
  10. "strconv"
  11. "strings"
  12. "imuslab.com/arozos/mod/common"
  13. fs "imuslab.com/arozos/mod/filesystem"
  14. "imuslab.com/arozos/mod/filesystem/arozfs"
  15. "imuslab.com/arozos/mod/filesystem/shortcut"
  16. module "imuslab.com/arozos/mod/modules"
  17. prout "imuslab.com/arozos/mod/prouter"
  18. )
  19. //Desktop script initiation
  20. func DesktopInit() {
  21. systemWideLogger.PrintAndLog("Desktop", "Starting Desktop Services", nil)
  22. router := prout.NewModuleRouter(prout.RouterOption{
  23. ModuleName: "Desktop",
  24. AdminOnly: false,
  25. UserHandler: userHandler,
  26. DeniedHandler: func(w http.ResponseWriter, r *http.Request) {
  27. common.SendErrorResponse(w, "Permission Denied")
  28. },
  29. })
  30. //Register all the required API
  31. router.HandleFunc("/system/desktop/listDesktop", desktop_listFiles)
  32. router.HandleFunc("/system/desktop/theme", desktop_theme_handler)
  33. router.HandleFunc("/system/desktop/files", desktop_fileLocation_handler)
  34. router.HandleFunc("/system/desktop/host", desktop_hostdetailHandler)
  35. router.HandleFunc("/system/desktop/user", desktop_handleUserInfo)
  36. router.HandleFunc("/system/desktop/preference", desktop_preference_handler)
  37. router.HandleFunc("/system/desktop/createShortcut", desktop_shortcutHandler)
  38. //API related to desktop based operations
  39. router.HandleFunc("/system/desktop/opr/renameShortcut", desktop_handleShortcutRename)
  40. //Initialize desktop database
  41. err := sysdb.NewTable("desktop")
  42. if err != nil {
  43. log.Fatal(err)
  44. os.Exit(1)
  45. }
  46. //Register Desktop Module
  47. moduleHandler.RegisterModule(module.ModuleInfo{
  48. Name: "Desktop",
  49. Desc: "The Web Desktop experience for everyone",
  50. Group: "Interface Module",
  51. IconPath: "img/desktop/desktop.png",
  52. Version: internal_version,
  53. StartDir: "",
  54. SupportFW: false,
  55. LaunchFWDir: "",
  56. SupportEmb: false,
  57. })
  58. }
  59. /*
  60. FUNCTIONS RELATED TO PARSING DESKTOP FILE ICONS
  61. The functions in this section handle file listing and its icon locations.
  62. */
  63. func desktop_initUserFolderStructure(username string) {
  64. //Call to filesystem for creating user file struture at root dir
  65. userinfo, _ := userHandler.GetUserInfoFromUsername(username)
  66. homedir, err := userinfo.GetHomeDirectory()
  67. if err != nil {
  68. systemWideLogger.PrintAndLog("Desktop", "Unable to initiate user desktop folder", err)
  69. return
  70. }
  71. if !fs.FileExists(filepath.Join(homedir, "Desktop")) {
  72. //Desktop directory not exists. Create one and copy a template desktop
  73. os.MkdirAll(homedir+"Desktop", 0755)
  74. templateFolder := "./system/desktop/template/"
  75. if fs.FileExists(templateFolder) {
  76. templateFiles, _ := filepath.Glob(templateFolder + "*")
  77. for _, tfile := range templateFiles {
  78. input, _ := ioutil.ReadFile(tfile)
  79. ioutil.WriteFile(homedir+"Desktop/"+filepath.Base(tfile), input, 0755)
  80. }
  81. }
  82. }
  83. }
  84. //Return the information about the host
  85. func desktop_hostdetailHandler(w http.ResponseWriter, r *http.Request) {
  86. type returnStruct struct {
  87. Hostname string
  88. DeviceUUID string
  89. BuildVersion string
  90. InternalVersion string
  91. DeviceVendor string
  92. DeviceModel string
  93. VendorIcon string
  94. }
  95. jsonString, _ := json.Marshal(returnStruct{
  96. Hostname: *host_name,
  97. DeviceUUID: deviceUUID,
  98. BuildVersion: build_version,
  99. InternalVersion: internal_version,
  100. DeviceVendor: deviceVendor,
  101. DeviceModel: deviceModel,
  102. VendorIcon: iconVendor,
  103. })
  104. common.SendJSONResponse(w, string(jsonString))
  105. }
  106. func desktop_handleShortcutRename(w http.ResponseWriter, r *http.Request) {
  107. //Check if the user directory already exists
  108. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  109. if err != nil {
  110. common.SendErrorResponse(w, "User not logged in")
  111. return
  112. }
  113. //Get the shortcut file that is renaming
  114. target, err := common.Mv(r, "src", false)
  115. if err != nil {
  116. common.SendErrorResponse(w, "Invalid shortcut file path given")
  117. return
  118. }
  119. //Get the new name
  120. new, err := common.Mv(r, "new", false)
  121. if err != nil {
  122. common.SendErrorResponse(w, "Invalid new name given")
  123. return
  124. }
  125. fsh, subpath, _ := GetFSHandlerSubpathFromVpath(target)
  126. fshAbs := fsh.FileSystemAbstraction
  127. //Check if the file actually exists and it is on desktop
  128. rpath, err := fshAbs.VirtualPathToRealPath(subpath, userinfo.Username)
  129. if err != nil {
  130. common.SendErrorResponse(w, err.Error())
  131. return
  132. }
  133. if target[:14] != "user:/Desktop/" {
  134. common.SendErrorResponse(w, "Shortcut not on desktop")
  135. return
  136. }
  137. if !fshAbs.FileExists(rpath) {
  138. common.SendErrorResponse(w, "File not exists")
  139. return
  140. }
  141. //OK. Change the name of the shortcut
  142. originalShortcut, err := fshAbs.ReadFile(rpath)
  143. if err != nil {
  144. common.SendErrorResponse(w, "Shortcut file read failed")
  145. return
  146. }
  147. lines := strings.Split(string(originalShortcut), "\n")
  148. if len(lines) < 4 {
  149. //Invalid shortcut properties
  150. common.SendErrorResponse(w, "Invalid shortcut file")
  151. return
  152. }
  153. //Change the 2nd line to the new name
  154. lines[1] = new
  155. newShortcutContent := strings.Join(lines, "\n")
  156. err = fshAbs.WriteFile(rpath, []byte(newShortcutContent), 0755)
  157. if err != nil {
  158. common.SendErrorResponse(w, err.Error())
  159. return
  160. }
  161. common.SendOK(w)
  162. }
  163. func desktop_listFiles(w http.ResponseWriter, r *http.Request) {
  164. //Check if the user directory already exists
  165. userinfo, _ := userHandler.GetUserInfoFromRequest(w, r)
  166. username := userinfo.Username
  167. //Initiate the user folder structure. Do nothing if the structure already exists.
  168. desktop_initUserFolderStructure(username)
  169. //List all files inside the user desktop directory
  170. fsh, subpath, _ := GetFSHandlerSubpathFromVpath("user:/Desktop/")
  171. fshAbs := fsh.FileSystemAbstraction
  172. userDesktopRealpath, _ := fshAbs.VirtualPathToRealPath(subpath, userinfo.Username)
  173. files, err := filepath.Glob(userDesktopRealpath + "/*")
  174. if err != nil {
  175. log.Fatal("Error. Desktop unable to load user files for :" + username)
  176. return
  177. }
  178. //Desktop object structure
  179. type desktopObject struct {
  180. Filepath string
  181. Filename string
  182. Ext string
  183. IsDir bool
  184. IsEmptyDir bool
  185. IsShortcut bool
  186. IsShared bool
  187. ShortcutImage string
  188. ShortcutType string
  189. ShortcutName string
  190. ShortcutPath string
  191. IconX int
  192. IconY int
  193. }
  194. var desktopFiles []desktopObject
  195. for _, this := range files {
  196. //Always use linux convension for directory seperator
  197. if filepath.Base(this)[:1] == "." {
  198. //Skipping hidden files
  199. continue
  200. }
  201. this = filepath.ToSlash(this)
  202. thisFileObject := new(desktopObject)
  203. thisFileObject.Filepath, _ = fshAbs.RealPathToVirtualPath(this, userinfo.Username)
  204. thisFileObject.Filename = filepath.Base(this)
  205. thisFileObject.Ext = filepath.Ext(this)
  206. thisFileObject.IsDir = fshAbs.IsDir(this)
  207. if thisFileObject.IsDir {
  208. //Check if this dir is empty
  209. filesInFolder, _ := fshAbs.Glob(filepath.ToSlash(filepath.Clean(this)) + "/*")
  210. fc := 0
  211. for _, f := range filesInFolder {
  212. if filepath.Base(f)[:1] != "." {
  213. fc++
  214. }
  215. }
  216. if fc > 0 {
  217. thisFileObject.IsEmptyDir = false
  218. } else {
  219. thisFileObject.IsEmptyDir = true
  220. }
  221. } else {
  222. //File object. Default true
  223. thisFileObject.IsEmptyDir = true
  224. }
  225. //Check if the file is a shortcut
  226. isShortcut := false
  227. if filepath.Ext(this) == ".shortcut" {
  228. isShortcut = true
  229. shortcutInfo, _ := fshAbs.ReadFile(this)
  230. infoSegments := strings.Split(strings.ReplaceAll(string(shortcutInfo), "\r\n", "\n"), "\n")
  231. if len(infoSegments) < 4 {
  232. thisFileObject.ShortcutType = "invalid"
  233. } else {
  234. thisFileObject.ShortcutType = infoSegments[0]
  235. thisFileObject.ShortcutName = infoSegments[1]
  236. thisFileObject.ShortcutPath = infoSegments[2]
  237. thisFileObject.ShortcutImage = infoSegments[3]
  238. }
  239. }
  240. thisFileObject.IsShortcut = isShortcut
  241. //Check if this file is shared
  242. thisFileObject.IsShared = shareManager.FileIsShared(userinfo, this)
  243. //Check the file location
  244. username, _ := authAgent.GetUserName(w, r)
  245. x, y, _ := getDesktopLocatioFromPath(thisFileObject.Filename, username)
  246. //This file already have a location on desktop
  247. thisFileObject.IconX = x
  248. thisFileObject.IconY = y
  249. desktopFiles = append(desktopFiles, *thisFileObject)
  250. }
  251. //Convert the struct to json string
  252. jsonString, _ := json.Marshal(desktopFiles)
  253. common.SendJSONResponse(w, string(jsonString))
  254. }
  255. //functions to handle desktop icon locations. Location is directly written into the center db.
  256. func getDesktopLocatioFromPath(filename string, username string) (int, int, error) {
  257. //As path include username, there is no different if there are username in the key
  258. locationdata := ""
  259. err := sysdb.Read("desktop", username+"/filelocation/"+filename, &locationdata)
  260. if err != nil {
  261. //The file location is not set. Return error
  262. return -1, -1, errors.New("This file do not have a location registry")
  263. }
  264. type iconLocation struct {
  265. X int
  266. Y int
  267. }
  268. thisFileLocation := iconLocation{
  269. X: -1,
  270. Y: -1,
  271. }
  272. //Start parsing the from the json data
  273. json.Unmarshal([]byte(locationdata), &thisFileLocation)
  274. return thisFileLocation.X, thisFileLocation.Y, nil
  275. }
  276. //Set the icon location of a given filepath
  277. func setDesktopLocationFromPath(filename string, username string, x int, y int) error {
  278. //You cannot directly set path of others people's deskop. Hence, fullpath needed to be parsed from auth username
  279. userinfo, _ := userHandler.GetUserInfoFromUsername(username)
  280. fsh, subpath, _ := GetFSHandlerSubpathFromVpath("user:/Desktop/")
  281. fshAbs := fsh.FileSystemAbstraction
  282. desktoppath, _ := fshAbs.VirtualPathToRealPath(subpath, userinfo.Username)
  283. path := filepath.Join(desktoppath, filename)
  284. type iconLocation struct {
  285. X int
  286. Y int
  287. }
  288. newLocation := new(iconLocation)
  289. newLocation.X = x
  290. newLocation.Y = y
  291. //Check if the file exits
  292. if fshAbs.FileExists(path) == false {
  293. return errors.New("Given filename not exists.")
  294. }
  295. //Parse the location to json
  296. jsonstring, err := json.Marshal(newLocation)
  297. if err != nil {
  298. log.Fatal("Unable to parse new file location on desktop for file: " + path)
  299. return err
  300. }
  301. //systemWideLogger.PrintAndLog(key,string(jsonstring),nil)
  302. //Write result to database
  303. sysdb.Write("desktop", username+"/filelocation/"+filename, string(jsonstring))
  304. return nil
  305. }
  306. func delDesktopLocationFromPath(filename string, username string) {
  307. //Delete a file icon location from db
  308. sysdb.Delete("desktop", username+"/filelocation/"+filename)
  309. }
  310. //Return the user information to the client
  311. func desktop_handleUserInfo(w http.ResponseWriter, r *http.Request) {
  312. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  313. if err != nil {
  314. common.SendErrorResponse(w, err.Error())
  315. return
  316. }
  317. type returnStruct struct {
  318. Username string
  319. UserIcon string
  320. UserGroups []string
  321. IsAdmin bool
  322. StorageQuotaTotal int64
  323. StorageQuotaLeft int64
  324. }
  325. //Calculate the storage quota left
  326. remainingQuota := userinfo.StorageQuota.TotalStorageQuota - userinfo.StorageQuota.UsedStorageQuota
  327. if userinfo.StorageQuota.TotalStorageQuota == -1 {
  328. remainingQuota = -1
  329. }
  330. //Get the list of user permission group names
  331. pgs := []string{}
  332. for _, pg := range userinfo.GetUserPermissionGroup() {
  333. pgs = append(pgs, pg.Name)
  334. }
  335. jsonString, _ := json.Marshal(returnStruct{
  336. Username: userinfo.Username,
  337. UserIcon: userinfo.GetUserIcon(),
  338. IsAdmin: userinfo.IsAdmin(),
  339. UserGroups: pgs,
  340. StorageQuotaTotal: userinfo.StorageQuota.GetUserStorageQuota(),
  341. StorageQuotaLeft: remainingQuota,
  342. })
  343. common.SendJSONResponse(w, string(jsonString))
  344. }
  345. //Icon handling function for web endpoint
  346. func desktop_fileLocation_handler(w http.ResponseWriter, r *http.Request) {
  347. get, _ := common.Mv(r, "get", true) //Check if there are get request for a given filepath
  348. set, _ := common.Mv(r, "set", true) //Check if there are any set request for a given filepath
  349. del, _ := common.Mv(r, "del", true) //Delete the given filename coordinate
  350. if set != "" {
  351. //Set location with given paramter
  352. x := 0
  353. y := 0
  354. sx, _ := common.Mv(r, "x", true)
  355. sy, _ := common.Mv(r, "y", true)
  356. path := set
  357. x, err := strconv.Atoi(sx)
  358. if err != nil {
  359. x = 0
  360. }
  361. y, err = strconv.Atoi(sy)
  362. if err != nil {
  363. y = 0
  364. }
  365. //Set location of icon from path
  366. username, _ := authAgent.GetUserName(w, r)
  367. err = setDesktopLocationFromPath(path, username, x, y)
  368. if err != nil {
  369. common.SendErrorResponse(w, err.Error())
  370. return
  371. }
  372. common.SendJSONResponse(w, string("\"OK\""))
  373. } else if get != "" {
  374. username, _ := authAgent.GetUserName(w, r)
  375. x, y, _ := getDesktopLocatioFromPath(get, username)
  376. result := []int{x, y}
  377. json_string, _ := json.Marshal(result)
  378. common.SendJSONResponse(w, string(json_string))
  379. } else if del != "" {
  380. username, _ := authAgent.GetUserName(w, r)
  381. delDesktopLocationFromPath(del, username)
  382. } else {
  383. //No argument has been set
  384. common.SendJSONResponse(w, "Paramter missing.")
  385. }
  386. }
  387. //////////////////////////////// END OF DESKTOP FILE ICON HANDLER ///////////////////////////////////////////////////
  388. func desktop_theme_handler(w http.ResponseWriter, r *http.Request) {
  389. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  390. if err != nil {
  391. common.SendErrorResponse(w, "User not logged in")
  392. return
  393. }
  394. username := userinfo.Username
  395. //Check if the set GET paramter is set.
  396. targetTheme, _ := common.Mv(r, "set", false)
  397. getUserTheme, _ := common.Mv(r, "get", false)
  398. loadUserTheme, _ := common.Mv(r, "load", false)
  399. if targetTheme == "" && getUserTheme == "" && loadUserTheme == "" {
  400. //List all the currnet themes in the list
  401. themes, err := filepath.Glob("web/img/desktop/bg/*")
  402. if err != nil {
  403. log.Fatal("Error. Unable to search bg from destkop image root. Are you sure the web data folder exists?")
  404. return
  405. }
  406. //Prase the results to json array
  407. //Tips: You must use captial letter for varable in struct that is accessable as public :)
  408. type desktopTheme struct {
  409. Theme string
  410. Bglist []string
  411. }
  412. var desktopThemeList []desktopTheme
  413. acceptBGFormats := []string{
  414. ".jpg",
  415. ".png",
  416. ".gif",
  417. }
  418. for _, file := range themes {
  419. if fs.IsDir(file) {
  420. thisTheme := new(desktopTheme)
  421. thisTheme.Theme = filepath.Base(file)
  422. bglist, _ := filepath.Glob(file + "/*")
  423. var thisbglist []string
  424. for _, bg := range bglist {
  425. ext := filepath.Ext(bg)
  426. //if (sliceutil.Contains(acceptBGFormats, ext) ){
  427. if common.StringInArray(acceptBGFormats, ext) {
  428. //This file extension is supported
  429. thisbglist = append(thisbglist, filepath.Base(bg))
  430. }
  431. }
  432. thisTheme.Bglist = thisbglist
  433. desktopThemeList = append(desktopThemeList, *thisTheme)
  434. }
  435. }
  436. //Return the results as JSON string
  437. jsonString, err := json.Marshal(desktopThemeList)
  438. if err != nil {
  439. log.Fatal(err)
  440. }
  441. common.SendJSONResponse(w, string(jsonString))
  442. return
  443. } else if getUserTheme == "true" {
  444. //Get the user's theme from database
  445. result := ""
  446. sysdb.Read("desktop", username+"/theme", &result)
  447. if result == "" {
  448. //This user has not set a theme yet. Use default
  449. common.SendJSONResponse(w, string("\"default\""))
  450. return
  451. } else {
  452. //This user already set a theme. Use its set theme
  453. common.SendJSONResponse(w, string("\""+result+"\""))
  454. return
  455. }
  456. } else if loadUserTheme != "" {
  457. //Load user theme base on folder path
  458. userFsh, err := GetFsHandlerByUUID("user:/")
  459. if err != nil {
  460. common.SendErrorResponse(w, "Unable to resolve user root path")
  461. return
  462. }
  463. userFshAbs := userFsh.FileSystemAbstraction
  464. rpath, err := userFshAbs.VirtualPathToRealPath(loadUserTheme, userinfo.Username)
  465. if err != nil {
  466. common.SendErrorResponse(w, "Custom folder load failed")
  467. return
  468. }
  469. //Check if the folder exists
  470. if !userFshAbs.FileExists(rpath) {
  471. common.SendErrorResponse(w, "Custom folder load failed")
  472. return
  473. }
  474. if userinfo.CanRead(loadUserTheme) == false {
  475. //No read permission
  476. common.SendErrorResponse(w, "Permission denied")
  477. return
  478. }
  479. //Scan for jpg, gif or png
  480. imageList := []string{}
  481. scanPath := filepath.ToSlash(filepath.Clean(rpath)) + "/"
  482. pngFiles, _ := filepath.Glob(scanPath + "*.png")
  483. jpgFiles, _ := filepath.Glob(scanPath + "*.jpg")
  484. gifFiles, _ := filepath.Glob(scanPath + "*.gif")
  485. //Merge all 3 slice into one image list
  486. imageList = append(imageList, pngFiles...)
  487. imageList = append(imageList, jpgFiles...)
  488. imageList = append(imageList, gifFiles...)
  489. //Convert the image list back to vpaths
  490. virtualImageList := []string{}
  491. for _, image := range imageList {
  492. vpath, err := userFshAbs.RealPathToVirtualPath(image, userinfo.Username)
  493. if err != nil {
  494. continue
  495. }
  496. virtualImageList = append(virtualImageList, vpath)
  497. }
  498. js, _ := json.Marshal(virtualImageList)
  499. common.SendJSONResponse(w, string(js))
  500. } else if targetTheme != "" {
  501. //Set the current user theme
  502. sysdb.Write("desktop", username+"/theme", targetTheme)
  503. common.SendJSONResponse(w, "\"OK\"")
  504. return
  505. }
  506. }
  507. func desktop_preference_handler(w http.ResponseWriter, r *http.Request) {
  508. preferenceType, _ := common.Mv(r, "preference", true)
  509. value, _ := common.Mv(r, "value", true)
  510. remove, _ := common.Mv(r, "remove", true)
  511. username, err := authAgent.GetUserName(w, r)
  512. if err != nil {
  513. //user not logged in. Redirect to login page.
  514. common.SendErrorResponse(w, "User not logged in")
  515. return
  516. }
  517. if preferenceType == "" && value == "" {
  518. //Invalid options. Return error reply.
  519. common.SendErrorResponse(w, "Error. Undefined paramter.")
  520. return
  521. } else if preferenceType != "" && value == "" && remove == "" {
  522. //Getting config from the key.
  523. result := ""
  524. sysdb.Read("desktop", username+"/preference/"+preferenceType, &result)
  525. jsonString, _ := json.Marshal(result)
  526. common.SendJSONResponse(w, string(jsonString))
  527. return
  528. } else if preferenceType != "" && value == "" && remove == "true" {
  529. //Remove mode
  530. sysdb.Delete("desktop", username+"/preference/"+preferenceType)
  531. common.SendOK(w)
  532. return
  533. } else if preferenceType != "" && value != "" {
  534. //Setting config from the key
  535. sysdb.Write("desktop", username+"/preference/"+preferenceType, value)
  536. common.SendOK(w)
  537. return
  538. } else {
  539. common.SendErrorResponse(w, "Error. Undefined paramter.")
  540. return
  541. }
  542. }
  543. func desktop_shortcutHandler(w http.ResponseWriter, r *http.Request) {
  544. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  545. if err != nil {
  546. //user not logged in. Redirect to login page.
  547. common.SendErrorResponse(w, "User not logged in")
  548. return
  549. }
  550. shortcutType, err := common.Mv(r, "stype", true)
  551. if err != nil {
  552. common.SendErrorResponse(w, err.Error())
  553. return
  554. }
  555. shortcutText, err := common.Mv(r, "stext", true)
  556. if err != nil {
  557. common.SendErrorResponse(w, err.Error())
  558. return
  559. }
  560. shortcutPath, err := common.Mv(r, "spath", true)
  561. if err != nil {
  562. common.SendErrorResponse(w, err.Error())
  563. return
  564. }
  565. shortcutIcon, err := common.Mv(r, "sicon", true)
  566. if err != nil {
  567. common.SendErrorResponse(w, err.Error())
  568. return
  569. }
  570. shortcutCreationDest, err := common.Mv(r, "sdest", true)
  571. if err != nil {
  572. //Default create on desktop
  573. shortcutCreationDest = "user:/Desktop/"
  574. }
  575. if !userinfo.CanWrite(shortcutCreationDest) {
  576. common.SendErrorResponse(w, "Permission denied")
  577. return
  578. }
  579. //Resolve vpath to fsh and subpath
  580. fsh, subpath, err := GetFSHandlerSubpathFromVpath(shortcutCreationDest)
  581. if err != nil {
  582. common.SendErrorResponse(w, err.Error())
  583. return
  584. }
  585. fshAbs := fsh.FileSystemAbstraction
  586. shorcutRealDest, err := fshAbs.VirtualPathToRealPath(subpath, userinfo.Username)
  587. if err != nil {
  588. common.SendErrorResponse(w, err.Error())
  589. return
  590. }
  591. //Filter illegal characters in the shortcut filename
  592. shortcutText = arozfs.FilterIllegalCharInFilename(shortcutText, " ")
  593. //If dest not exists, create it
  594. if !fshAbs.FileExists(shorcutRealDest) {
  595. fshAbs.MkdirAll(shorcutRealDest, 0755)
  596. }
  597. //Generate a filename for the shortcut
  598. shortcutFilename := shorcutRealDest + "/" + shortcutText + ".shortcut"
  599. counter := 1
  600. for fshAbs.FileExists(shortcutFilename) {
  601. shortcutFilename = shorcutRealDest + "/" + shortcutText + "(" + strconv.Itoa(counter) + ")" + ".shortcut"
  602. counter++
  603. }
  604. //Write the shortcut to file
  605. shortcutContent := shortcut.GenerateShortcutBytes(shortcutPath, shortcutType, shortcutText, shortcutIcon)
  606. err = fshAbs.WriteFile(shortcutFilename, shortcutContent, 0775)
  607. if err != nil {
  608. common.SendErrorResponse(w, err.Error())
  609. return
  610. }
  611. common.SendOK(w)
  612. }