| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659 | package mainimport (	"encoding/json"	"errors"	"io/ioutil"	"log"	"net/http"	"os"	"path/filepath"	"strconv"	"strings"	"imuslab.com/arozos/mod/common"	fs "imuslab.com/arozos/mod/filesystem"	module "imuslab.com/arozos/mod/modules"	prout "imuslab.com/arozos/mod/prouter")//Desktop script initiationfunc DesktopInit() {	log.Println("Starting Desktop Services")	router := prout.NewModuleRouter(prout.RouterOption{		ModuleName:  "Desktop",		AdminOnly:   false,		UserHandler: userHandler,		DeniedHandler: func(w http.ResponseWriter, r *http.Request) {			common.SendErrorResponse(w, "Permission Denied")		},	})	//Register all the required API	router.HandleFunc("/system/desktop/listDesktop", desktop_listFiles)	router.HandleFunc("/system/desktop/theme", desktop_theme_handler)	router.HandleFunc("/system/desktop/files", desktop_fileLocation_handler)	router.HandleFunc("/system/desktop/host", desktop_hostdetailHandler)	router.HandleFunc("/system/desktop/user", desktop_handleUserInfo)	router.HandleFunc("/system/desktop/preference", desktop_preference_handler)	router.HandleFunc("/system/desktop/createShortcut", desktop_shortcutHandler)	//API related to desktop based operations	router.HandleFunc("/system/desktop/opr/renameShortcut", desktop_handleShortcutRename)	//Initialize desktop database	err := sysdb.NewTable("desktop")	if err != nil {		log.Fatal(err)		os.Exit(1)	}	//Register Desktop Module	moduleHandler.RegisterModule(module.ModuleInfo{		Name:        "Desktop",		Desc:        "The Web Desktop experience for everyone",		Group:       "Interface Module",		IconPath:    "img/desktop/desktop.png",		Version:     internal_version,		StartDir:    "",		SupportFW:   false,		LaunchFWDir: "",		SupportEmb:  false,	})}/*	FUNCTIONS RELATED TO PARSING DESKTOP FILE ICONS	The functions in this section handle file listing and its icon locations.*/func desktop_initUserFolderStructure(username string) {	//Call to filesystem for creating user file struture at root dir	userinfo, _ := userHandler.GetUserInfoFromUsername(username)	homedir, err := userinfo.GetHomeDirectory()	if err != nil {		log.Println(err)	}	if !fs.FileExists(filepath.Join(homedir, "Desktop")) {		//Desktop directory not exists. Create one and copy a template desktop		os.MkdirAll(homedir+"Desktop", 0755)		templateFolder := "./system/desktop/template/"		if fs.FileExists(templateFolder) {			templateFiles, _ := filepath.Glob(templateFolder + "*")			for _, tfile := range templateFiles {				input, _ := ioutil.ReadFile(tfile)				ioutil.WriteFile(homedir+"Desktop/"+filepath.Base(tfile), input, 0755)			}		}	}}//Return the information about the hostfunc desktop_hostdetailHandler(w http.ResponseWriter, r *http.Request) {	type returnStruct struct {		Hostname        string		DeviceUUID      string		BuildVersion    string		InternalVersion string		DeviceVendor    string		DeviceModel     string		VendorIcon      string	}	jsonString, _ := json.Marshal(returnStruct{		Hostname:        *host_name,		DeviceUUID:      deviceUUID,		BuildVersion:    build_version,		InternalVersion: internal_version,		DeviceVendor:    deviceVendor,		DeviceModel:     deviceModel,		VendorIcon:      iconVendor,	})	common.SendJSONResponse(w, string(jsonString))}func desktop_handleShortcutRename(w http.ResponseWriter, r *http.Request) {	//Check if the user directory already exists	userinfo, err := userHandler.GetUserInfoFromRequest(w, r)	if err != nil {		common.SendErrorResponse(w, "User not logged in")		return	}	//Get the shortcut file that is renaming	target, err := common.Mv(r, "src", false)	if err != nil {		common.SendErrorResponse(w, "Invalid shortcut file path given")		return	}	//Get the new name	new, err := common.Mv(r, "new", false)	if err != nil {		common.SendErrorResponse(w, "Invalid new name given")		return	}	//Check if the file actually exists and it is on desktop	rpath, err := userinfo.VirtualPathToRealPath(target)	if err != nil {		common.SendErrorResponse(w, err.Error())		return	}	if target[:14] != "user:/Desktop/" {		common.SendErrorResponse(w, "Shortcut not on desktop")		return	}	if !fs.FileExists(rpath) {		common.SendErrorResponse(w, "File not exists")		return	}	//OK. Change the name of the shortcut	originalShortcut, err := ioutil.ReadFile(rpath)	if err != nil {		common.SendErrorResponse(w, "Shortcut file read failed")		return	}	lines := strings.Split(string(originalShortcut), "\n")	if len(lines) < 4 {		//Invalid shortcut properties		common.SendErrorResponse(w, "Invalid shortcut file")		return	}	//Change the 2nd line to the new name	lines[1] = new	newShortcutContent := strings.Join(lines, "\n")	err = ioutil.WriteFile(rpath, []byte(newShortcutContent), 0755)	if err != nil {		common.SendErrorResponse(w, err.Error())		return	}	common.SendOK(w)}func desktop_listFiles(w http.ResponseWriter, r *http.Request) {	//Check if the user directory already exists	userinfo, _ := userHandler.GetUserInfoFromRequest(w, r)	username := userinfo.Username	//Initiate the user folder structure. Do nothing if the structure already exists.	desktop_initUserFolderStructure(username)	//List all files inside the user desktop directory	userDesktopRealpath, _ := userinfo.VirtualPathToRealPath("user:/Desktop/")	files, err := filepath.Glob(userDesktopRealpath + "/*")	if err != nil {		log.Fatal("Error. Desktop unable to load user files for :" + username)		return	}	//Desktop object structure	type desktopObject struct {		Filepath      string		Filename      string		Ext           string		IsDir         bool		IsEmptyDir    bool		IsShortcut    bool		IsShared      bool		ShortcutImage string		ShortcutType  string		ShortcutName  string		ShortcutPath  string		IconX         int		IconY         int	}	var desktopFiles []desktopObject	for _, this := range files {		//Always use linux convension for directory seperator		if filepath.Base(this)[:1] == "." {			//Skipping hidden files			continue		}		this = filepath.ToSlash(this)		thisFileObject := new(desktopObject)		thisFileObject.Filepath, _ = userinfo.RealPathToVirtualPath(this)		thisFileObject.Filename = filepath.Base(this)		thisFileObject.Ext = filepath.Ext(this)		thisFileObject.IsDir = fs.IsDir(this)		if thisFileObject.IsDir {			//Check if this dir is empty			filesInFolder, _ := filepath.Glob(filepath.ToSlash(filepath.Clean(this)) + "/*")			fc := 0			for _, f := range filesInFolder {				if filepath.Base(f)[:1] != "." {					fc++				}			}			if fc > 0 {				thisFileObject.IsEmptyDir = false			} else {				thisFileObject.IsEmptyDir = true			}		} else {			//File object. Default true			thisFileObject.IsEmptyDir = true		}		//Check if the file is a shortcut		isShortcut := false		if filepath.Ext(this) == ".shortcut" {			isShortcut = true			shortcutInfo, _ := ioutil.ReadFile(this)			infoSegments := strings.Split(strings.ReplaceAll(string(shortcutInfo), "\r\n", "\n"), "\n")			if len(infoSegments) < 4 {				thisFileObject.ShortcutType = "invalid"			} else {				thisFileObject.ShortcutType = infoSegments[0]				thisFileObject.ShortcutName = infoSegments[1]				thisFileObject.ShortcutPath = infoSegments[2]				thisFileObject.ShortcutImage = infoSegments[3]			}		}		thisFileObject.IsShortcut = isShortcut		//Check if this file is shared		thisFileObject.IsShared = shareManager.FileIsShared(this)		//Check the file location		username, _ := authAgent.GetUserName(w, r)		x, y, _ := getDesktopLocatioFromPath(thisFileObject.Filename, username)		//This file already have a location on desktop		thisFileObject.IconX = x		thisFileObject.IconY = y		desktopFiles = append(desktopFiles, *thisFileObject)	}	//Convert the struct to json string	jsonString, _ := json.Marshal(desktopFiles)	common.SendJSONResponse(w, string(jsonString))}//functions to handle desktop icon locations. Location is directly written into the center db.func getDesktopLocatioFromPath(filename string, username string) (int, int, error) {	//As path include username, there is no different if there are username in the key	locationdata := ""	err := sysdb.Read("desktop", username+"/filelocation/"+filename, &locationdata)	if err != nil {		//The file location is not set. Return error		return -1, -1, errors.New("This file do not have a location registry")	}	type iconLocation struct {		X int		Y int	}	thisFileLocation := iconLocation{		X: -1,		Y: -1,	}	//Start parsing the from the json data	json.Unmarshal([]byte(locationdata), &thisFileLocation)	return thisFileLocation.X, thisFileLocation.Y, nil}//Set the icon location of a given filepathfunc setDesktopLocationFromPath(filename string, username string, x int, y int) error {	//You cannot directly set path of others people's deskop. Hence, fullpath needed to be parsed from auth username	userinfo, _ := userHandler.GetUserInfoFromUsername(username)	desktoppath, _ := userinfo.VirtualPathToRealPath("user:/Desktop/")	path := filepath.Join(desktoppath, filename)	type iconLocation struct {		X int		Y int	}	newLocation := new(iconLocation)	newLocation.X = x	newLocation.Y = y	//Check if the file exits	if fs.FileExists(path) == false {		return errors.New("Given filename not exists.")	}	//Parse the location to json	jsonstring, err := json.Marshal(newLocation)	if err != nil {		log.Fatal("Unable to parse new file location on desktop for file: " + path)		return err	}	//log.Println(key,string(jsonstring))	//Write result to database	sysdb.Write("desktop", username+"/filelocation/"+filename, string(jsonstring))	return nil}func delDesktopLocationFromPath(filename string, username string) {	//Delete a file icon location from db	sysdb.Delete("desktop", username+"/filelocation/"+filename)}//Return the user information to the clientfunc desktop_handleUserInfo(w http.ResponseWriter, r *http.Request) {	userinfo, err := userHandler.GetUserInfoFromRequest(w, r)	if err != nil {		common.SendErrorResponse(w, err.Error())		return	}	type returnStruct struct {		Username          string		UserIcon          string		UserGroups        []string		IsAdmin           bool		StorageQuotaTotal int64		StorageQuotaLeft  int64	}	//Calculate the storage quota left	remainingQuota := userinfo.StorageQuota.TotalStorageQuota - userinfo.StorageQuota.UsedStorageQuota	if userinfo.StorageQuota.TotalStorageQuota == -1 {		remainingQuota = -1	}	//Get the list of user permission group names	pgs := []string{}	for _, pg := range userinfo.GetUserPermissionGroup() {		pgs = append(pgs, pg.Name)	}	jsonString, _ := json.Marshal(returnStruct{		Username:          userinfo.Username,		UserIcon:          userinfo.GetUserIcon(),		IsAdmin:           userinfo.IsAdmin(),		UserGroups:        pgs,		StorageQuotaTotal: userinfo.StorageQuota.GetUserStorageQuota(),		StorageQuotaLeft:  remainingQuota,	})	common.SendJSONResponse(w, string(jsonString))}//Icon handling function for web endpointfunc desktop_fileLocation_handler(w http.ResponseWriter, r *http.Request) {	get, _ := common.Mv(r, "get", true) //Check if there are get request for a given filepath	set, _ := common.Mv(r, "set", true) //Check if there are any set request for a given filepath	del, _ := common.Mv(r, "del", true) //Delete the given filename coordinate	if set != "" {		//Set location with given paramter		x := 0		y := 0		sx, _ := common.Mv(r, "x", true)		sy, _ := common.Mv(r, "y", true)		path := set		x, err := strconv.Atoi(sx)		if err != nil {			x = 0		}		y, err = strconv.Atoi(sy)		if err != nil {			y = 0		}		//Set location of icon from path		username, _ := authAgent.GetUserName(w, r)		err = setDesktopLocationFromPath(path, username, x, y)		if err != nil {			common.SendErrorResponse(w, err.Error())			return		}		common.SendJSONResponse(w, string("\"OK\""))	} else if get != "" {		username, _ := authAgent.GetUserName(w, r)		x, y, _ := getDesktopLocatioFromPath(get, username)		result := []int{x, y}		json_string, _ := json.Marshal(result)		common.SendJSONResponse(w, string(json_string))	} else if del != "" {		username, _ := authAgent.GetUserName(w, r)		delDesktopLocationFromPath(del, username)	} else {		//No argument has been set		common.SendJSONResponse(w, "Paramter missing.")	}}////////////////////////////////   END OF DESKTOP FILE ICON HANDLER ///////////////////////////////////////////////////func desktop_theme_handler(w http.ResponseWriter, r *http.Request) {	userinfo, err := userHandler.GetUserInfoFromRequest(w, r)	if err != nil {		common.SendErrorResponse(w, "User not logged in")		return	}	username := userinfo.Username	//Check if the set GET paramter is set.	targetTheme, _ := common.Mv(r, "set", false)	getUserTheme, _ := common.Mv(r, "get", false)	loadUserTheme, _ := common.Mv(r, "load", false)	if targetTheme == "" && getUserTheme == "" && loadUserTheme == "" {		//List all the currnet themes in the list		themes, err := filepath.Glob("web/img/desktop/bg/*")		if err != nil {			log.Fatal("Error. Unable to search bg from destkop image root. Are you sure the web data folder exists?")			return		}		//Prase the results to json array		//Tips: You must use captial letter for varable in struct that is accessable as public :)		type desktopTheme struct {			Theme  string			Bglist []string		}		var desktopThemeList []desktopTheme		acceptBGFormats := []string{			".jpg",			".png",			".gif",		}		for _, file := range themes {			if fs.IsDir(file) {				thisTheme := new(desktopTheme)				thisTheme.Theme = filepath.Base(file)				bglist, _ := filepath.Glob(file + "/*")				var thisbglist []string				for _, bg := range bglist {					ext := filepath.Ext(bg)					//if (sliceutil.Contains(acceptBGFormats, ext) ){					if common.StringInArray(acceptBGFormats, ext) {						//This file extension is supported						thisbglist = append(thisbglist, filepath.Base(bg))					}				}				thisTheme.Bglist = thisbglist				desktopThemeList = append(desktopThemeList, *thisTheme)			}		}		//Return the results as JSON string		jsonString, err := json.Marshal(desktopThemeList)		if err != nil {			log.Fatal(err)		}		common.SendJSONResponse(w, string(jsonString))		return	} else if getUserTheme == "true" {		//Get the user's theme from database		result := ""		sysdb.Read("desktop", username+"/theme", &result)		if result == "" {			//This user has not set a theme yet. Use default			common.SendJSONResponse(w, string("\"default\""))			return		} else {			//This user already set a theme. Use its set theme			common.SendJSONResponse(w, string("\""+result+"\""))			return		}	} else if loadUserTheme != "" {		//Load user theme base on folder path		rpath, err := userinfo.VirtualPathToRealPath(loadUserTheme)		if err != nil {			common.SendErrorResponse(w, "Custom folder load failed")			return		}		//Check if the folder exists		if !fs.FileExists(rpath) {			common.SendErrorResponse(w, "Custom folder load failed")			return		}		if userinfo.CanRead(loadUserTheme) == false {			//No read permission			common.SendErrorResponse(w, "Permission denied")			return		}		//Scan for jpg, gif or png		imageList := []string{}		scanPath := filepath.ToSlash(filepath.Clean(rpath)) + "/"		pngFiles, _ := filepath.Glob(scanPath + "*.png")		jpgFiles, _ := filepath.Glob(scanPath + "*.jpg")		gifFiles, _ := filepath.Glob(scanPath + "*.gif")		//Merge all 3 slice into one image list		imageList = append(imageList, pngFiles...)		imageList = append(imageList, jpgFiles...)		imageList = append(imageList, gifFiles...)		//Convert the image list back to vpaths		virtualImageList := []string{}		for _, image := range imageList {			vpath, err := userinfo.RealPathToVirtualPath(image)			if err != nil {				continue			}			virtualImageList = append(virtualImageList, vpath)		}		js, _ := json.Marshal(virtualImageList)		common.SendJSONResponse(w, string(js))	} else if targetTheme != "" {		//Set the current user theme		sysdb.Write("desktop", username+"/theme", targetTheme)		common.SendJSONResponse(w, "\"OK\"")		return	}}func desktop_preference_handler(w http.ResponseWriter, r *http.Request) {	preferenceType, _ := common.Mv(r, "preference", true)	value, _ := common.Mv(r, "value", true)	remove, _ := common.Mv(r, "remove", true)	username, err := authAgent.GetUserName(w, r)	if err != nil {		//user not logged in. Redirect to login page.		common.SendErrorResponse(w, "User not logged in")		return	}	if preferenceType == "" && value == "" {		//Invalid options. Return error reply.		common.SendErrorResponse(w, "Error. Undefined paramter.")		return	} else if preferenceType != "" && value == "" && remove == "" {		//Getting config from the key.		result := ""		sysdb.Read("desktop", username+"/preference/"+preferenceType, &result)		jsonString, _ := json.Marshal(result)		common.SendJSONResponse(w, string(jsonString))		return	} else if preferenceType != "" && value == "" && remove == "true" {		//Remove mode		sysdb.Delete("desktop", username+"/preference/"+preferenceType)		common.SendOK(w)		return	} else if preferenceType != "" && value != "" {		//Setting config from the key		sysdb.Write("desktop", username+"/preference/"+preferenceType, value)		common.SendOK(w)		return	} else {		common.SendErrorResponse(w, "Error. Undefined paramter.")		return	}}func desktop_shortcutHandler(w http.ResponseWriter, r *http.Request) {	username, err := authAgent.GetUserName(w, r)	if err != nil {		//user not logged in. Redirect to login page.		common.SendErrorResponse(w, "User not logged in")		return	}	userinfo, _ := userHandler.GetUserInfoFromUsername(username)	shortcutType, err := common.Mv(r, "stype", true)	if err != nil {		common.SendErrorResponse(w, err.Error())		return	}	shortcutText, err := common.Mv(r, "stext", true)	if err != nil {		common.SendErrorResponse(w, err.Error())		return	}	shortcutPath, err := common.Mv(r, "spath", true)	if err != nil {		common.SendErrorResponse(w, err.Error())		return	}	shortcutIcon, err := common.Mv(r, "sicon", true)	if err != nil {		common.SendErrorResponse(w, err.Error())		return	}	//OK to proceed. Generate a shortcut on the user desktop	userDesktopPath, _ := userinfo.VirtualPathToRealPath("user:/Desktop")	if !fs.FileExists(userDesktopPath) {		os.MkdirAll(userDesktopPath, 0755)	}	//Check if there are desktop icon. If yes, override icon on module	if shortcutType == "module" && fs.FileExists("./web/"+filepath.ToSlash(filepath.Dir(shortcutIcon)+"/desktop_icon.png")) {		shortcutIcon = filepath.ToSlash(filepath.Join(filepath.Dir(shortcutIcon), "/desktop_icon.png"))	}	shortcutText = strings.ReplaceAll(shortcutText, "/", "")	for strings.Contains(shortcutText, "../") {		shortcutText = strings.ReplaceAll(shortcutText, "../", "")	}	shortcutFilename := userDesktopPath + "/" + shortcutText + ".shortcut"	counter := 1	for fs.FileExists(shortcutFilename) {		shortcutFilename = userDesktopPath + "/" + shortcutText + "(" + strconv.Itoa(counter) + ")" + ".shortcut"		counter++	}	err = ioutil.WriteFile(shortcutFilename, []byte(shortcutType+"\n"+shortcutText+"\n"+shortcutPath+"\n"+shortcutIcon), 0755)	if err != nil {		common.SendErrorResponse(w, err.Error())		return	}	common.SendOK(w)}
 |