|
@@ -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))
|
|
|
+}
|