www.go 5.1 KB

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