desktop.go 19 KB

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