From c79e605b60040c4c0e5c792fa447487c2b8ae246 Mon Sep 17 00:00:00 2001 From: horus_arch Date: Mon, 15 Jun 2015 13:38:47 +0200 Subject: Use flex to extract html. Icono-font used for icons. --- Makefile | 13 ++++ favicon.go | 7 ++ intercept.go | 107 ++++++++++++++++++++++++++++ main.go | 36 +++++++++- scanner.go | 56 +++++++++++++++ sort.go | 17 +++++ template.go | 224 ----------------------------------------------------------- upload.go | 9 ++- urlscanner.h | 18 +++++ urlscanner.l | 52 ++++++++++++++ view.go | 112 ++++++++++++++++++++++++++++++ 11 files changed, 423 insertions(+), 228 deletions(-) create mode 100644 Makefile create mode 100644 favicon.go create mode 100644 intercept.go create mode 100644 scanner.go create mode 100644 sort.go delete mode 100644 template.go create mode 100644 urlscanner.h create mode 100644 urlscanner.l create mode 100644 view.go diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1575525 --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +LEX := flex +LFLAGS := +CC := gcc +CFLAGS := -Wall -pedantic +APP := uhttpd + +all: clean urlscanner.o scanner.go + go build -o $(APP) + +urlscanner.o: urlscanner.h + +clean: + rm -f *.o $(APP) *.exe diff --git a/favicon.go b/favicon.go new file mode 100644 index 0000000..e958a32 --- /dev/null +++ b/favicon.go @@ -0,0 +1,7 @@ +package main + +import "html/template" + +func getFavicon() template.HTML { + return template.HTML("") +} diff --git a/intercept.go b/intercept.go new file mode 100644 index 0000000..8201f3e --- /dev/null +++ b/intercept.go @@ -0,0 +1,107 @@ +package main + +import ( + "html/template" + "log" + "net/http" + "sort" + "strings" +) + +type TemplateHandler struct { + handler http.Handler +} + +func (t *TemplateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + + w.Header().Set("Server", "uhttpd") + + // We use our custom handler to prettify directories. Files are served via default. + if !strings.HasSuffix(r.URL.Path, "/") { + t.handler.ServeHTTP(w, r) + return + } + + rec := NewRecorder() + + defer rec.Body.Reset() + + // passing the recorder instead of the real ResponseWriter + t.handler.ServeHTTP(rec, r) + + // let the standard lib handle all the caching + if rec.Code > 300 && rec.Code < 400 { + log.Println("Code: ", rec.Code) + t.handler.ServeHTTP(w, r) + return + } + + // we copy the original headers first + for k, v := range rec.Header() { + w.Header()[k] = v + } + + // not found + if rec.Code == 404 { + w.Header().Set("Content-Type", "text/html") + w.WriteHeader(404) + tmpl := template.New("404") + tmpl, err := tmpl.Parse(get404()) + if err != nil { + log.Println(err.Error()) + w.WriteHeader(500) + w.Write([]byte(err.Error())) + return + } + + err = tmpl.Execute(w, struct{ URL string }{URL: r.URL.Path}) + if err != nil { + log.Println(err.Error()) + w.WriteHeader(500) + w.Write([]byte(err.Error())) + return + } + return + } + + // we serve a file instead of a html page + if !strings.Contains(w.Header().Get("Content-Type"), "text/html") { + w.Write(rec.Body.Bytes()) + return + } + + data := rec.Body.String() + + // we serve the directoy page + if strings.HasPrefix(data, "
") {
+ execTemplate(w, r, data)
+ } else {
+ w.Write(rec.Body.Bytes())
+ }
+}
+
+func execTemplate(w http.ResponseWriter, r *http.Request, data string) {
+
+ links := getToken(data)
+ sort.Sort(LinksAsSlice(links)) // see sort.go
+
+ tmpl := template.New("page")
+ tmpl, err := tmpl.Parse(getTemplate())
+ if err != nil {
+ log.Println(err.Error())
+ w.WriteHeader(500)
+ w.Write([]byte(err.Error()))
+ return
+ }
+
+ err = tmpl.Execute(w, struct {
+ Links []Link
+ URL string
+ Favicon template.HTML
+ AllowUpload bool
+ }{Links: links, URL: r.URL.Path, Favicon: getFavicon(), AllowUpload: _allow_upload})
+ if err != nil {
+ log.Println(err)
+ }
+
+}
diff --git a/main.go b/main.go
index c17edd1..fd2189f 100644
--- a/main.go
+++ b/main.go
@@ -4,6 +4,7 @@ import (
"flag"
"fmt"
"log"
+ "net"
"net/http"
"os"
)
@@ -19,6 +20,8 @@ func accessLog(h http.Handler, quiet bool) http.Handler {
return http.HandlerFunc(fn)
}
+var _allow_upload bool // todo: inject as dependency
+
func main() {
ip_f := flag.String("ip", "0.0.0.0", "IP adress to listen on.")
port_f := flag.String("port", "3000", "Port to listen on.")
@@ -35,13 +38,44 @@ func main() {
}
if !*quiet_f {
- fmt.Println("Starting uhttpd serving \"" + *dir_f + "\" on " + *ip_f + ":" + port + ".")
+ fmt.Print("You started µhttpd listening on " + *ip_f + ":" + port + " serving \"" + *dir_f + "\" as content.")
+ if !*disallow_upl_f {
+ fmt.Println(" Upload is enabled and accessible under /upload.")
+ } else {
+ fmt.Println(" Upload is disabled.")
+ }
+ fmt.Print("To view open your browser and try to open this as a url: ")
+
+ if *ip_f == "0.0.0.0" {
+ addrs, err := net.InterfaceAddrs()
+ if err != nil {
+ log.Fatal(err)
+ return
+ }
+
+ for cnt, adr := range addrs {
+ if ipnet, ok := adr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
+ if ipnet.IP.To4() != nil {
+ if cnt > 1 {
+ fmt.Printf(" or http://%s:%s/", ipnet.IP.String(), port)
+ } else {
+ fmt.Printf("http://%s:%s/", ipnet.IP.String(), port)
+ }
+ }
+ }
+ }
+ } else {
+ fmt.Print("http://" + *ip_f + ":" + port + "/")
+ }
+ fmt.Print("\n\n")
+
}
mux := http.NewServeMux()
if !*disallow_upl_f {
os.MkdirAll(*upl_dir_f, 0755)
mux.Handle("/upload", uploadHandler(*upl_dir_f, *quiet_f))
+ _allow_upload = true
}
mux.Handle("/", accessLog(http.FileServer(http.Dir(*dir_f)), *quiet_f))
log.Fatal(http.ListenAndServe(*ip_f+":"+port, mux))
diff --git a/scanner.go b/scanner.go
new file mode 100644
index 0000000..10be11b
--- /dev/null
+++ b/scanner.go
@@ -0,0 +1,56 @@
+package main
+
+/*
+#include
+#include
+#include "urlscanner.h"
+*/
+import "C"
+import (
+ "log"
+ "strings"
+)
+
+type Link struct {
+ Url string
+ Text string
+ IsDir bool
+}
+
+func getToken(input string) []Link {
+ var token C.int
+ var ls []Link
+ var l Link
+
+ C.scan_string(C.CString(input))
+
+ for token = C.yylex(); token != C.MYEOF; token = C.yylex() {
+
+ if token == C.TOKEN_URL {
+ // flex reads the href attr
+
+ l.Url = C.GoString(C.yylval)
+
+ if strings.HasSuffix(l.Url, "/") {
+ l.IsDir = true
+ }
+
+ } else if token == C.TOKEN_TEXT {
+ // flex reads the link description
+
+ l.Text = C.GoString(C.yylval)
+ ls = append(ls, l)
+ l = Link{}
+ } else {
+ // lexical error
+
+ l = Link{}
+ log.Printf("Lexical Error on line %d \n", C.yylineno)
+ continue
+ }
+ }
+
+ return ls
+}
+
+// sort
diff --git a/sort.go b/sort.go
new file mode 100644
index 0000000..6f34797
--- /dev/null
+++ b/sort.go
@@ -0,0 +1,17 @@
+package main
+
+// Dirty hack. Source:
+// https://stackoverflow.com/questions/26526736/invalid-receiver-type-t-t-is-an-unnamed-type-workaround
+type LinksAsSlice []Link
+
+func (slice LinksAsSlice) Len() int {
+ return len(slice)
+}
+
+func (slice LinksAsSlice) Less(i, j int) bool {
+ return slice[i].Text < slice[j].Text
+}
+
+func (slice LinksAsSlice) Swap(i, j int) {
+ slice[i], slice[j] = slice[j], slice[i]
+}
diff --git a/template.go b/template.go
deleted file mode 100644
index 7f9a134..0000000
--- a/template.go
+++ /dev/null
@@ -1,224 +0,0 @@
-package main
-
-import (
- "html/template"
- "log"
- "net/http"
- "strings"
-)
-
-type TemplateHandler struct {
- handler http.Handler
-}
-
-func (t *TemplateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-
- w.Header().Set("Server", "uhttpd")
-
- // We use our custom handler to prettify directories. Files are served via default.
- if !strings.HasSuffix(r.URL.Path, "/") {
- t.handler.ServeHTTP(w, r)
- return
- }
-
- rec := NewRecorder()
-
- defer rec.Body.Reset()
-
- // passing the recorder instead of the real ResponseWriter
- t.handler.ServeHTTP(rec, r)
-
- // let the standard lib handle all the caching
- if rec.Code > 300 && rec.Code < 400 {
- log.Println("Code: ", rec.Code)
- t.handler.ServeHTTP(w, r)
- return
- }
-
- // we copy the original headers first
- for k, v := range rec.Header() {
- w.Header()[k] = v
- }
-
- // not found
- if rec.Code == 404 {
- w.Header().Set("Content-Type", "text/html")
- w.WriteHeader(404)
- tmpl := template.New("404")
- tmpl, err := tmpl.Parse(get404())
- if err != nil {
- log.Println(err.Error())
- w.WriteHeader(500)
- w.Write([]byte(err.Error()))
- return
- }
-
- err = tmpl.Execute(w, struct{ URL string }{URL: r.URL.Path})
- if err != nil {
- log.Println(err.Error())
- w.WriteHeader(500)
- w.Write([]byte(err.Error()))
- return
- }
- return
- }
-
- // we serve a file instead of a html page
- if !strings.Contains(w.Header().Get("Content-Type"), "text/html") {
- w.Write(rec.Body.Bytes())
- return
- }
-
- data := rec.Body.String()
-
- // we serve the directoy page
- if strings.HasPrefix(data, "") {
- execTemplate(w, r, data)
- } else {
- w.Write(rec.Body.Bytes())
- }
-}
-
-func execTemplate(w http.ResponseWriter, r *http.Request, data string) {
- data = strings.Replace(data, "", "", 1)
- data = strings.Replace(data, "
", "", 1)
- data = strings.Replace(data, "", "
", -1)
-
- tmpl := template.New("page")
- tmpl, err := tmpl.Parse(getTemplate())
- if err != nil {
- log.Println(err.Error())
- w.WriteHeader(500)
- w.Write([]byte(err.Error()))
- return
- }
-
- err = tmpl.Execute(w, struct {
- Data template.HTML
- URL string
- }{Data: template.HTML(data), URL: r.URL.Path})
- if err != nil {
- log.Println(err)
- }
-
-}
-
-func getTemplate() string {
- return `
-
-
-
- {{.URL}}
-
-
-
-
-
-
-
-
-
- {{.URL}}
-
- {{if ne .URL "/"}}
- .. (up a dir)
- {{end}}
- {{.Data}}
-
-
-
-
-
-
-`
-}
-
-func get404() string {
- return `
-
-
-
- Not Found - {{.URL}}
-
-
-
-
-
-
-
-
-
-`
-}
-
-func getUpload() string {
- return `
-
-
-
- Upload Form
-
-
-
-
-
-
-
- Upload Form
-
-
-
-
-
-`
-}
-
-func getUploaded() string {
- return `
-
-
-
- Uploaded {{.File}}
-
-
-
-
-
-
-
-
-
-`
-}
diff --git a/upload.go b/upload.go
index 6e0ce56..daefd47 100644
--- a/upload.go
+++ b/upload.go
@@ -12,7 +12,7 @@ import (
func uploadHandler(dir string, quiet bool) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
- // uhttpd strong
+ // µhttpd strong
w.Header().Set("Server", "uhttpd")
// we are handling the upload
@@ -75,7 +75,10 @@ func uploadHandler(dir string, quiet bool) http.Handler {
return
}
- err = tmpl.Execute(w, struct{ File string }{File: header.Filename})
+ err = tmpl.Execute(w, struct {
+ File string
+ Favicon template.HTML
+ }{File: header.Filename, Favicon: getFavicon()})
if err != nil {
log.Println(err.Error())
w.WriteHeader(500)
@@ -96,7 +99,7 @@ func uploadHandler(dir string, quiet bool) http.Handler {
return
}
- err = tmpl.Execute(w, nil)
+ err = tmpl.Execute(w, struct{ Favicon template.HTML }{Favicon: getFavicon()})
if err != nil {
log.Println(err.Error())
w.WriteHeader(500)
diff --git a/urlscanner.h b/urlscanner.h
new file mode 100644
index 0000000..f815c41
--- /dev/null
+++ b/urlscanner.h
@@ -0,0 +1,18 @@
+#ifndef URLSCANNER_H
+#define URLSCANNER_H
+
+#define MYEOF EOF
+#define TOKEN_URL 257
+#define TOKEN_TEXT 258
+
+#ifndef YYSTYPE
+#define YYSTYPE yystype
+typedef char* yystype;
+#endif
+
+extern int yylex();
+extern yystype yylval;
+extern int yylineno;
+extern void scan_string ( const char *str );
+
+#endif
diff --git a/urlscanner.l b/urlscanner.l
new file mode 100644
index 0000000..5f9acb6
--- /dev/null
+++ b/urlscanner.l
@@ -0,0 +1,52 @@
+%option noyywrap
+%option yylineno
+%option nounput
+%option nodefault
+
+%{
+#include "urlscanner.h"
+#include "string.h"
+yystype yylval;
+%}
+
+%x ATAG_HREF
+%x ATAG_BETWEEN
+%x ATAG_DESC
+
+%%
+
+[^<]* { }
+
+"]*"href=\"" { BEGIN(ATAG_HREF); }
+
+[^\"]+ {
+ yylval = strdup(yytext);
+ BEGIN(ATAG_BETWEEN);
+ return TOKEN_URL;
+}
+
+\" {
+ yylval = "";
+ BEGIN(ATAG_BETWEEN);
+ return TOKEN_URL;
+}
+
+[^>]* { }
+
+">" { BEGIN(ATAG_DESC); }
+
+[^<]* {
+ yylval = strdup(yytext);
+ BEGIN(INITIAL);
+ return TOKEN_TEXT;
+}
+
+. { /* skip */ }
+
+<> { return MYEOF; }
+
+%%
+
+void scan_string(const char* str) {
+ yy_switch_to_buffer(yy_scan_string(str));
+}
diff --git a/view.go b/view.go
new file mode 100644
index 0000000..3530bf0
--- /dev/null
+++ b/view.go
@@ -0,0 +1,112 @@
+package main
+
+func getTemplate() string {
+ return `
+
+
+
+ {{.URL}}
+
+
+
+ {{.Favicon}}
+
+
+
+
+
+ {{.URL}}
+ {{.URL}}
+ {{if .AllowUpload}}To upload please click here.
{{end}}
+
+ {{if ne .URL "/"}}
+ .. (up a dir)
+ {{end}}
+ {{range .Links}}
+ {{else}}file" title="File: {{.Text}}">{{end}} {{.Text}}
+ {{end}}
+
+
+
+
+
+`
+}
+
+func get404() string {
+ return `
+
+
+
+ Not Found - {{.URL}}
+
+
+ {{.Favicon}}
+
+
+
+
+
+
+`
+}
+
+func getUpload() string {
+ return `
+
+
+
+ Upload Form
+
+
+ {{.Favicon}}
+
+
+
+
+ Upload Form
+
+
+
+
+
+`
+}
+
+func getUploaded() string {
+ return `
+
+
+
+ Uploaded {{.File}}
+
+
+ {{.Favicon}}
+
+
+
+
+
+
+`
+}
--
cgit v1.2.3