/* * MIT License * * Copyright (c) 2020 Alexey Edelev * * 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, "\n\n\n\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, "
\n") fmt.Fprintf(w, "From:%s
\n", html.EscapeString(email.from)) fmt.Fprintf(w, "Subject:%s
\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, "\n") } func main() { http.HandleFunc("/", handler) log.Fatal(http.ListenAndServe(":8080", nil)) }