💾 Archived View for tozip.chickenkiller.com › sss.go captured on 2023-03-20 at 17:59:53.

View Raw

More Information

⬅️ Previous capture (2022-07-16)

➡️ Next capture (2023-06-16)

🚧 View Differences

-=-=-=-=-=-=-

/* simple server for the spartan protocol
 */

package main

import (
	"bufio"
	"errors"
	"fmt"
	"net"
	"os"
	"path/filepath"
	"strings"
)

const (
	//CONN_HOST = "localhost"
	//CONN_HOST = "0.0.0.0"
	//CONN_HOST = ""
	CONN_HOST = "127.0.0.1"
	//CONN_HOST = "192.168.0.27"
	//CONN_HOST = "blinkyshark.chickenkiller.com"
	CONN_PORT = "3000"
	CONN_TYPE = "tcp"
	DATA_DIR  = "/home/pi/repos/gemtext"
)

func main() {
	// Listen for incoming connections.
	l, err := net.Listen(CONN_TYPE, CONN_HOST+":"+CONN_PORT)
	if err != nil {
		fmt.Println("Error listening:", err.Error())
		os.Exit(1)
	}
	// Close the listener when the application closes.
	defer l.Close()
	fmt.Println("Listening on " + CONN_HOST + ":" + CONN_PORT)
	for {
		// Listen for an incoming connection.
		conn, err := l.Accept()
		if err != nil {
			fmt.Println("Error accepting: ", err.Error())
			os.Exit(1)
		}
		// Handle connections in a new goroutine.
		go handleRequest(conn)
	}
}

func get_path(conn net.Conn) (string, error) {

	oops := func(msg string) (string, error) { return "", errors.New(msg) }

	buf := make([]byte, 1024) // buffer to hold incoming data
	reqLen, err := conn.Read(buf) // Read the incoming connection into the buffer.
	//fmt.Println("reqLen:", reqLen)
	if err != nil { return "", err }
	request := string(buf[:reqLen]) 
	request = strings.TrimRight(request, "\r\n")
	fields := strings.Fields(request)
	if len(fields) != 3 {
		str := "Bad request. Expected 3 fields, got '" + request + "'"
		fmt.Println(str)
		return oops(str) 
	}


	path_in := fields[1]
	if path_in == "/" { path_in = "index.gmi" }
	path, err := filepath.Abs(DATA_DIR + "/" + path_in)
	if err != nil { return "", err }

	fmt.Println("path requested:", path)

	// check for out-of-directory stuff
	if len(path) < len(DATA_DIR) { return oops("path name is too short") }
	if DATA_DIR != path[0:len(DATA_DIR)] { return oops("data root violated") }

	info, err := os.Stat(path)
	if err != nil { return "", err }
	if info.IsDir() { return oops("Won't serve directories") }

	fmt.Println("server=", fields[0])
	fmt.Println("loc=", path)
	fmt.Println("Received request ", string(buf[0:reqLen]))
	return path, err
}

// Handles incoming requests.
func handleRequest(conn net.Conn) {
	defer conn.Close()

	oops := func(err_str string) {
		err_str = "4 " + err_str;
		fmt.Println(err_str)
		conn.Write([]byte(err_str + "\r\n"))
	}

	pathname, err := get_path(conn)
	if err != nil { oops(err.Error()) ; return }
	
	fp, err := os.Open(pathname)
	if err != nil { oops("file not found") ; return }
	defer fp.Close()

	response := "2 text/gemini"
	if len(pathname) < 4 || pathname[len(pathname)-4:] != ".gmi" {
		response = "2 text/plain; charset=utf-8"
	}
	conn.Write([]byte(response + "\r\n"))

	scanner := bufio.NewScanner(fp)
	for scanner.Scan() {
		txt1 := scanner.Text()
		//fmt.Println([]byte(txt1))
		txt1 += "\r\n"
		conn.Write([]byte(txt1))
	}

	if err := scanner.Err(); err != nil { oops(err.Error()) }
}