main.router.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. package main
  2. /*
  3. ArOZ Online System Main Request Router
  4. This is used to check authentication before actually serving file to the target client
  5. This function also handle the special page (login.html and user.html) delivery
  6. */
  7. import (
  8. "net/http"
  9. "path/filepath"
  10. "strconv"
  11. "strings"
  12. fs "imuslab.com/arozos/mod/filesystem"
  13. "imuslab.com/arozos/mod/network/gzipmiddleware"
  14. "imuslab.com/arozos/mod/utils"
  15. )
  16. func mrouter(h http.Handler) http.Handler {
  17. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  18. /*
  19. You can also check the path for url using r.URL.Path
  20. */
  21. //Container apps come first: an app hostname, /app/<slug>/, or a root
  22. //request made by an app page (which must never be answered from the
  23. //ArozOS web root, not even the public /script or /img paths below)
  24. if appProxyManager != nil && appProxyManager.HandleRequest(w, r) {
  25. return
  26. }
  27. if r.URL.Path == "/favicon.ico" || r.URL.Path == "/manifest.webmanifest" || r.URL.Path == "/robots.txt" || r.URL.Path == "/humans.txt" {
  28. //Serving web specification files. Allow no auth access.
  29. h.ServeHTTP(w, r)
  30. } else if r.URL.Path == "/login.html" {
  31. //Login page. Require special treatment for template.
  32. //Get the redirection address from the request URL
  33. red, _ := utils.GetPara(r, "redirect")
  34. //Append the redirection addr into the template
  35. imgsrc := filepath.Join(vendorResRoot, "auth_icon.png")
  36. if !fs.FileExists(imgsrc) {
  37. imgsrc = "./web/img/public/auth_icon.png"
  38. }
  39. imageBase64, _ := utils.LoadImageAsBase64(imgsrc)
  40. parsedPage, err := utils.Templateload("web/login.html", map[string]string{
  41. "redirection_addr": red,
  42. "usercount": strconv.Itoa(authAgent.GetUserCounts()),
  43. "service_logo": imageBase64,
  44. "login_addr": "system/auth/login",
  45. })
  46. if err != nil {
  47. panic("Error. Unable to parse login page. Is web directory data exists?")
  48. }
  49. w.Header().Add("Content-Type", "text/html; charset=UTF-8")
  50. w.Write([]byte(parsedPage))
  51. } else if r.URL.Path == "/reset.html" && authAgent.GetUserCounts() > 0 {
  52. //Password restart page. Allow access only when user number > 0
  53. system_resetpw_handlePasswordReset(w, r)
  54. } else if r.URL.Path == "/user.html" && authAgent.GetUserCounts() == 0 {
  55. //Serve user management page. This only allows serving of such page when the total usercount = 0 (aka System Initiation)
  56. h.ServeHTTP(w, r)
  57. } else if (len(r.URL.Path) > 11 && r.URL.Path[:11] == "/img/public") || (len(r.URL.Path) > 7 && r.URL.Path[:7] == "/script") {
  58. //Public image directory. Allow anyone to access resources inside this directory.
  59. if filepath.Ext("web"+fs.DecodeURI(r.RequestURI)) == ".js" {
  60. //Fixed serve js meme type invalid bug on Firefox
  61. w.Header().Add("Content-Type", "application/javascript; charset=UTF-8")
  62. }
  63. h.ServeHTTP(w, r)
  64. } else if len(r.URL.Path) >= len("/cluster/acn") && r.URL.Path[:12] == "/cluster/acn" {
  65. //Cluster node-to-node protocol (ACN). Requests are authenticated by
  66. //node signatures, not user sessions, see mod/cluster/acn
  67. if clusterManager == nil {
  68. //Cluster disabled (-disable_cluster) or its agent failed to start
  69. http.NotFound(w, r)
  70. return
  71. }
  72. clusterManager.ACNHandler().ServeHTTP(w, r)
  73. } else if len(r.URL.Path) >= len("/caldav") && r.URL.Path[:7] == "/caldav" {
  74. //CalDAV sub-router (bidirectional calendar sync for iOS)
  75. if CalDAVHandler == nil {
  76. errorHandleInternalServerError(w, r)
  77. return
  78. }
  79. CalDAVHandler.ServeHTTP(w, r)
  80. } else if r.URL.Path == "/.well-known/caldav" {
  81. //CalDAV service discovery redirect (RFC 6764)
  82. http.Redirect(w, r, "/caldav/", http.StatusMovedPermanently)
  83. } else if len(r.URL.Path) >= len("/webdav") && r.URL.Path[:7] == "/webdav" {
  84. //WebDAV sub-router
  85. if WebDAVManager == nil {
  86. errorHandleInternalServerError(w, r)
  87. return
  88. }
  89. WebDAVManager.HandleRequest(w, r)
  90. } else if len(r.URL.Path) >= len("/share") && r.URL.Path[:6] == "/share" {
  91. //Share Manager sub-router
  92. if shareManager == nil {
  93. errorHandleInternalServerError(w, r)
  94. return
  95. }
  96. shareManager.HandleShareAccess(w, r)
  97. } else if len(r.URL.Path) >= len("/api/remote") && r.URL.Path[:11] == "/api/remote" {
  98. //Serverless sub-router
  99. if AGIGateway == nil {
  100. errorHandleInternalServerError(w, r)
  101. return
  102. }
  103. AGIGateway.ExtAPIHandler(w, r)
  104. } else if len(r.URL.Path) >= len("/fileview") && r.URL.Path[:9] == "/fileview" {
  105. //File server sub-router
  106. if DirListManager == nil {
  107. errorHandleInternalServerError(w, r)
  108. return
  109. }
  110. DirListManager.ServerWebFileRequest(w, r)
  111. } else if r.URL.Path == "/" && authAgent.CheckAuth(r) {
  112. //Use logged in and request the index. Serve the user's interface module
  113. w.Header().Set("Cache-Control", "no-cache, no-store, no-transform, must-revalidate, private, max-age=0")
  114. userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
  115. if err != nil {
  116. //ERROR!! Server default
  117. h.ServeHTTP(w, r)
  118. } else {
  119. interfaceModule := userinfo.GetInterfaceModules()
  120. if len(interfaceModule) == 1 && interfaceModule[0] == "Desktop" {
  121. http.Redirect(w, r, "./desktop.html", http.StatusTemporaryRedirect)
  122. } else if len(interfaceModule) == 1 {
  123. //User with default interface module not desktop
  124. modileInfo := moduleHandler.GetModuleInfoByID(interfaceModule[0])
  125. if modileInfo == nil {
  126. //The module is not found or not enabled
  127. http.Redirect(w, r, "./SystemAO/boot/interface_disabled.html", http.StatusTemporaryRedirect)
  128. return
  129. }
  130. http.Redirect(w, r, modileInfo.StartDir, http.StatusTemporaryRedirect)
  131. } else if len(interfaceModule) > 1 {
  132. //Redirect to module selector
  133. http.Redirect(w, r, "./SystemAO/boot/interface_selector.html", http.StatusTemporaryRedirect)
  134. } else if len(interfaceModule) == 0 {
  135. //Redirect to error page
  136. http.Redirect(w, r, "./SystemAO/boot/no_interfaceing.html", http.StatusTemporaryRedirect)
  137. } else {
  138. //For unknown operations, send it to desktop
  139. http.Redirect(w, r, "./desktop.html", http.StatusTemporaryRedirect)
  140. }
  141. }
  142. } else if ((len(r.URL.Path) >= 5 && r.URL.Path[:5] == "/www/") || r.URL.Path == "/www") && *allow_homepage {
  143. //Serve the custom homepage of the user defined. Hand over to the www router
  144. userWwwHandler.RouteRequest(w, r)
  145. } else if authAgent.CheckAuth(r) {
  146. //User logged in. Continue to serve the file the client want
  147. authAgent.UpdateSessionExpireTime(w, r)
  148. if build_version == "development" {
  149. //Do something if development build
  150. //w.Header().Add("Cross-Origin-Opener-Policy", "same-origin")
  151. //w.Header().Add("Cross-Origin-Embedder-Policy", "require-corp")
  152. }
  153. if filepath.Ext("web"+fs.DecodeURI(r.RequestURI)) == ".js" {
  154. //Fixed serve js meme type invalid bug on Firefox
  155. w.Header().Add("Content-Type", "application/javascript; charset=UTF-8")
  156. }
  157. if !*disable_subservices {
  158. //Enable subservice access
  159. //Check if this path is reverse proxy path. If yes, serve with proxyserver
  160. isRP, proxy, rewriteURL, subserviceObject := ssRouter.CheckIfReverseProxyPath(r)
  161. if isRP {
  162. //Check user permission on that module
  163. ssRouter.HandleRoutingRequest(w, r, proxy, subserviceObject, rewriteURL)
  164. return
  165. }
  166. }
  167. //Not subservice routine. Handle file server
  168. if !*enable_dir_listing {
  169. if strings.HasSuffix(r.URL.Path, "/") {
  170. //User trying to access a directory. Send NOT FOUND.
  171. if fileExistsInWebOrVendor(r.URL.Path + "index.html") {
  172. //Index exists. Allow passthrough
  173. } else {
  174. errorHandleNotFound(w, r)
  175. return
  176. }
  177. }
  178. }
  179. if !fileExistsInWebOrVendor(r.URL.Path) {
  180. //File not found
  181. errorHandleNotFound(w, r)
  182. return
  183. }
  184. routerStaticContentServer(h, w, r)
  185. } else {
  186. //User not logged in. Check if the path end with public/. If yes, allow public access
  187. if !fileExistsInWebOrVendor(r.URL.Path) {
  188. //Requested file not exists on the server. Return not found
  189. errorHandleNotFound(w, r)
  190. } else if r.URL.Path[len(r.URL.Path)-1:] != "/" && filepath.Base(filepath.Dir(r.URL.Path)) == "public" {
  191. //This file path end with public/. Allow public access
  192. routerStaticContentServer(h, w, r)
  193. } else if *allow_homepage && len(r.URL.Path) >= 5 && r.URL.Path[:5] == "/www/" {
  194. //Handle public home serving if homepage mode is enabled
  195. routerStaticContentServer(h, w, r)
  196. } else {
  197. //Other paths
  198. //Rediect to login page
  199. w.Header().Set("Cache-Control", "no-cache, no-store, no-transform, must-revalidate, private, max-age=0")
  200. http.Redirect(w, r, utils.ConstructRelativePathFromRequestURL(r.RequestURI, "login.html")+"?redirect="+r.URL.String(), 307)
  201. }
  202. }
  203. })
  204. }
  205. // fileExistsInWebOrVendor reports whether a URL path resolves to a file in
  206. // either the standard ./web directory or the vendorResRoot/web/ overlay.
  207. func fileExistsInWebOrVendor(urlPath string) bool {
  208. if fs.FileExists("web" + urlPath) {
  209. return true
  210. }
  211. return fs.FileExists(filepath.Join(vendorResRoot, "web", urlPath))
  212. }
  213. func routerStaticContentServer(h http.Handler, w http.ResponseWriter, r *http.Request) {
  214. if *enable_gzip {
  215. gzipmiddleware.Compress(h).ServeHTTP(w, r)
  216. } else {
  217. h.ServeHTTP(w, r)
  218. }
  219. }