Feat: Per-user vhosts (user.sometilde.org)
Limitations: - Doesn't care about `extra.stuff.here.user.host.name` - No custom hostname for user subdomains - Returns not-found for `user.host.name` where `/home/user/userdir/` doesn't exist
This commit is contained in:
parent
efbf17cfd6
commit
2dfaed4fa3
11
README.md
11
README.md
|
@ -86,6 +86,7 @@ Here are the config options and their default values
|
|||
|
||||
* `userdirEnable=true`: enable serving `/~user/*` requests
|
||||
* `userdir="public_spartan"`: root directory for users. This should not have trailing slashes, and it is relative to `/home/user/`
|
||||
* `userSubdomains=false`: User vhosts. Whether to allow `user.host.name/foo.txt` being the same as `host.name/~user/foo.txt` (When `hostname="host.name"`). **NOTE**: This only works when `hostname` option is set.
|
||||
|
||||
**CGI**
|
||||
|
||||
|
@ -143,11 +144,19 @@ Keep in mind that CGI scripts (as of now) are run by the same user as the server
|
|||
- [x] user homedir
|
||||
- [x] hostname, port
|
||||
- [x] public dir
|
||||
- [ ] dirlist title
|
||||
- [x] dirlist title
|
||||
- [x] user vhost
|
||||
- [ ] userdir slug
|
||||
- [ ] redirects
|
||||
- [x] CGI
|
||||
- [x] pipe data block
|
||||
- [ ] user cgi config and change uid to user
|
||||
- [ ] regex in cgi paths
|
||||
- [ ] SCGI
|
||||
|
||||
- [ ] Multiple servers with each of their own confs
|
||||
|
||||
README:
|
||||
- [ ] Add example confs
|
||||
- [ ] Add example .service files
|
||||
```
|
||||
|
|
|
@ -15,6 +15,7 @@ type Config struct {
|
|||
RootDir string
|
||||
UserDirEnable bool
|
||||
UserDir string
|
||||
UserSubdomains bool
|
||||
DirlistEnable bool
|
||||
DirlistReverse bool
|
||||
DirlistSort string
|
||||
|
@ -33,6 +34,7 @@ var defaultConf = &Config{
|
|||
DirlistTitles: true,
|
||||
UserDirEnable: true,
|
||||
UserDir: "public_spartan",
|
||||
UserSubdomains: false,
|
||||
CGIPaths: []string{"cgi/"},
|
||||
UserCGIEnable: false, // Turned off by default because scripts are run by server user as of now
|
||||
}
|
||||
|
|
47
spsrv.go
47
spsrv.go
|
@ -21,6 +21,7 @@ import (
|
|||
type Request struct {
|
||||
conn io.ReadWriteCloser
|
||||
netConn *net.Conn
|
||||
vhost string
|
||||
user string
|
||||
path string // Requested path
|
||||
filePath string // Actual file path that does not include the content dir name
|
||||
|
@ -155,11 +156,17 @@ func handleConnection(netConn net.Conn, conf *Config) {
|
|||
sendResponseHeader(conn, statusClientError, "Bad request")
|
||||
return
|
||||
}
|
||||
userSubdomainReq := false
|
||||
if conf.Hostname != "" {
|
||||
if conf.Hostname != host {
|
||||
log.Println("Request host does not match config value Hostname, returning client error.")
|
||||
sendResponseHeader(conn, statusClientError, "No proxying to other hosts!")
|
||||
return
|
||||
if conf.UserDirEnable && conf.UserSubdomains && strings.HasSuffix(host, conf.Hostname) {
|
||||
userSubdomainReq = true
|
||||
}
|
||||
if !userSubdomainReq {
|
||||
log.Println("Request host does not match config value Hostname, returning client error.")
|
||||
sendResponseHeader(conn, statusClientError, "No proxying to other hosts!")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if strings.Contains(reqPath, "..") {
|
||||
|
@ -185,7 +192,13 @@ func handleConnection(netConn net.Conn, conf *Config) {
|
|||
data += newData
|
||||
}
|
||||
}
|
||||
req := &Request{path: reqPath, netConn: &netConn, conn: conn, data: data, dataLen: dataLen}
|
||||
|
||||
var vhost string
|
||||
if userSubdomainReq {
|
||||
// TODO: Handle extra dots like a.b.host.name?
|
||||
vhost = strings.TrimSuffix(host, "."+conf.Hostname)
|
||||
}
|
||||
req := &Request{vhost: vhost, path: reqPath, netConn: &netConn, conn: conn, data: data, dataLen: dataLen}
|
||||
|
||||
// Time to fetch the files!
|
||||
path := resolvePath(reqPath, conf, req)
|
||||
|
@ -219,25 +232,35 @@ func handleConnection(netConn net.Conn, conf *Config) {
|
|||
// resolvePath takes in teh request path and returns the cleaned filepath that needs to be fetched.
|
||||
// It also handles user directories paths /~user/ and /~user if user directories is enabled in the config.
|
||||
func resolvePath(reqPath string, conf *Config, req *Request) (path string) {
|
||||
// Handle tildes
|
||||
if conf.UserDirEnable && strings.HasPrefix(reqPath, "/~") {
|
||||
var user string
|
||||
// Handle user subdomains
|
||||
if req.vhost != "" {
|
||||
user = req.vhost
|
||||
path = reqPath
|
||||
} else if conf.UserDirEnable && strings.HasPrefix(reqPath, "/~") {
|
||||
// Handle tildes
|
||||
// Note that user.host.name/~user/ would treat it as a literal folder named /~user/
|
||||
// (hence using `else if`)
|
||||
bits := strings.Split(reqPath, "/")
|
||||
username := bits[1][1:]
|
||||
user = bits[1][1:]
|
||||
|
||||
// /~user to /~user/ is somehow able to be handled together with any other /folder to /foler/ redirects
|
||||
// /~user to /~user/ is somehow able to be handled together with any other /folder to /folder/ redirects
|
||||
// So I won't worry about that nor handle it specifically
|
||||
|
||||
req.filePath = strings.TrimPrefix(filepath.Clean(strings.TrimPrefix(reqPath, "/~"+username)), "/")
|
||||
req.filePath = strings.TrimPrefix(filepath.Clean(strings.TrimPrefix(reqPath, "/~"+user)), "/")
|
||||
path = req.filePath
|
||||
}
|
||||
|
||||
new_prefix := filepath.Join("/home/", username, conf.UserDir)
|
||||
req.user = username
|
||||
path = filepath.Clean(strings.Replace(reqPath, bits[1], new_prefix, 1))
|
||||
if user != "" {
|
||||
path = filepath.Join("/home/", user, conf.UserDir, path)
|
||||
req.user = user
|
||||
|
||||
if strings.HasSuffix(reqPath, "/") {
|
||||
path = filepath.Join(path, "index.gmi")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
path = reqPath
|
||||
// TODO: [config] default index file for a directory is index.gmi
|
||||
if strings.HasSuffix(reqPath, "/") || reqPath == "" {
|
||||
|
|
Loading…
Reference in New Issue