package main import ( "bufio" // "fmt" "io" "log" "net" "os" "strings" ) const HOST = "0.0.0.0" const PORT = "7070" const HOSTNAME = "demiurge.shoko.home" const DIR = "./" func formatLine(line string) string { trimmed := strings.TrimRight(line, "\r\n") splitted := strings.Split(trimmed, "\t") if len(splitted) == 3 { return line } else if len(splitted) == 2 { line += "\t" + HOSTNAME + "\t" + PORT } else if len(splitted) == 1 { line += "\tErr\t" + HOSTNAME + "\t" + PORT } return line + "\n" } func writeError(c net.Conn, msg string) { c.Write([]byte(formatLine("3" + msg))) } func printGophermap(c net.Conn, dir string) { file, err := os.Open(dir + "/Gophermap") if err != nil { writeError(c, err.Error()) log.Println(err) } defer func() { if err = file.Close(); err != nil { log.Println(err) } }() scanner := bufio.NewScanner(file) for scanner.Scan() { c.Write([]byte(formatLine(scanner.Text()) + "\n")) } c.Write([]byte(".\r\n")) } func printFile(c net.Conn, path string) { file, err := os.Open(path) if err != nil { writeError(c, err.Error()) log.Println(err) } defer func() { if err = file.Close(); err != nil { log.Println(err) } }() // 1024 bytes or 1K for buffer const bufSz = 1024 b := make([]byte, bufSz) for { readSz, err := file.Read(b) if err != nil { if err != io.EOF { log.Println(err) } break } c.Write(b[:readSz]) } } func connHandle(c net.Conn) { data, err := bufio.NewReader(c).ReadString('\n') if err != nil { log.Println(err) return } selector := strings.TrimRight(data, "\r\n") log.Println(selector) if selector == "" { printGophermap(c, "./") } else if strings.Contains(selector, "..") { writeError(c, "Selector contains ..") } else if selector[0] == '/' { info, err := os.Stat(selector[1:]) if err != nil { writeError(c, err.Error()) log.Println(err) c.Close() return } if info.IsDir() { printGophermap(c, selector[1:]) } else { printFile(c, selector[1:]) } } else { writeError(c, "Selector doesn't start with a /") } c.Close() } func main() { const ADDR = HOST + ":" + PORT log.Printf("Starting thomomys on %s\n", ADDR) os.Chdir(DIR) l, err := net.Listen("tcp", ADDR) if err != nil { log.Fatal(err) return } defer l.Close() for { c, err := l.Accept() if err != nil { log.Println(err) return } go connHandle(c) } }