You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gophi/gopher/server.go

134 lines
3.5 KiB
Go

package gopher
import (
"gophi/core"
"os"
"strings"
)
var (
// Gophermap formatting globals
subgophermapSizeMax int64
pageWidth int
footer []byte
)
// serve is the global gopher server's serve function
func serve(client *core.Client) {
// Receive line from client
received, err := client.Conn().ReadLine()
if err != nil {
client.LogError("Conn read fail")
handleError(client, err)
return
}
// Split up to first tab in case we've been
// given index search query (which we use to set CGI env),
// or extra Gopher+ information (which we don't care about)
raw, extra := core.SplitBy(string(received), "\t")
// Ensure we've received a valid URL string
if core.HasAsciiControlBytes(raw) {
client.LogError("Invalid request: %s", raw)
handleError(client, core.ErrInvalidRequest.Extendf("%s has ascii control bytes", raw))
return
}
// Parse the encoded URI into path and query components
path, query, err := core.ParseEncodedURI(raw)
if err != nil {
client.LogError("Invalid request: %s", raw)
handleError(client, err)
return
}
// If prefixed by 'URL:' send a redirect
if strings.HasPrefix(path, "/URL:") {
raw = raw[5:]
client.Conn().Write(generateHTMLRedirect(raw))
client.LogInfo("Redirect to: %s", raw)
return
}
// Create new request and add the extra query part
request := core.NewRequest(core.BuildPath(path), query)
request.AddToQuery(extra)
// Handle the request!
err = core.HandleClient(client, request)
// Final error handling
if err != nil {
handleError(client, err)
client.LogError("Failed to serve: %s", request.String())
} else {
client.LogInfo("Served: %s", request.String())
}
}
func handleDirectory(client *core.Client, file *os.File, p *core.Path) error {
// First check for gophermap, create gophermap Path object
gophermap := p.JoinPathUnsafe("gophermap")
// If gophermap exists, we fetch this
file2, err := core.OpenFile(gophermap)
if err == nil {
stat, osErr := file2.Stat()
if osErr == nil {
// Fetch gophermap and defer close
defer file2.Close()
return core.FetchFile(client, file2, stat, gophermap)
}
// Else, just close file2
file2.Close()
}
// Slice to write
dirContents := make([]byte, 0)
// Add directory heading, empty line and a back line
dirContents = append(dirContents, buildLine(typeInfo, "[ "+core.Hostname+p.Selector()+" ]", "TITLE", nullHost, nullPort)...)
dirContents = append(dirContents, buildInfoLine("")...)
dirContents = append(dirContents, buildLine(typeDirectory, "..", p.Selector(), core.Hostname, core.Port)...)
// Scan directory and build lines
err = core.ScanDirectory(
file,
p,
func(file os.FileInfo, fp *core.Path) {
// Append new formatted file listing (if correct type)
dirContents = appendFileListing(dirContents, file, fp)
},
)
if err != nil {
return err
}
// Add footer, write contents
dirContents = append(dirContents, footer...)
return client.Conn().Write(dirContents)
}
func handleLargeFile(client *core.Client, file *os.File, p *core.Path) error {
return client.Conn().ReadFrom(file)
}
// handleError determines whether to send an error response to the client, and logs to system
func handleError(client *core.Client, err error) {
response, ok := generateErrorResponse(err)
if ok {
client.Conn().Write(response)
}
core.SystemLog.Errorf(err.Error())
}
// newFileContents returns a new FileContents object
func newFileContent(p *core.Path) core.FileContent {
if isGophermap(p) {
return &gophermapContent{}
}
return &core.RegularFileContent{}
}