123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- package www
- import (
- "io"
- "io/ioutil"
- "net/http"
- "net/url"
- "path/filepath"
- "strings"
- "imuslab.com/arozos/mod/agi"
- "imuslab.com/arozos/mod/database"
- "imuslab.com/arozos/mod/user"
- )
- /*
- www package
- This package is the replacement handler for global homepage function in ArozOS.
- This allow users to host and create their website using any folder within the user
- access file system.
- */
- type Options struct {
- UserHandler *user.UserHandler
- Database *database.Database
- AgiGateway *agi.Gateway
- }
- type Handler struct {
- Options Options
- }
- /*
- New WebRoot Handler create a new handler for handling and routing webroots
- */
- func NewWebRootHandler(options Options) *Handler {
- //Create the homepage database table
- options.Database.NewTable("www")
- //Return the handler object
- return &Handler{
- Options: options,
- }
- }
- func (h *Handler) RouteRequest(w http.ResponseWriter, r *http.Request) {
- //Check if it is reaching www root folder or any files directly under www.
- if filepath.ToSlash(filepath.Clean(r.URL.Path)) == "/www" {
- //Direct access of the root folder. Serve the homepage description.
- http.ServeFile(w, r, "web/SystemAO/www/index.html")
- return
- } else if filepath.ToSlash(filepath.Dir(r.URL.Path)) == "/www" {
- //Missing the last / at the end of the path
- r.URL.Path = r.URL.Path + "/"
- http.Redirect(w, r, filepath.ToSlash(filepath.Dir(r.URL.Path))+"/", http.StatusTemporaryRedirect)
- return
- }
- //Escape the URL
- decodedValue, err := url.QueryUnescape(r.URL.Path)
- if err != nil {
- //failed to decode. Just use its raw value
- decodedValue = r.URL.Path
- }
- //Check the user name of the user root
- parsedRequestURL := strings.Split(filepath.ToSlash(filepath.Clean(decodedValue)[1:]), "/")
- //Malparsed URL. Ignore request
- if len(parsedRequestURL) < 2 {
- serveNotFoundTemplate(w, r)
- return
- }
- //Extract user information
- username := parsedRequestURL[1]
- userinfo, err := h.Options.UserHandler.GetUserInfoFromUsername(username)
- if err != nil {
- serveNotFoundTemplate(w, r)
- return
- }
- //Check if this user enabled homepage
- enabled := h.CheckUserHomePageEnabled(userinfo.Username)
- if !enabled {
- serveNotFoundTemplate(w, r)
- return
- }
- //Check if the user have his webroot correctly configured
- webroot, err := h.GetUserWebRoot(userinfo.Username)
- if err != nil {
- //User do not have a correctly configured webroot. Serve instruction
- handleWebrootError(w)
- return
- }
- //User webroot real path conversion
- fsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(webroot)
- if err != nil {
- handleWebrootError(w)
- return
- }
- webrootRealpath, err := fsh.FileSystemAbstraction.VirtualPathToRealPath(webroot, userinfo.Username)
- if err != nil {
- handleWebrootError(w)
- return
- }
- //Perform path rewrite
- rewrittenPath := strings.Join(parsedRequestURL[2:], "/")
- //Actual accessing file path
- targetFilePath := filepath.ToSlash(filepath.Join(webrootRealpath, rewrittenPath))
- //Check if the file exists
- if !fsh.FileSystemAbstraction.FileExists(targetFilePath) {
- serveNotFoundTemplate(w, r)
- return
- }
- //Fix mimetype of js files on Windows 10 bug
- if filepath.Ext(targetFilePath) == ".js" {
- w.Header().Set("Content-Type", "application/javascript")
- }
- if filepath.Ext(targetFilePath) == "" {
- //Reading a folder. Check if index.htm or index.html exists.
- if fsh.FileSystemAbstraction.FileExists(filepath.Join(targetFilePath, "index.html")) {
- targetFilePath = filepath.ToSlash(filepath.Join(targetFilePath, "index.html"))
- } else if fsh.FileSystemAbstraction.FileExists(filepath.Join(targetFilePath, "index.htm")) {
- targetFilePath = filepath.ToSlash(filepath.Join(targetFilePath, "index.htm"))
- } else {
- //Not allow listing folder
- http.ServeFile(w, r, "system/errors/forbidden.html")
- return
- }
- }
- //Record the client IP for analysis, to be added in the future if needed
- //Execute it if it is agi file
- if fsh.FileSystemAbstraction.FileExists(targetFilePath) && filepath.Ext(targetFilePath) == ".agi" {
- result, err := h.Options.AgiGateway.ExecuteAGIScriptAsUser(fsh, targetFilePath, userinfo, r)
- if err != nil {
- w.Write([]byte("500 - Internal Server Error \n" + err.Error()))
- return
- }
- w.Write([]byte(result))
- return
- }
- //Serve the file
- if fsh.FileSystemAbstraction.FileExists(targetFilePath) {
- http.ServeFile(w, r, targetFilePath)
- } else {
- f, err := fsh.FileSystemAbstraction.ReadStream(targetFilePath)
- if err != nil {
- w.Write([]byte(err.Error()))
- return
- }
- io.Copy(w, f)
- f.Close()
- }
- }
- func serveNotFoundTemplate(w http.ResponseWriter, r *http.Request) {
- http.ServeFile(w, r, "system/errors/notfound.html")
- }
- func handleWebrootError(w http.ResponseWriter) {
- w.WriteHeader(http.StatusInternalServerError)
- if fileExists("./system/www/nowebroot.html") {
- content, err := ioutil.ReadFile("./system/www/nowebroot.html")
- if err != nil {
- w.Write([]byte("500 - Internal Server Error"))
- } else {
- w.Write(content)
- }
- } else {
- w.Write([]byte("500 - Internal Server Error"))
- }
- }
|