desktop.go 18 KB

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