3 changed files with 140 additions and 127 deletions
@ -1,162 +1,172 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"bufio" |
||||
"fmt" |
||||
"io/ioutil" |
||||
"mime" |
||||
"net" |
||||
"net/url" |
||||
"os" |
||||
"path/filepath" |
||||
"strings" |
||||
"bufio" |
||||
"fmt" |
||||
"io/ioutil" |
||||
"mime" |
||||
"net" |
||||
"net/url" |
||||
"os" |
||||
"path/filepath" |
||||
"strings" |
||||
) |
||||
|
||||
const ( |
||||
VERSION float32 = 0.1 |
||||
REQ_ERR string = "5 Invalid request\r\n" |
||||
SRV_ERR string = "4 Internal server error\r\n" |
||||
VERSION float32 = 0.1 |
||||
REQ_ERR string = "5 Invalid request\r\n" |
||||
SRV_ERR string = "4 Internal server error\r\n" |
||||
) |
||||
|
||||
var conf *config |
||||
|
||||
func serveFile(path string, conn net.Conn) { |
||||
ext := filepath.Ext(path) |
||||
var mimeType string |
||||
if ext == ".gmi" || ext == ".gemini" { |
||||
mimeType = "text/gemini" |
||||
} else { |
||||
mimeType = mime.TypeByExtension(ext) |
||||
} |
||||
|
||||
if mimeType == "" { |
||||
mimeType = "application/octet-stream" |
||||
} |
||||
contents, err := ioutil.ReadFile(path) |
||||
ext := filepath.Ext(path) |
||||
var mimeType string |
||||
if ext == ".gmi" || ext == ".gemini" { |
||||
mimeType = "text/gemini" |
||||
} else { |
||||
mimeType = mime.TypeByExtension(ext) |
||||
} |
||||
|
||||
if mimeType == "" { |
||||
mimeType = "application/octet-stream" |
||||
} |
||||
contents, err := ioutil.ReadFile(path) |
||||
if err != nil { |
||||
fmt.Printf("5 - %s\n", string(path)) |
||||
conn.Write([]byte(REQ_ERR)) |
||||
} |
||||
conn.Write([]byte(fmt.Sprintf("20 %s\r\n", mimeType))) |
||||
conn.Write([]byte(fmt.Sprintf("2 %s\r\n", mimeType))) |
||||
conn.Write(contents) |
||||
fmt.Printf("2 - %s\n", string(path)) |
||||
} |
||||
|
||||
func getSystemPath(p string, full string) (string, error) { |
||||
hasTrailingSlash := strings.HasSuffix(p, "/") |
||||
var sysPath string |
||||
if strings.HasPrefix(p, conf.userPrefix) { |
||||
segments := strings.SplitN(p, "/", -1) |
||||
segments[1] = strings.Replace(p, conf.userPrefix, conf.userRoot, 1) |
||||
un := segments[2] |
||||
prefix := filepath.Join(conf.userPrefix, un, conf.userFolder) |
||||
if len(segments) > 3 { |
||||
suffix := strings.Join(segments[3:], "/") |
||||
sysPath = filepath.Join(prefix, suffix) |
||||
} else { |
||||
sysPath = prefix |
||||
} |
||||
} else { |
||||
sysPath = filepath.Join(conf.documentRoot, p) |
||||
} |
||||
fInfo, err := os.Stat(p) |
||||
if err != nil { |
||||
return "", fmt.Errorf(REQ_ERR) |
||||
} |
||||
|
||||
// Fail if file does not exist or perms aren't right
|
||||
hasTrailingSlash := strings.HasSuffix(p, "/") |
||||
var sysPath string |
||||
if strings.HasPrefix(p, conf.userPrefix) { |
||||
segments := strings.SplitN(p, "/", -1) |
||||
un := segments[2] |
||||
prefix := filepath.Join(conf.userRoot, un, conf.userFolder) |
||||
if len(segments) > 3 { |
||||
suffix := strings.Join(segments[3:], "/") |
||||
sysPath = filepath.Join(prefix, suffix) |
||||
} else { |
||||
sysPath = prefix |
||||
} |
||||
} else { |
||||
sysPath = filepath.Join(conf.documentRoot, p) |
||||
} |
||||
fInfo, err := os.Stat(sysPath) |
||||
if err != nil { |
||||
return "", fmt.Errorf(REQ_ERR) |
||||
} |
||||
|
||||
// Fail if file does not exist or perms aren't right
|
||||
if os.IsNotExist(err) || os.IsPermission(err) { |
||||
return "", fmt.Errorf(REQ_ERR) |
||||
return "", fmt.Errorf(REQ_ERR) |
||||
} else if err != nil { |
||||
return "", fmt.Errorf(SRV_ERR) |
||||
return "", fmt.Errorf(SRV_ERR) |
||||
} else if uint64(fInfo.Mode().Perm())&0444 != 0444 { |
||||
return "", fmt.Errorf(REQ_ERR) |
||||
return "", fmt.Errorf(REQ_ERR) |
||||
} else if fInfo.IsDir() { |
||||
if !hasTrailingSlash { |
||||
return "", fmt.Errorf("3 %s/\r\n", full) |
||||
} |
||||
} |
||||
if !hasTrailingSlash { |
||||
return "", fmt.Errorf("3 %s/\r\n", full) |
||||
} |
||||
} |
||||
|
||||
return sysPath, nil |
||||
return sysPath, nil |
||||
} |
||||
|
||||
func handleRequest(conn net.Conn) { |
||||
defer conn.Close() |
||||
defer conn.Close() |
||||
|
||||
reader := bufio.NewReaderSize(conn, 1024) |
||||
reader := bufio.NewReaderSize(conn, 1024) |
||||
request, overflow, err := reader.ReadLine() |
||||
if overflow { |
||||
conn.Write([]byte("5 Request too long\r\n")) |
||||
return |
||||
fmt.Printf("5 - %s\n", string(request)) |
||||
conn.Write([]byte(REQ_ERR)) |
||||
return |
||||
} else if err != nil { |
||||
conn.Write([]byte("4 Internal server error\r\n")) |
||||
return |
||||
} |
||||
|
||||
uri, err := url.Parse(string(request)) |
||||
if err != nil { |
||||
conn.Write([]byte("5 Invalid request")) |
||||
return |
||||
} |
||||
if uri.Scheme == "" { |
||||
uri.Scheme = "mercury" |
||||
} |
||||
|
||||
if uri.Scheme != "mercury" { |
||||
conn.Write([]byte("5 Invalid URL scheme")) |
||||
return |
||||
} |
||||
|
||||
if reqHn := strings.Split(uri.Host, ":")[0]; reqHn != conf.host { |
||||
conn.Write([]byte("5 Invalid request")) |
||||
return |
||||
} |
||||
|
||||
path, err := getSystemPath(uri.Path, uri.String()) |
||||
if err != nil { |
||||
conn.Write([]byte(err.Error())) |
||||
return |
||||
} |
||||
fInfo, err := os.Stat(path) |
||||
if err != nil { |
||||
conn.Write([]byte(REQ_ERR)) |
||||
return |
||||
} |
||||
if fInfo.IsDir() { |
||||
indexPath := filepath.Join(path, conf.indexFile) |
||||
indInfo, err := os.Stat(indexPath) |
||||
if err == nil && uint64(indInfo.Mode().Perm())&0444 == 0444 { |
||||
serveFile(indexPath, conn) |
||||
} else { |
||||
conn.Write([]byte(REQ_ERR)) |
||||
} |
||||
return |
||||
} |
||||
|
||||
serveFile(path, conn) |
||||
return |
||||
} |
||||
fmt.Printf("4 - %s", string(request)) |
||||
conn.Write([]byte(SRV_ERR)) |
||||
fmt.Println(err) |
||||
return |
||||
} |
||||
|
||||
uri, err := url.Parse(string(request)) |
||||
if err != nil { |
||||
fmt.Printf("5 - %s\n", string(request)) |
||||
conn.Write([]byte(REQ_ERR)) |
||||
return |
||||
} |
||||
if uri.Scheme == "" { |
||||
uri.Scheme = "mercury" |
||||
} |
||||
|
||||
if uri.Scheme != "mercury" { |
||||
fmt.Printf("5 - %s\n", string(request)) |
||||
conn.Write([]byte(REQ_ERR)) |
||||
return |
||||
} |
||||
|
||||
if reqHn := strings.Split(uri.Host, ":")[0]; reqHn != conf.host { |
||||
fmt.Printf("5 - %s\n", string(request)) |
||||
conn.Write([]byte(REQ_ERR)) |
||||
return |
||||
} |
||||
|
||||
path, err := getSystemPath(uri.Path, uri.String()) |
||||
if err != nil { |
||||
fmt.Println(err.Error()) |
||||
conn.Write([]byte(err.Error())) |
||||
return |
||||
} |
||||
fInfo, err := os.Stat(path) |
||||
if err != nil { |
||||
fmt.Printf("5 - %s\n", string(request)) |
||||
conn.Write([]byte(REQ_ERR)) |
||||
return |
||||
} |
||||
if fInfo.IsDir() { |
||||
indexPath := filepath.Join(path, conf.indexFile) |
||||
indInfo, err := os.Stat(indexPath) |
||||
if err == nil && uint64(indInfo.Mode().Perm())&0444 == 0444 { |
||||
serveFile(indexPath, conn) |
||||
} else { |
||||
fmt.Printf("5 - %s\n", string(request)) |
||||
conn.Write([]byte(REQ_ERR)) |
||||
} |
||||
return |
||||
} |
||||
|
||||
serveFile(path, conn) |
||||
return |
||||
} |
||||
|
||||
func main() { |
||||
conf = makeConfig() |
||||
fmt.Printf("𐌕𐌖𐌓𐌌𐌑 - Version %f", VERSION) |
||||
if conf.host == "" { |
||||
fmt.Fprintln(os.Stderr, ">> No host is configured for this server\n>> Please recompile with a host name in config.go") |
||||
os.Exit(1) |
||||
} |
||||
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", conf.port)) |
||||
if err != nil { |
||||
fmt.Fprintf(os.Stderr, "Error listening on port %d:\n%s\n", conf.port, err.Error()) |
||||
os.Exit(1) |
||||
} |
||||
|
||||
for { |
||||
conn, err := listener.Accept() |
||||
if err != nil { |
||||
fmt.Fprintf(os.Stderr, "Error listening on port %d:\n%s\n", conf.port, err.Error()) |
||||
continue |
||||
} |
||||
go handleRequest(conn) |
||||
} |
||||
conf = makeConfig() |
||||
fmt.Printf("𐌕𐌖𐌓𐌌𐌑 - Version %.1f\n\n", VERSION) |
||||
if conf.host == "" { |
||||
fmt.Fprintln(os.Stderr, ">> No host is configured for this server\n>> Please recompile with a host name in config.go") |
||||
os.Exit(1) |
||||
} |
||||
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", conf.port)) |
||||
if err != nil { |
||||
fmt.Fprintf(os.Stderr, "Error listening on port %d:\n%s\n", conf.port, err.Error()) |
||||
os.Exit(1) |
||||
} |
||||
|
||||
fmt.Printf("Listening on port %d\n", conf.port) |
||||
|
||||
for { |
||||
conn, err := listener.Accept() |
||||
if err != nil { |
||||
fmt.Fprintf(os.Stderr, "Error listening on port %d:\n%s\n", conf.port, err.Error()) |
||||
continue |
||||
} |
||||
go handleRequest(conn) |
||||
} |
||||
} |
||||
|
Loading…
Reference in new issue