desktop.go 15 KB

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