gophi/parse.go
kim (grufwub) 8a67a88660 use net/url's path parser, pass more Request objects round instead of RequestPath
Signed-off-by: kim (grufwub) <grufwub@gmail.com>
2020-05-14 17:54:06 +01:00

155 lines
4.7 KiB
Go

package main
import (
"strings"
"net/url"
)
/* Parse a request string into a path and parameters string */
func parseGopherUrl(request string) (*GopherUrl, *GophorError) {
if strings.Contains(request, "#") || // we don't support fragments
strings.HasPrefix(request, "GET ") { // we don't support HTTP requests
return nil, &GophorError{ InvalidRequestErr, nil }
}
/* Check if string contains any ASCII control byte */
for i := 0; i < len(request); i += 1 {
if request[i] < ' ' || request[i] == 0x7f {
return nil, &GophorError{ InvalidRequestErr, nil }
}
}
/* Split into 2 substrings by '?'. Url path and query */
split := strings.SplitN(request, "?", 2)
/* Unescape path */
path, err := url.PathUnescape(split[0])
if err != nil {
return nil, &GophorError{ InvalidRequestErr, nil }
}
/* Return GopherUrl based on this split request */
if len(split) == 1 {
return &GopherUrl{ path, "" }, nil
} else {
return &GopherUrl{ path, split[1] }, nil
}
}
/* Parse line type from contents */
func parseLineType(line string) ItemType {
lineLen := len(line)
if lineLen == 0 {
return TypeInfoNotStated
} else if lineLen == 1 {
/* The only accepted types for a length 1 line */
switch ItemType(line[0]) {
case TypeEnd:
return TypeEnd
case TypeEndBeginList:
return TypeEndBeginList
case TypeComment:
return TypeComment
case TypeInfo:
return TypeInfo
case TypeTitle:
return TypeTitle
default:
return TypeUnknown
}
} else if !strings.Contains(line, string(Tab)) {
/* The only accepted types for a line with no tabs */
switch ItemType(line[0]) {
case TypeComment:
return TypeComment
case TypeTitle:
return TypeTitle
case TypeInfo:
return TypeInfo
case TypeHiddenFile:
return TypeHiddenFile
case TypeSubGophermap:
return TypeSubGophermap
default:
return TypeInfoNotStated
}
}
return ItemType(line[0])
}
/* Parses a line in a gophermap into a new request object */
func parseLineRequestString(requestPath *RequestPath, lineStr string) (*Request, *GophorError) {
if strings.HasPrefix(lineStr, "/") {
/* Assume is absolute (well, seeing server root as '/') */
if withinCgiBin(lineStr[1:]) {
/* CGI script, parse request path and parameters */
url, gophorErr := parseGopherUrl(lineStr[1:])
if gophorErr != nil {
return nil, gophorErr
} else {
return &Request{ NewRequestPath(requestPath.RootDir(), url.Path), url.Parameters }, nil
}
} else {
/* Regular file, no more parsing */
return &Request{ NewRequestPath(requestPath.RootDir(), lineStr[1:]), "" }, nil
}
} else {
/* Assume relative to current directory */
if withinCgiBin(lineStr) && requestPath.Relative() == "" {
/* If begins with cgi-bin and is at root dir, parse as cgi-bin */
url, gophorErr := parseGopherUrl(lineStr)
if gophorErr != nil {
return nil, gophorErr
} else {
return &Request{ NewRequestPath(requestPath.RootDir(), url.Path), url.Parameters }, nil
}
} else {
/* Regular file, no more parsing */
return &Request{ NewRequestPath(requestPath.RootDir(), requestPath.JoinCurDir(lineStr)), "" }, nil
}
}
}
/* Split a string according to a rune, that supports delimiting with '\' */
func splitStringByRune(str string, r rune) []string {
ret := make([]string, 0)
buf := ""
delim := false
for _, c := range str {
switch c {
case r:
if !delim {
ret = append(ret, buf)
buf = ""
} else {
buf += string(c)
delim = false
}
case '\\':
if !delim {
delim = true
} else {
buf += string(c)
delim = false
}
default:
if !delim {
buf += string(c)
} else {
buf += "\\"+string(c)
delim = false
}
}
}
if len(buf) > 0 || len(ret) == 0 {
ret = append(ret, buf)
}
return ret
}