Browse Source

Basic parser

Alexey Edelev 5 years ago
parent
commit
7f0302cd99
1 changed files with 195 additions and 0 deletions
  1. 195 0
      main.go

+ 195 - 0
main.go

@@ -0,0 +1,195 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of gostfix project https://git.semlanik.org/semlanik/gostfix
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package main
+
+import (
+	"bufio"
+	"fmt"
+	"html"
+	"log"
+	"net/http"
+	"os"
+	"regexp"
+	"strings"
+)
+
+const (
+	StateHeaderScan = iota
+	StateBodyScan
+	StateContentScan
+)
+
+const (
+	HeaderRegExp        = "^([\x21-\x7E^:]+):(.*)"
+	FoldingRegExp       = "^\\s+(.*)"
+	BoundaryStartRegExp = "^--(.*)"
+	BoundaryEndRegExp   = "^--(.*)--$"
+	BoundaryRegExp      = "boundary=\"(.*)\""
+)
+
+type Email struct {
+	from        string
+	to          string
+	cc          string
+	bcc         string
+	date        string
+	subject     string
+	contentType string
+	body        string
+}
+
+func NewEmail() *Email {
+	return &Email{
+		contentType: "plain/text",
+	}
+}
+
+func handler(w http.ResponseWriter, r *http.Request) {
+	fmt.Fprintf(w, "<!DOCTYPE html>\n<html lang=\"en\">\n<head><meta charset=\"utf-8\"/></head>\n<body>\n")
+
+	state := StateHeaderScan
+	headerFinder, err := regexp.Compile(HeaderRegExp)
+	if err != nil {
+		log.Fatalf("Invalid regexp %s\n", err)
+	}
+
+	foldingFinder, err := regexp.Compile(FoldingRegExp)
+	if err != nil {
+		log.Fatalf("Invalid regexp %s\n", err)
+	}
+
+	boundaryStartFinder, err := regexp.Compile(BoundaryStartRegExp)
+	if err != nil {
+		log.Fatalf("Invalid regexp %s\n", err)
+	}
+
+	boundaryEndFinder, err := regexp.Compile(BoundaryEndRegExp)
+	if err != nil {
+		log.Fatalf("Invalid regexp %s\n", err)
+	}
+
+	boundaryFinder, err := regexp.Compile(BoundaryRegExp)
+
+	file, _ := os.Open("./semlanik")
+	scanner := bufio.NewScanner(file)
+	activeBoundary := ""
+	var previousHeader *string = nil
+	for email := NewEmail(); scanner.Scan(); {
+		if scanner.Text() == "" {
+			if state == StateHeaderScan {
+				boundaryCapture := boundaryFinder.FindStringSubmatch(email.contentType)
+				if len(boundaryCapture) == 2 {
+					activeBoundary = boundaryCapture[1]
+				} else {
+					activeBoundary = ""
+				}
+				state = StateBodyScan
+				fmt.Printf("--------------------------Start body scan content type:%s boundary: %s -------------------------\n", email.contentType, activeBoundary)
+			} else if state == StateBodyScan {
+				// fmt.Printf("--------------------------Previous email body activeBoundary: %s -------------------------\n%s\n", activeBoundary, email.body)
+				// if activeBoundary != "" {
+				// 	r := strings.NewReader(email.body)
+				// 	multipartReader := multipart.NewReader(r, activeBoundary)
+				// 	for part, err := multipartReader.NextPart(); err == nil; {
+				// 		fmt.Printf("Body content type: %s\n", part.Header.Get("Content-type"))
+				// 	}
+				// }
+				fmt.Printf("--------------------------Previous email-------------------------\n%v\n", email)
+
+				fmt.Fprintf(w, "<div>\n")
+				fmt.Fprintf(w, "<b>From:</b>%s<br/>\n", html.EscapeString(email.from))
+				fmt.Fprintf(w, "<b>Subject:</b>%s<br/>\n", html.EscapeString(email.subject))
+				previousHeader = nil
+				activeBoundary = ""
+				email = NewEmail()
+				state = StateHeaderScan
+			} else {
+				fmt.Printf("Empty line in state %d\n", state)
+			}
+		}
+
+		if state == StateHeaderScan {
+			capture := headerFinder.FindStringSubmatch(scanner.Text())
+			if len(capture) == 3 {
+				fmt.Printf("capture Header %s : %s\n", strings.ToLower(capture[0]), strings.ToLower(capture[1]))
+				header := strings.ToLower(capture[1])
+				switch header {
+				case "from":
+					previousHeader = &email.from
+				case "to":
+					previousHeader = &email.to
+				case "cc":
+					previousHeader = &email.cc
+				case "bcc":
+					previousHeader = &email.bcc
+				case "subject":
+					previousHeader = &email.subject
+				case "date":
+					previousHeader = &email.date
+				case "content-type":
+					previousHeader = &email.contentType
+				default:
+					previousHeader = nil
+				}
+				if previousHeader != nil {
+					*previousHeader += capture[2]
+				}
+				continue
+			}
+
+			capture = foldingFinder.FindStringSubmatch(scanner.Text())
+			if len(capture) == 2 && previousHeader != nil {
+				*previousHeader += capture[1]
+				continue
+			}
+		} else {
+			email.body += scanner.Text() + "\n"
+			if activeBoundary != "" {
+				capture := boundaryEndFinder.FindStringSubmatch(scanner.Text())
+				if len(capture) == 2 {
+					fmt.Printf("capture Boundary End %s\n", capture[1])
+					if activeBoundary == capture[1] {
+						state = StateBodyScan
+					}
+
+					continue
+				}
+				capture = boundaryStartFinder.FindStringSubmatch(scanner.Text())
+				if len(capture) == 2 {
+					fmt.Printf("capture Boundary Start %s\n", capture[1])
+					state = StateContentScan
+					continue
+				}
+			}
+		}
+	}
+	fmt.Fprintf(w, "</body>\n</html>")
+}
+
+func main() {
+	http.HandleFunc("/", handler)
+	log.Fatal(http.ListenAndServe(":8080", nil))
+}