module.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. package modules
  2. import (
  3. "encoding/json"
  4. "net/http"
  5. "sort"
  6. "strings"
  7. "imuslab.com/arozos/mod/user"
  8. "imuslab.com/arozos/mod/utils"
  9. )
  10. type ModuleInfo struct {
  11. Name string //Name of this module. e.g. "Audio"
  12. Desc string //Description for this module
  13. Group string //Group of the module, e.g. "system" / "media" etc
  14. IconPath string //Module icon image path e.g. "Audio/img/function_icon.png"
  15. Version string //Version of the module. Format: [0-9]*.[0-9][0-9].[0-9]
  16. StartDir string //Default starting dir, e.g. "Audio/index.html"
  17. SupportFW bool //Support floatWindow. If yes, floatWindow dir will be loaded
  18. LaunchFWDir string //This link will be launched instead of 'StartDir' if fw mode
  19. SupportEmb bool //Support embedded mode
  20. LaunchEmb string //This link will be launched instead of StartDir / Fw if a file is opened with this module
  21. InitFWSize []int //Floatwindow init size. [0] => Width, [1] => Height
  22. InitEmbSize []int //Embedded mode init size. [0] => Width, [1] => Height
  23. SupportedExt []string //Supported File Extensions. e.g. ".mp3", ".flac", ".wav"
  24. //Hidden properties
  25. allowReload bool //Allow module reload by user
  26. }
  27. type ModuleHandler struct {
  28. LoadedModule []*ModuleInfo
  29. userHandler *user.UserHandler
  30. tmpDirectory string
  31. extIconRegistry map[string]string // lowercased ext (no dot) --> web-root-relative icon path
  32. // OnModuleUninstall is an optional hook called when a module is uninstalled.
  33. // The hook receives the module name and can be used to clean up associated resources
  34. // (e.g. remove scheduled cron jobs registered by that module).
  35. OnModuleUninstall func(moduleName string)
  36. }
  37. func NewModuleHandler(userHandler *user.UserHandler, tmpFolderPath string) *ModuleHandler {
  38. return &ModuleHandler{
  39. LoadedModule: []*ModuleInfo{},
  40. userHandler: userHandler,
  41. tmpDirectory: tmpFolderPath,
  42. extIconRegistry: map[string]string{},
  43. }
  44. }
  45. // RegisterExtIcon stores a web-root-relative icon path for the given file extension.
  46. // ext may include or omit a leading dot; it is normalised to lowercase without dot.
  47. func (m *ModuleHandler) RegisterExtIcon(ext, iconPath string) {
  48. ext = strings.ToLower(strings.TrimPrefix(ext, "."))
  49. m.extIconRegistry[ext] = iconPath
  50. }
  51. // HandleGetExtIcons serves the extension-to-icon registry as a JSON object.
  52. func (m *ModuleHandler) HandleGetExtIcons(w http.ResponseWriter, r *http.Request) {
  53. js, _ := json.Marshal(m.extIconRegistry)
  54. utils.SendJSONResponse(w, string(js))
  55. }
  56. // Register endpoint. Provide moduleInfo datastructure or unparsed json
  57. func (m *ModuleHandler) RegisterModule(module ModuleInfo) {
  58. m.LoadedModule = append(m.LoadedModule, &module)
  59. //Add the module into universal module if it is utilities or system tools
  60. moduleGroupLowerCase := strings.ToLower(module.Group)
  61. if moduleGroupLowerCase == "utilities" || moduleGroupLowerCase == "system tools" {
  62. m.userHandler.UniversalModules = append(m.userHandler.UniversalModules, module.Name)
  63. }
  64. }
  65. // Sort the module list
  66. func (m *ModuleHandler) ModuleSortList() {
  67. sort.Slice(m.LoadedModule, func(i, j int) bool {
  68. return m.LoadedModule[i].Name < m.LoadedModule[j].Name
  69. })
  70. }
  71. // Register a module from JSON string
  72. func (m *ModuleHandler) RegisterModuleFromJSON(jsonstring string, allowReload bool) error {
  73. var thisModuleInfo ModuleInfo
  74. err := json.Unmarshal([]byte(jsonstring), &thisModuleInfo)
  75. if err != nil {
  76. return err
  77. }
  78. thisModuleInfo.allowReload = allowReload
  79. m.RegisterModule(thisModuleInfo)
  80. return nil
  81. }
  82. // Register a module from AGI script
  83. func (m *ModuleHandler) RegisterModuleFromAGI(jsonstring string) error {
  84. var thisModuleInfo ModuleInfo
  85. err := json.Unmarshal([]byte(jsonstring), &thisModuleInfo)
  86. if err != nil {
  87. return err
  88. }
  89. //AGI interface loaded module must allow runtime reload
  90. thisModuleInfo.allowReload = true
  91. m.RegisterModule(thisModuleInfo)
  92. return nil
  93. }
  94. func (m *ModuleHandler) DeregisterModule(moduleName string) {
  95. newLoadedModuleList := []*ModuleInfo{}
  96. for _, thisModule := range m.LoadedModule {
  97. if thisModule.Name != moduleName {
  98. newLoadedModuleList = append(newLoadedModuleList, thisModule)
  99. }
  100. }
  101. m.LoadedModule = newLoadedModuleList
  102. }
  103. // Get a list of module names
  104. func (m *ModuleHandler) GetModuleNameList() []string {
  105. result := []string{}
  106. for _, module := range m.LoadedModule {
  107. result = append(result, module.Name)
  108. }
  109. return result
  110. }
  111. // GetModuleListJSONForUser returns a JSON string of all modules the given username can access
  112. func (m *ModuleHandler) GetModuleListJSONForUser(username string) string {
  113. userinfo, err := m.userHandler.GetUserInfoFromUsername(username)
  114. if err != nil {
  115. return "[]"
  116. }
  117. accessable := []*ModuleInfo{}
  118. for _, mod := range m.LoadedModule {
  119. if userinfo.GetModuleAccessPermission(mod.Name) {
  120. accessable = append(accessable, mod)
  121. }
  122. }
  123. js, _ := json.Marshal(accessable)
  124. return string(js)
  125. }
  126. // Handle Default Launcher
  127. func (m *ModuleHandler) HandleDefaultLauncher(w http.ResponseWriter, r *http.Request) {
  128. username, _ := m.userHandler.GetAuthAgent().GetUserName(w, r)
  129. opr, _ := utils.GetPara(r, "opr") //Operation, accept {get, set, launch}
  130. ext, _ := utils.GetPara(r, "ext")
  131. moduleName, _ := utils.GetPara(r, "module")
  132. ext = strings.ToLower(ext)
  133. //Check if the default folder exists.
  134. switch opr {
  135. case "get":
  136. //Get the opener for this file type
  137. value := ""
  138. err := m.userHandler.GetDatabase().Read("module", "default/"+username+"/"+ext, &value)
  139. if err != nil {
  140. utils.SendErrorResponse(w, "No default opener")
  141. return
  142. }
  143. js, _ := json.Marshal(value)
  144. utils.SendJSONResponse(w, string(js))
  145. return
  146. case "launch":
  147. //Get launch paramter for this extension
  148. value := ""
  149. err := m.userHandler.GetDatabase().Read("module", "default/"+username+"/"+ext, &value)
  150. if err != nil {
  151. utils.SendErrorResponse(w, "No default opener")
  152. return
  153. }
  154. //Get the launch paramter of this module
  155. var modInfo *ModuleInfo = nil
  156. modExists := false
  157. for _, mod := range m.LoadedModule {
  158. if mod.Name == value {
  159. modInfo = mod
  160. modExists = true
  161. }
  162. }
  163. if !modExists {
  164. //This module has been removed or not exists anymore
  165. utils.SendErrorResponse(w, "Default opener no longer exists.")
  166. return
  167. } else {
  168. //Return launch inforamtion
  169. jsonString, _ := json.Marshal(modInfo)
  170. utils.SendJSONResponse(w, string(jsonString))
  171. }
  172. case "set":
  173. //Set the opener for this filetype
  174. if moduleName == "" {
  175. utils.SendErrorResponse(w, "Missing paratmer 'module'")
  176. return
  177. }
  178. //Check if module name exists
  179. moduleValid := false
  180. for _, mod := range m.LoadedModule {
  181. if mod.Name == moduleName {
  182. moduleValid = true
  183. }
  184. }
  185. if moduleValid {
  186. m.userHandler.GetDatabase().Write("module", "default/"+username+"/"+ext, moduleName)
  187. utils.SendJSONResponse(w, "\"OK\"")
  188. } else {
  189. utils.SendErrorResponse(w, "Given module not exists.")
  190. }
  191. case "list":
  192. //List all the values that belongs to default opener
  193. dbDump, _ := m.userHandler.GetDatabase().ListTable("module")
  194. results := [][]string{}
  195. for _, entry := range dbDump {
  196. key := string(entry[0])
  197. if strings.Contains(key, "default/"+username+"/") {
  198. //This is a correct matched entry
  199. extInfo := strings.Split(key, "/")
  200. ext := extInfo[len(extInfo)-1:]
  201. moduleName := ""
  202. json.Unmarshal(entry[1], &moduleName)
  203. results = append(results, []string{ext[0], moduleName})
  204. }
  205. }
  206. jsonString, _ := json.Marshal(results)
  207. utils.SendJSONResponse(w, string(jsonString))
  208. return
  209. }
  210. }
  211. func (m *ModuleHandler) ListLoadedModules(w http.ResponseWriter, r *http.Request) {
  212. userinfo, _ := m.userHandler.GetUserInfoFromRequest(w, r)
  213. ///Parse a list of modules where the user has permission to access
  214. userAccessableModules := []*ModuleInfo{}
  215. for _, thisModule := range m.LoadedModule {
  216. thisModuleName := thisModule.Name
  217. if userinfo.GetModuleAccessPermission(thisModuleName) {
  218. userAccessableModules = append(userAccessableModules, thisModule)
  219. }
  220. }
  221. //Return the loaded modules as a list of JSON string
  222. jsonString, _ := json.Marshal(userAccessableModules)
  223. utils.SendJSONResponse(w, string(jsonString))
  224. }
  225. func (m *ModuleHandler) GetModuleInfoByID(moduleid string) *ModuleInfo {
  226. for _, module := range m.LoadedModule {
  227. if module.Name == moduleid {
  228. return module
  229. }
  230. }
  231. return nil
  232. }
  233. func (m *ModuleHandler) GetLaunchParameter(w http.ResponseWriter, r *http.Request) {
  234. moduleName, _ := utils.GetPara(r, "module")
  235. if moduleName == "" {
  236. utils.SendErrorResponse(w, "Missing paramter 'module'.")
  237. return
  238. }
  239. //Loop through the modules and see if the module exists.
  240. var targetLaunchInfo *ModuleInfo = nil
  241. found := false
  242. for _, module := range m.LoadedModule {
  243. thisModuleName := module.Name
  244. if thisModuleName == moduleName {
  245. targetLaunchInfo = module
  246. found = true
  247. }
  248. }
  249. if found {
  250. jsonString, _ := json.Marshal(targetLaunchInfo)
  251. utils.SendJSONResponse(w, string(jsonString))
  252. return
  253. } else {
  254. utils.SendErrorResponse(w, "Given module not exists.")
  255. return
  256. }
  257. }