瀏覽代碼

- do not expose sync.Mutex
- content type more flexible to white-space and missing charset param

flashmob 6 年之前
父節點
當前提交
fb8dcd69d1
共有 2 個文件被更改,包括 58 次插入57 次删除
  1. 45 56
      mail/mime/mime.go
  2. 13 1
      mail/mime/mime_test.go

+ 45 - 56
mail/mime/mime.go

@@ -10,9 +10,10 @@ import (
 	"sync"
 )
 
-// todo
-// - content-disposition
-// - make the error available
+// - TODO support RFC 2047 provides support for non-US-ASCII character sets in RFC 822
+//   message header comments
+// - not
+//
 
 type mimepart struct {
 	/*
@@ -64,7 +65,7 @@ type Parser struct {
 	boundaryMatched       int
 	count                 uint
 	result                chan parserMsg
-	sync.Mutex
+	mux                   sync.Mutex
 	// mime variables
 
 	// Parts is the mime parts tree. The parser builds the parts as it consumes the input
@@ -133,18 +134,14 @@ func (c *contentType) params() (ret string) {
 	defer func() {
 		c.b.Reset()
 	}()
-	sp := ""
-	for k, v := range c.parameters {
-		c.b.WriteString(k + "=\"" + v + "\"" + sp)
-		if len(c.parameters) > 1 && sp == "" {
-			sp = " "
-		}
+	for k := range c.parameters {
+		c.b.WriteString("; " + k + "=\"" + c.parameters[k] + "\"")
 	}
 	return c.b.String()
 }
 
 func (c *contentType) String() (ret string) {
-	ret = fmt.Sprintf("%s/%s %s", c.superType, c.subType,
+	ret = fmt.Sprintf("%s/%s%s", c.superType, c.subType,
 		c.params())
 	return
 }
@@ -480,17 +477,6 @@ func (p *Parser) isWSP(b byte) bool {
 	return b == ' ' || b == '\t'
 }
 
-// type "/" subtype
-// *(";" parameter)
-
-// content disposition
-// The Content-Disposition Header Field (rfc2183)
-// https://stackoverflow.com/questions/48347574/do-rfc-standards-require-the-filename-value-for-mime-attachment-to-be-encapsulat
-func (p *Parser) contentDisposition() (result contentType, err error) {
-	result = contentType{}
-	return
-}
-
 func (p *Parser) contentType() (result contentType, err error) {
 	result = contentType{}
 
@@ -505,42 +491,45 @@ func (p *Parser) contentType() (result contentType, err error) {
 	if result.subType, err = p.mimeSubType(); err != nil {
 		return
 	}
-	if p.ch == ';' {
-		p.next()
-		for {
-			if p.ch == '\n' {
-				c := p.peek()
-				if p.isWSP(c) {
-					p.next() // skip \n (FWS)
-					continue
-				}
-				if c == '\n' { // end of header
-					return
-				}
-			}
-			if p.isWSP(p.ch) { // skip WSP
-				p.next()
+
+	for {
+		if p.ch == ';' {
+			p.next()
+			continue
+		}
+		if p.ch == '\n' {
+			c := p.peek()
+			if p.isWSP(c) {
+				p.next() // skip \n (FWS)
 				continue
 			}
-			if p.ch == '(' {
-				if err = p.comment(); err != nil {
-					return
-				}
-				continue
+			if c == '\n' { // end of header
+				return
+			}
+		}
+		if p.isWSP(p.ch) { // skip WSP
+			p.next()
+			continue
+		}
+		if p.ch == '(' {
+			if err = p.comment(); err != nil {
+				return
 			}
+			continue
+		}
 
-			if p.ch > 32 && p.ch < 128 && !isTokenSpecial[p.ch] {
-				if key, val, err := p.parameter(); err != nil {
-					return result, err
-				} else {
-					if result.parameters == nil {
-						result.parameters = make(map[string]string, 1)
-					}
-					result.parameters[key] = val
-				}
+		if p.ch > 32 && p.ch < 128 && !isTokenSpecial[p.ch] {
+			if key, val, err := p.parameter(); err != nil {
+				return result, err
 			} else {
-				break
+				// add the new parameter
+				if result.parameters == nil {
+					result.parameters = make(map[string]string, 1)
+				}
+				result.parameters[key] = val
 			}
+		} else {
+			break
 		}
 	}
 
@@ -841,10 +830,10 @@ func (p *Parser) reset() {
 // Close tells the MIME Parser there's no more data & waits for it to return a result
 // it will return an io.EOF error if no error with parsing MIME was detected
 func (p *Parser) Close() error {
-	p.Lock()
+	p.mux.Lock()
 	defer func() {
 		p.reset()
-		p.Unlock()
+		p.mux.Unlock()
 	}()
 	if p.count == 0 {
 		// already closed
@@ -868,9 +857,9 @@ func (p *Parser) Close() error {
 func (p *Parser) Parse(buf []byte) error {
 	defer func() {
 		p.count++
-		p.Unlock()
+		p.mux.Unlock()
 	}()
-	p.Lock()
+	p.mux.Lock()
 
 	// Feed the new slice. Assumes that the parser is blocked now, waiting
 	// for new data, or not started yet.

+ 13 - 1
mail/mime/mime_test.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"io"
 	"strconv"
+	"strings"
 	"testing"
 	"time"
 )
@@ -60,11 +61,13 @@ func TestMimeContentType(t *testing.T) {
 		<-p.consumed
 		p.gotNewSlice <- false
 	}()
-	p.inject([]byte("text/plain; charset=us-ascii"))
+	subject := "text/plain; charset=\"us-ascii\"; boundary=\"foo\""
+	p.inject([]byte(subject))
 	contentType, err := p.contentType()
 	if err != nil {
 		t.Error(err)
 	}
+
 	if contentType.subType != "plain" {
 		t.Error("contentType.subType expecting 'plain', got:", contentType.subType)
 	}
@@ -72,6 +75,10 @@ func TestMimeContentType(t *testing.T) {
 	if contentType.superType != "text" {
 		t.Error("contentType.subType expecting 'text', got:", contentType.superType)
 	}
+
+	if ct := contentType.String(); strings.Compare(contentType.String(), subject) != 0 {
+		t.Error("\n[" + ct + "]\ndoes not equal\n[" + subject + "]")
+	}
 }
 
 func TestEmailHeader(t *testing.T) {
@@ -483,5 +490,10 @@ func TestNonMineEmail(t *testing.T) {
 			fmt.Println(p.Parts[part].Part + "  " + strconv.Itoa(int(p.Parts[part].StartingPos)) + "  " + strconv.Itoa(int(p.Parts[part].StartingPosBody)) + "  " + strconv.Itoa(int(p.Parts[part].EndingPosBody)))
 		}
 	}
+	p.Close()
+	p.inject([]byte{' '})
+	if err := p.mime(nil, ""); err != nil && err != NotMime && err != io.EOF {
+		t.Error(err)
+	}
 
 }