www.go 4.5 KB

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