* initial commit
authorUrban Wallasch <urban.wallasch@freenet.de>
Wed, 3 Apr 2019 19:13:43 +0000 (21:13 +0200)
committerUrban Wallasch <urban.wallasch@freenet.de>
Wed, 3 Apr 2019 19:13:43 +0000 (21:13 +0200)
.gitignore [new file with mode: 0644]
LICENSE [new file with mode: 0644]
Makefile [new file with mode: 0644]
dummy.txt [new file with mode: 0644]
gogopherd.go [new file with mode: 0644]
index.gox [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..e4e58c6
--- /dev/null
@@ -0,0 +1,2 @@
+gogopherd
+stuff
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..9d4c0ba
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2016, Urban Wallasch
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..1603da1
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,29 @@
+#######################################
+##  Makefile for project: gogopherd  ##
+#######################################
+
+PROJECT := gogopherd
+
+GOBLD   := go build
+
+BIN     := $(PROJECT)
+SRC     := $(wildcard *.go)
+SELF    := $(lastword $(MAKEFILE_LIST))
+
+
+.PHONY: all clean distclean
+
+all: $(BIN)
+
+$(BIN): $(SRC) $(SELF)
+       $(GOBLD) $(SRVSRC)
+
+clean:
+       $(RM) $(BIN)
+
+distclean: clean
+
+
+###########
+##  EOF  ##
+###########
diff --git a/dummy.txt b/dummy.txt
new file mode 100644 (file)
index 0000000..e2da9dc
--- /dev/null
+++ b/dummy.txt
@@ -0,0 +1,2 @@
+Hello, world!
+Delirium Solarium Besenstiel
diff --git a/gogopherd.go b/gogopherd.go
new file mode 100644 (file)
index 0000000..c1aadd7
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * This file is part of the gogopherd project.
+ *
+ * Copyright 2019 Urban Wallasch <irrwahn35@freenet.de>
+ * See LICENSE file for more details.
+ *
+ */
+/*
+ * Gogopherd- a minimalistic gopher server written in Go.
+ *
+ * Changelog:
+ * 2019-04-03   Initial version.
+ */
+
+package main
+
+import (
+       "bufio"
+       "flag"
+       "fmt"
+       "io/ioutil"
+       "log"
+       "net"
+       "os"
+       "strings"
+)
+
+var (
+       index  []string
+       logger *log.Logger
+       tracer *log.Logger
+)
+
+var cfg = struct {
+       bindIface   string
+       bindPortTcp string
+       fqdn        string
+       docRoot     string
+       indexFile   string
+       verbose     bool
+}{
+       bindIface:   "localhost",
+       bindPortTcp: "7070",
+       fqdn:        "localhost",
+       docRoot:     ".",
+       indexFile:   "index.gox",
+       verbose:     false,
+}
+
+func initialize() {
+       flag.StringVar(&cfg.bindIface, "i", cfg.bindIface, "interface to bind to")
+       flag.StringVar(&cfg.bindPortTcp, "p", cfg.bindPortTcp, "TCP port to listen on")
+       flag.StringVar(&cfg.docRoot, "d", cfg.docRoot, "document root directory")
+       flag.StringVar(&cfg.indexFile, "x", cfg.indexFile, "index file")
+       flag.BoolVar(&cfg.verbose, "v", cfg.verbose, "produce verbose output")
+       flag.Parse()
+       if 0 != len(flag.Args()) {
+               fmt.Println("unrecognized options: ", flag.Args())
+               flag.Usage()
+               os.Exit(1)
+       }
+
+       logger = log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Lshortfile)
+       if cfg.verbose {
+               tracer = log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Lshortfile)
+       } else {
+               tracer = log.New(ioutil.Discard, "", 0)
+       }
+
+       tracer.Print("interface:  ", cfg.bindIface)
+       tracer.Print("TCP port:   ", cfg.bindPortTcp)
+       tracer.Print("doc root:   ", cfg.docRoot)
+       tracer.Print("index file: ", cfg.indexFile)
+       tracer.Print("verbose:    ", cfg.verbose)
+}
+
+func checkFatal(err error, msg string) {
+       if err != nil {
+               logger.Print(msg, ": ", err.Error())
+               os.Exit(1)
+       }
+}
+
+func check(err error, msg string)(error) {
+       if err != nil {
+               tracer.Print(msg, ": ", err.Error())
+       }
+       return err
+}
+
+func getErrorString(code string) (string) {
+       // TODO flesh this out
+       res := "Error: " + code + " Resource not found. "
+       return res;
+}
+
+func sanitizeSelector(selector string) (string) {
+       selector = strings.Replace(selector, "\r", "", -1)
+       selector = strings.Replace(selector, "\n", "", -1)
+       selector = strings.Replace(selector, "../", "/", -1)
+       selector = strings.Replace(selector, "//", "/", -1)
+       selector = strings.Replace(selector, "//", "/", -1)
+       return selector
+}
+
+func getResource(selector string) ([]byte, error) {
+       if ( selector == "" || selector == "/" ) {
+               selector = cfg.indexFile
+       }
+       resPath := cfg.docRoot + "/" + selector
+       res, err := ioutil.ReadFile(resPath)
+       if check(err,"ReadFile " + resPath) != nil { res = []byte(""); }
+       return res, err;
+}
+
+func handleRequest(conn net.Conn) {
+       defer conn.Close()
+       reply := []byte("")
+       req, err := bufio.NewReader(conn).ReadString('\n')
+       if ( check(err,"ReadString") != nil ) {
+               return
+       } else {
+               tracer.Print("request: '", req, "'")
+               selector := sanitizeSelector(req)
+               tracer.Print("selector: '", selector, "'")
+               reply, err = getResource(selector)
+               if ( err != nil ) { reply = []byte(getErrorString("404") + selector) }
+       }
+       conn.Write(reply)
+}
+
+func serveTCP(sock net.Listener) {
+       for {
+               conn, err := sock.Accept()
+               checkFatal(err, "Accept")
+               tracer.Print("TCP connect from ", conn.RemoteAddr())
+               go handleRequest(conn)
+       }
+}
+
+func main() {
+       initialize()
+       bindaddr := cfg.bindIface + ":" + cfg.bindPortTcp
+       tsock, err := net.Listen("tcp", bindaddr)
+       checkFatal(err, "net.Listen tcp " + bindaddr)
+       defer tsock.Close()
+       tracer.Print("listening on TCP ", bindaddr)
+       go serveTCP(tsock)
+       // send ready signal
+       fmt.Println("")
+       // exit on any input on stdin
+       reader := bufio.NewReader(os.Stdin)
+       reader.ReadRune()
+}
+
+/* EOF */
diff --git a/index.gox b/index.gox
new file mode 100644 (file)
index 0000000..ded7352
--- /dev/null
+++ b/index.gox
@@ -0,0 +1,5 @@
+iWelcome to gogopherd on localhost\r
+i---------------------------------\r
+0dummy.txt     /dummy.txt      localhost       7070\r
+0gogopherd source code /gogopherd.go   localhost       7070\r
+9gogopherd binary      /gogopherd      localhost       7070\r