www.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. package www
  2. import (
  3. "io"
  4. "io/ioutil"
  5. "net/http"
  6. "net/url"
  7. "path/filepath"
  8. "strings"
  9. "imuslab.com/arozos/mod/agi"
  10. "imuslab.com/arozos/mod/database"
  11. "imuslab.com/arozos/mod/user"
  12. "imuslab.com/arozos/mod/utils"
  13. )
  14. /*
  15. www package
  16. This package is the replacement handler for global homepage function in ArozOS.
  17. This allow users to host and create their website using any folder within the user
  18. access file system.
  19. */
  20. type Options struct {
  21. UserHandler *user.UserHandler
  22. Database *database.Database
  23. AgiGateway *agi.Gateway
  24. }
  25. type Handler struct {
  26. Options Options
  27. }
  28. /*
  29. New WebRoot Handler create a new handler for handling and routing webroots
  30. */
  31. func NewWebRootHandler(options Options) *Handler {
  32. //Create the homepage database table
  33. options.Database.NewTable("www")
  34. //Return the handler object
  35. return &Handler{
  36. Options: options,
  37. }
  38. }
  39. func (h *Handler) RouteRequest(w http.ResponseWriter, r *http.Request) {
  40. //Check if it is reaching www root folder or any files directly under www.
  41. if filepath.ToSlash(filepath.Clean(r.URL.Path)) == "/www" {
  42. //Direct access of the root folder. Serve the homepage description.
  43. http.ServeFile(w, r, "web/SystemAO/www/index.html")
  44. return
  45. } else if filepath.ToSlash(filepath.Dir(r.URL.Path)) == "/www" {
  46. //Missing the last / at the end of the path
  47. r.URL.Path = r.URL.Path + "/"
  48. http.Redirect(w, r, filepath.ToSlash(filepath.Dir(r.URL.Path))+"/", http.StatusTemporaryRedirect)
  49. return
  50. }
  51. //Escape the URL
  52. decodedValue, err := url.QueryUnescape(r.URL.Path)
  53. if err != nil {
  54. //failed to decode. Just use its raw value
  55. decodedValue = r.URL.Path
  56. }
  57. //Check the user name of the user root
  58. parsedRequestURL := strings.Split(filepath.ToSlash(filepath.Clean(decodedValue)[1:]), "/")
  59. //Malparsed URL. Ignore request
  60. if len(parsedRequestURL) < 2 {
  61. serveNotFoundTemplate(w, r)
  62. return
  63. }
  64. //Extract user information
  65. username := parsedRequestURL[1]
  66. userinfo, err := h.Options.UserHandler.GetUserInfoFromUsername(username)
  67. if err != nil {
  68. serveNotFoundTemplate(w, r)
  69. return
  70. }
  71. //Check if this user enabled homepage
  72. enabled := h.CheckUserHomePageEnabled(userinfo.Username)
  73. if !enabled {
  74. serveNotFoundTemplate(w, r)
  75. return
  76. }
  77. //Check if the user have his webroot correctly configured
  78. webroot, err := h.GetUserWebRoot(userinfo.Username)
  79. if err != nil {
  80. //User do not have a correctly configured webroot. Serve instruction
  81. handleWebrootError(w)
  82. return
  83. }
  84. //User webroot real path conversion
  85. fsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(webroot)
  86. if err != nil {
  87. handleWebrootError(w)
  88. return
  89. }
  90. webrootRealpath, err := fsh.FileSystemAbstraction.VirtualPathToRealPath(webroot, userinfo.Username)
  91. if err != nil {
  92. handleWebrootError(w)
  93. return
  94. }
  95. //Perform path rewrite
  96. rewrittenPath := strings.Join(parsedRequestURL[2:], "/")
  97. //Actual accessing file path
  98. targetFilePath := filepath.ToSlash(filepath.Join(webrootRealpath, rewrittenPath))
  99. //Check if the file exists
  100. if !fsh.FileSystemAbstraction.FileExists(targetFilePath) {
  101. serveNotFoundTemplate(w, r)
  102. return
  103. }
  104. //Fix mimetype of js files on Windows 10 bug
  105. if filepath.Ext(targetFilePath) == ".js" {
  106. w.Header().Set("Content-Type", "application/javascript")
  107. }
  108. if filepath.Ext(targetFilePath) == "" {
  109. //Reading a folder. Check if index.htm or index.html exists.
  110. if fsh.FileSystemAbstraction.FileExists(filepath.Join(targetFilePath, "index.html")) {
  111. targetFilePath = filepath.ToSlash(filepath.Join(targetFilePath, "index.html"))
  112. } else if fsh.FileSystemAbstraction.FileExists(filepath.Join(targetFilePath, "index.htm")) {
  113. targetFilePath = filepath.ToSlash(filepath.Join(targetFilePath, "index.htm"))
  114. } else {
  115. //Not allow listing folder
  116. http.ServeFile(w, r, "system/errors/forbidden.html")
  117. return
  118. }
  119. }
  120. //Record the client IP for analysis, to be added in the future if needed
  121. //Execute it if it is agi file
  122. if fsh.FileSystemAbstraction.FileExists(targetFilePath) && filepath.Ext(targetFilePath) == ".agi" {
  123. result, err := h.Options.AgiGateway.ExecuteAGIScriptAsUser(fsh, targetFilePath, userinfo, r)
  124. if err != nil {
  125. w.Write([]byte("500 - Internal Server Error \n" + err.Error()))
  126. return
  127. }
  128. w.Write([]byte(result))
  129. return
  130. }
  131. //Serve the file
  132. if fsh.FileSystemAbstraction.FileExists(targetFilePath) {
  133. http.ServeFile(w, r, targetFilePath)
  134. } else {
  135. f, err := fsh.FileSystemAbstraction.ReadStream(targetFilePath)
  136. if err != nil {
  137. w.Write([]byte(err.Error()))
  138. return
  139. }
  140. io.Copy(w, f)
  141. f.Close()
  142. }
  143. }
  144. func serveNotFoundTemplate(w http.ResponseWriter, r *http.Request) {
  145. http.ServeFile(w, r, "system/errors/notfound.html")
  146. }
  147. func handleWebrootError(w http.ResponseWriter) {
  148. w.WriteHeader(http.StatusInternalServerError)
  149. if utils.FileExists("./system/www/nowebroot.html") {
  150. content, err := ioutil.ReadFile("./system/www/nowebroot.html")
  151. if err != nil {
  152. w.Write([]byte("500 - Internal Server Error"))
  153. } else {
  154. w.Write(content)
  155. }
  156. } else {
  157. w.Write([]byte("500 - Internal Server Error"))
  158. }
  159. }