port string
fqdn string
docRoot string
- header string
+ message string
+ fsymln bool
verbose bool
}{
iface: "localhost",
port: "7070",
fqdn: "localhost",
docRoot: ".",
- header: "igogopherd on localhost \r\n",
+ message: "",
+ fsymln: false,
verbose: false,
}
func initialize() {
+ help := false
flag.StringVar(&cfg.iface, "i", cfg.iface, "interface to bind to")
flag.StringVar(&cfg.port, "p", cfg.port, "TCP port to listen on")
- flag.StringVar(&cfg.fqdn, "f", cfg.port, "fully qualified domain name")
- flag.StringVar(&cfg.docRoot, "d", cfg.docRoot, "document root directory")
- flag.StringVar(&cfg.header, "H", cfg.header, "index header")
+ flag.StringVar(&cfg.fqdn, "f", cfg.fqdn, "fully qualified domain name")
+ flag.StringVar(&cfg.docRoot, "r", cfg.docRoot, "document root directory")
+ flag.StringVar(&cfg.message, "M", cfg.message, "index greeter message")
+ flag.BoolVar(&help, "h", help, "show this help page")
+ flag.BoolVar(&cfg.fsymln, "s", cfg.fsymln, "follow symbolic links")
flag.BoolVar(&cfg.verbose, "v", cfg.verbose, "produce verbose output")
flag.Parse()
+ if help {
+ flag.Usage()
+ os.Exit(1)
+ }
if 0 != len(flag.Args()) {
fmt.Println("unrecognized options: ", flag.Args())
flag.Usage()
cfg.docRoot, err = canonicalizePath(cfg.docRoot)
checkFatal(err, "canonicalizePath "+cfg.docRoot)
+ if cfg.message != "" {
+ cfg.message = fmt.Sprintf("i%s\t\t\t\r\n", cfg.message)
+ }
+
tracer.Print("interface: ", cfg.iface)
tracer.Print("TCP port: ", cfg.port)
tracer.Print("doc root: ", cfg.docRoot)
+ tracer.Print("fsymlinks: ", cfg.fsymln)
tracer.Print("fqdn: ", cfg.fqdn)
- tracer.Print("header: ", cfg.header)
+ tracer.Print("message: ", cfg.message)
tracer.Print("verbose: ", cfg.verbose)
}
}
func canonicalizePath(path string) (string, error) {
- evp, err := filepath.EvalSymlinks(path)
- if check(err, "EvalSymlinks "+path) != nil {
- return path, err
- }
- abs, err := filepath.Abs(evp)
- if check(err, "Abs "+evp) != nil {
+ abs, err := filepath.Abs(path)
+ if check(err, "Abs "+path) != nil {
return path, err
}
return abs, err
}
-func validatePath(relpath string) (string, error) {
- path, err := canonicalizePath(cfg.docRoot + pathSep + relpath)
- if check(err, "canonicalizePath "+relpath) != nil {
+func validatePath(root string, path string) (string, error) {
+ cpath, err := canonicalizePath(path)
+ if check(err, "canonicalizePath "+path) != nil {
return path, err
}
- if len(path) < len(cfg.docRoot) || path[:len(cfg.docRoot)] != cfg.docRoot {
- return path, errors.New("Path outside docRoot")
+ if len(cpath) < len(root) || cpath[:len(root)] != root {
+ return cpath, errors.New("Path outside root")
}
- return path, err
+ return cpath, err
}
func guessFiletype(path string) (string, error) {
// Get the content
buffer := make([]byte, 512)
n, err := f.Read(buffer)
- if check(err, "Read "+path) != nil {
+ if err != io.EOF && check(err, "Read "+path) != nil {
return "i", err
}
buffer = buffer[:n]
func createIndex(selector string) (string, error) {
dirname := cfg.docRoot + selector
- d, err := os.Open(dirname)
- defer d.Close()
- if check(err, "Open "+dirname) != nil {
- return "", err
- }
- fi, err := d.Readdir(-1)
+ fi, err := ioutil.ReadDir(dirname)
if check(err, "Readdir "+dirname) != nil {
return "", err
}
loc := "\t" + cfg.fqdn + "\t" + cfg.port + "\r\n"
- list := "1HOME\t/" + loc
+ list := ""
updir, _ := filepath.Split(selector)
- list += "1..\t" + updir + loc
+ if dirname != cfg.docRoot {
+ list += "1..\t" + updir + loc
+ }
for _, fi := range fi {
fmode := fi.Mode()
if fmode.IsDir() {
// create a file reference
ftype, _ := guessFiletype(dirname + pathSep + fi.Name())
list += ftype + fi.Name() + " (" + humanSize(fi.Size()) + ")\t" + selector + pathSep + fi.Name() + loc
- } else if fmode&os.ModeSymlink != 0 {
- // create a reference according to link target
+ } else if cfg.fsymln == true && fmode&os.ModeSymlink != 0 {
+ // create a reference with a type matching the link target
linktarget, _ := os.Readlink(dirname + pathSep + fi.Name())
- path, err := validatePath(linktarget)
- if check(err, "validatePath "+path) == nil {
- tracer.Print("path: '", path, "'")
+ if linktarget[:1] != pathSep {
+ linktarget = dirname + pathSep + linktarget
+ }
+ path, err := canonicalizePath(linktarget)
+ if check(err, "canonicalizePath "+path) == nil {
lfi, err := os.Stat(path)
if check(err, "Stat "+path) == nil {
if lfi.IsDir() {
}
req = strings.TrimSpace(req)
tracer.Print("request: '", req, "'")
- // evaluate, canonicalize, and validate referenced path
- path, err := validatePath(req)
+ // canonicalize, and validate referenced path
+ path, err := validatePath(cfg.docRoot, cfg.docRoot+pathSep+req)
if check(err, "validatePath "+path) != nil {
return
}
- tracer.Print("path: '", path, "'")
+ tracer.Print("request path: '", path, "'")
+ // check for symbolic link
+ if cfg.fsymln == false {
+ fi, err := os.Lstat(path)
+ if check(err, "request path Lstat "+path) != nil {
+ return
+ }
+ if fi.Mode()&os.ModeSymlink != 0 {
+ return
+ }
+ }
+ // stat the file
fi, err := os.Stat(path)
- if check(err, "Stat "+path) != nil {
+ if check(err, "request path Stat "+path) != nil {
return
}
fmode := fi.Mode()
if check(err, "makeIndex "+path) != nil {
return
}
- nb, err := conn.Write([]byte(cfg.header + diridx))
+ nb, err := conn.Write([]byte(cfg.message + diridx))
if check(err, "Write") != nil {
return
}
return
}
} else {
- // TODO: handle other cases?
- nbytes = -1
+ return
}
tracer.Print(strconv.FormatInt(nbytes, 10) + " bytes sent.")
return