فهرست منبع

milestone - counts are correct

flashmob 6 سال پیش
والد
کامیت
6db20b95e5
2فایلهای تغییر یافته به همراه172 افزوده شده و 252 حذف شده
  1. 77 209
      backends/s_mime.go
  2. 95 43
      backends/s_mime_test.go

+ 77 - 209
backends/s_mime.go

@@ -80,9 +80,10 @@ type parser struct {
 	isHalting                   bool
 
 	// mime variables
-	parts   []*mimeHeader
-	msgPos  uint
-	msgLine uint
+	parts           []*mimeHeader
+	msgPos          uint
+	msgLine         uint
+	lastBoundaryPos uint
 }
 
 type mimeHeader struct {
@@ -210,6 +211,17 @@ func (p *parser) set(input []byte) {
 
 }
 
+func (p *parser) skip(nBytes int) {
+
+	for i := 0; i < nBytes; i++ {
+		p.next()
+		if p.ch == 0 {
+			return
+		}
+	}
+
+}
+
 // boundary scans until next boundary string, returns error if not found
 // syntax specified https://tools.ietf.org/html/rfc2046 p21
 func (p *parser) boundary(contentBoundary string) (end bool, err error) {
@@ -219,12 +231,8 @@ func (p *parser) boundary(contentBoundary string) (end bool, err error) {
 				p.next()
 			}
 		}
-		// todo: remove this
-		//temp := p.buf[p.pos-10:p.pos+45]
-		//_ = temp
 	}()
-	// gensen chosu
-	// shotoku chomen sho
+
 	if len(contentBoundary) < 1 {
 		err = errors.New("content boundary too short")
 	}
@@ -236,13 +244,16 @@ func (p *parser) boundary(contentBoundary string) (end bool, err error) {
 			// then let next() to advance the last char.
 			// in case the boundary is the tail part of buffer, calling next()
 			// will wait until we get a new buffer
-			p.pos = p.pos + i + len(boundary) - 1
-			p.next()
+
+			p.skip(i)
+			p.lastBoundaryPos = p.msgPos - 1 // - uint(len(boundary))
+			p.skip(len(boundary))
 			end = p.boundaryEnd()
 			p.transportPadding()
-			if p.ch != '\n' {
+			if p.ch != '\n' && p.ch != 0 {
 				err = errors.New("boundary new line expected")
 			}
+
 			return
 
 		} else {
@@ -262,8 +273,7 @@ func (p *parser) boundary(contentBoundary string) (end bool, err error) {
 					p.boundaryMatched = 0
 				}
 			}
-			p.pos = len(p.buf) - 1
-			p.next() // this will block until new bytes come in
+			p.skip(len(p.buf))
 			if p.ch == 0 {
 				return false, io.EOF
 			} else if p.boundaryMatched > 0 {
@@ -272,19 +282,21 @@ func (p *parser) boundary(contentBoundary string) (end bool, err error) {
 				if bytes.Compare(
 					p.buf[0:len(boundary)-p.boundaryMatched],
 					[]byte(boundary[p.boundaryMatched:])) == 0 {
+
 					// advance the pointer
-					p.pos += len(boundary) - p.boundaryMatched - 1
-					p.next()
+					p.skip(len(boundary) - p.boundaryMatched)
+
+					p.lastBoundaryPos = p.msgPos - 1 //- uint(len(boundary))
 					end = p.boundaryEnd()
 					p.transportPadding()
-					if p.ch != '\n' {
+					if p.ch != '\n' && p.ch != 0 {
 						err = errors.New("boundary new line expected")
 					}
 					return
 				}
 				p.boundaryMatched = 0
 			}
-			_ = subject
+			//_ = subject
 		}
 	}
 }
@@ -369,7 +381,7 @@ func (p *parser) header(mh *mimeHeader) (err error) {
 		}
 
 	}()
-	mh.startingPos = p.msgPos
+
 	for {
 
 		switch state {
@@ -714,7 +726,6 @@ func (p *parser) body(mh *mimeHeader) (err error) {
 		} else {
 			fmt.Println("boundary end:", end)
 		}
-		mh.endingPosBody = p.msgPos
 		return
 	} else {
 		for {
@@ -725,7 +736,6 @@ func (p *parser) body(mh *mimeHeader) (err error) {
 			}
 			if p.ch == '\n' && p.peek() == '\n' {
 				p.next()
-				mh.endingPosBody = p.msgPos
 				return
 			}
 
@@ -736,238 +746,96 @@ func (p *parser) body(mh *mimeHeader) (err error) {
 
 }
 
-func (p *parser) mime(boundary string, depth string) (err error) {
-	count := 1
-	h := NewMimeHeader()
-
-	if p.ch >= 33 && p.ch <= 126 {
-		err = p.header(h)
-		if err != nil {
-			//				temp := p.buf[p.pos:p.pos+20]
-			//				_ = temp
-			return err
+func (p *parser) isMulti(part *mimeHeader) bool {
+	if part.contentType != nil {
+		if part.contentType.superType == "multipart" ||
+			part.contentType.superType == "message" {
+			return true
 		}
-	} else {
-		fmt.Println("empty header")
-	}
-
-	if p.ch == '\n' && p.peek() == '\n' {
-		p.next()
-		p.next()
-	}
-	if h.contentBoundary != "" {
-		boundary = h.contentBoundary
-
 	}
+	return false
+}
 
-	if end, bErr := p.boundary(boundary); bErr != nil {
-		return bErr
-	} else if end {
-		h.endingPosBody = p.msgPos
-		return
-	}
+func (p *parser) multi(part *mimeHeader, depth string) (err error) {
+	if part.contentType != nil {
+		if part.contentType.superType == "multipart" {
+			if end, bErr := p.boundary(part.contentBoundary); bErr != nil {
+				return bErr
+			} else if end {
 
-	if depth == "1" {
-		p.addPart(h, depth)
-	} else {
-		p.addPart(h, depth+"."+strconv.Itoa(count))
-		if h.contentType != nil &&
-			(h.contentType.superType == "message" ||
-				h.contentType.superType == "multipart") {
-			return p.mime(boundary, depth+"."+strconv.Itoa(count))
-		} else {
-			count++
+				part.endingPosBody = p.lastBoundaryPos
+				return
+			}
 		}
-	}
-
-	for {
-
-		var part mimeHeader
-
-		part = *NewMimeHeader()
-		if p.ch >= 33 && p.ch <= 126 {
-			err = p.header(&part)
+		if part.contentType.superType == "message" ||
+			part.contentType.superType == "multipart" {
+			err = p.mime(part, depth)
 			if err != nil {
+
 				return err
 			}
-		}
-		if p.ch == '\n' && p.peek() == '\n' {
-			p.next()
-			p.next()
-		}
 
-		p.addPart(&part, depth+"."+strconv.Itoa(count))
-		if part.contentType != nil &&
-			(part.contentType.superType == "message" ||
-				part.contentType.superType == "multipart") {
-			return p.mime(boundary, depth+"."+strconv.Itoa(count))
-		} else {
-			if end, bErr := p.boundary(boundary); bErr != nil {
-				return bErr
-			} else if end {
-				part.endingPosBody = p.msgPos
-				break
-			}
 		}
 
-		count++
-
 	}
 	return
-
 }
 
-func (p *parser) mime2(boundary string, depth string) (err error) {
-	count := 1
-	h := NewMimeHeader()
-
-	if p.ch >= 33 && p.ch <= 126 {
-		err = p.header(h)
-		if err != nil {
-			//				temp := p.buf[p.pos:p.pos+20]
-			//				_ = temp
-			return err
-		}
-	} else {
-		fmt.Println("empty header")
-	}
-
-	if depth == "1" {
-		p.addPart(h, depth)
-	} else {
-		p.addPart(h, depth+"."+strconv.Itoa(count))
-		if h.contentType != nil &&
-			(h.contentType.superType == "message" ||
-				h.contentType.superType == "multipart") {
-			depth = depth + "." + strconv.Itoa(count)
-		}
-	}
-
-	if p.ch == '\n' && p.peek() == '\n' {
-		p.next()
-		p.next()
-	}
-	if h.contentBoundary != "" {
-		boundary = h.contentBoundary
-
-	}
-
-	if end, bErr := p.boundary(boundary); bErr != nil {
-		return bErr
-	} else if end {
-		h.endingPosBody = p.msgPos
-		return
-	}
+func (p *parser) mime(parent *mimeHeader, depth string) (err error) {
 
+	count := 1
 	for {
 
 		var part mimeHeader
 
 		part = *NewMimeHeader()
+		part.startingPos = p.msgPos
 		if p.ch >= 33 && p.ch <= 126 {
 			err = p.header(&part)
 			if err != nil {
 				return err
 			}
+		} else {
+			moo := p.buf[p.pos:]
+			_ = moo
+			//break
 		}
 		if p.ch == '\n' && p.peek() == '\n' {
 			p.next()
 			p.next()
 		}
-
-		p.addPart(&part, depth+"."+strconv.Itoa(count))
-		if part.contentType != nil &&
-			(part.contentType.superType == "message" ||
-				part.contentType.superType == "multipart") {
-			return p.mime(boundary, depth+"."+strconv.Itoa(count))
-		} else {
-
-			if end, bErr := p.boundary(boundary); bErr != nil {
-				return bErr
-			} else if end {
-				part.endingPosBody = p.msgPos
-				break
-			}
+		if part.contentBoundary == "" {
+			part.contentBoundary = parent.contentBoundary
 		}
-		count++
-
-	}
-	return
-
-}
-
-func (p *parser) mimeMsg(boundary string, depth string) (err error) {
-	count := 0
-	for {
-		count++
-		d := depth + "." + strconv.Itoa(count)
-		_ = d
-
-		{
-			h := NewMimeHeader()
-
-			if p.ch >= 33 && p.ch <= 126 {
-				err = p.header(h)
-				if err != nil {
-					//				temp := p.buf[p.pos:p.pos+20]
-					//				_ = temp
-					return err
-				}
-			} else {
-				fmt.Println("empty header")
-			}
-			if p.ch == '\n' && p.peek() == '\n' {
-				p.next()
-				p.next()
-			}
-
-			if h.contentBoundary != "" {
-				boundary = h.contentBoundary
-				if end, bErr := p.boundary(boundary); bErr != nil {
-					return bErr
-				} else if end {
-					return
-				}
-				err = p.mimeMsg(boundary, depth+"."+strconv.Itoa(count))
-				if err != nil {
-					return
-				}
-			} else {
-				// body-part end
-				for {
-					if end, bErr := p.boundary(boundary); bErr != nil {
-						return bErr
-					} else if end {
-						return
-					}
 
-				}
-
-			}
-
-			if p.ch == 0 {
-				return
-			}
+		part.startingPosBody = p.msgPos
+		partID := strconv.Itoa(count)
+		if depth != "" {
+			partID = depth + "." + strconv.Itoa(count)
+		}
+		p.addPart(&part, partID)
 
-			if h.contentType == nil {
-				fmt.Println("nope")
+		if p.isMulti(&part) {
+			err = p.multi(&part, partID)
+			part.endingPosBody = p.msgPos + 1 //p.lastBoundaryPos
+			if err != nil {
+				break
 			}
 
-			p.addPart(h, depth+"."+strconv.Itoa(count))
-			if h.contentType != nil &&
-				(h.contentType.superType == "message" ||
-					h.contentType.superType == "multipart") {
-
-				return p.mimeMsg(boundary, depth+"."+strconv.Itoa(count))
-			} else if end, bErr := p.boundary(boundary); bErr != nil {
+			return
+		} else {
+			if end, bErr := p.boundary(parent.contentBoundary); bErr != nil {
 				return bErr
 			} else if end {
+				part.endingPosBody = p.lastBoundaryPos
 				return
 			}
+			part.endingPosBody = p.lastBoundaryPos
 
 		}
-
+		count++
 	}
+	return
 }
 
 func (p *parser) close() error {

+ 95 - 43
backends/s_mime_test.go

@@ -3,6 +3,7 @@ package backends
 import (
 	"bytes"
 	"fmt"
+	"strconv"
 	"testing"
 )
 
@@ -118,16 +119,15 @@ func TestBoundary(t *testing.T) {
 	part.contentBoundary = "-wololo-"
 
 	// in the middle of the string
-	p.inject([]byte("The quick brown fo-wololo-x jumped over the lazy dog"))
+	p.inject([]byte("The quick brown fo---wololo-\nx jumped over the lazy dog"))
 
 	_, err = p.boundary(part.contentBoundary)
 	if err != nil {
 		t.Error(err)
 	}
 
-	//for c := p.next(); c != 0; c= p.next() {} // drain
-
-	p.inject([]byte("The quick brown fox jumped over the lazy dog-wololo-"))
+	// at the end (with the -- postfix)
+	p.inject([]byte("The quick brown fox jumped over the lazy dog---wololo---\n"))
 	_, err = p.boundary(part.contentBoundary)
 	if err != nil {
 		t.Error(err)
@@ -138,8 +138,8 @@ func TestBoundary(t *testing.T) {
 
 	// boundary is split over multiple slices
 	p.inject(
-		[]byte("The quick brown fox jumped ov-wolo"),
-		[]byte("lo-er the lazy dog"))
+		[]byte("The quick brown fox jumped ov---wolo"),
+		[]byte("lo---\ner the lazy dog"))
 	_, err = p.boundary(part.contentBoundary)
 	if err != nil {
 		t.Error(err)
@@ -149,7 +149,7 @@ func TestBoundary(t *testing.T) {
 	// the boundary with an additional buffer in between
 	p.inject([]byte("The quick brown fox jumped over the lazy dog"),
 		[]byte("this is the middle"),
-		[]byte("and thats the end-wololo-"))
+		[]byte("and thats the end---wololo---\n"))
 
 	_, err = p.boundary(part.contentBoundary)
 	if err != nil {
@@ -203,37 +203,6 @@ func TestMimeContentQuotedParams(t *testing.T) {
 
 }
 
-func msg() (err error) {
-	main := NewMimeHeader()
-	err = p.header(main)
-	if err != nil {
-		return err
-	}
-	p.addPart(main, "1")
-
-	if main.contentBoundary != "" {
-		// it's a message with mime parts
-
-		if end, bErr := p.boundary(main.contentBoundary); bErr != nil {
-			return bErr
-		} else if end {
-			return
-		}
-
-		if err = p.mimeMsg("", "1"); err != nil {
-			return err
-		}
-	} else {
-		// only contains one part (the body)
-		if err := p.body(main); err != nil {
-			return err
-		}
-	}
-	p.endBody(main)
-
-	return
-}
-
 var email = `From:  Al Gore <[email protected]>
 To:  White House Transportation Coordinator <[email protected]>
 Subject: [Fwd: Map of Argentina with Description]
@@ -254,7 +223,7 @@ Content-Transfer-Encoding: 7bit
 
 Fred,
 
-Fire up Air Force One!  We\'re going South!
+Fire up Air Force One!  We're going South!
 
 Thanks,
 Al
@@ -287,7 +256,7 @@ Content-Transfer-Encoding: 7bit
 
 Hi A1,
 
-I finally figured out this MIME thing.  Pretty cool.  I\'ll send you
+I finally figured out this MIME thing.  Pretty cool.  I'll send you
 some sax music in .au files next week!
 
 Anyway, the attached image is really too small to get a good look at
@@ -295,7 +264,7 @@ Argentina.  Try this for a much better map:
 
 http://www.1one1yp1anet.com/dest/sam/graphics/map-arg.htm
 
-Then again, shouldn\'t the CIA have something like that?
+Then again, shouldn't the CIA have something like that?
 
 Bill
 --DC8------------DC8638F443D87A7F0726DEF7
@@ -315,14 +284,97 @@ U6ZGxseyk8SasGw3J9GRzdTQky1iHNvcPNNI4TLeKdfMvy0vMqLrItvuxfDW8ubjueDtJufz
 
 `
 
+var email2 = `From: [email protected]
+Content-Type: multipart/mixed;
+        boundary="----_=_NextPart_001_01CBE273.65A0E7AA"
+To: [email protected]
+
+This is a multi-part message in MIME format.
+
+------_=_NextPart_001_01CBE273.65A0E7AA
+Content-Type: multipart/alternative;
+        boundary="----_=_NextPart_002_01CBE273.65A0E7AA"
+
+
+------_=_NextPart_002_01CBE273.65A0E7AA
+Content-Type: text/plain;
+        charset="UTF-8"
+Content-Transfer-Encoding: base64
+
+[base64-content]
+------_=_NextPart_002_01CBE273.65A0E7AA
+Content-Type: text/html;
+        charset="UTF-8"
+Content-Transfer-Encoding: base64
+
+[base64-content]
+------_=_NextPart_002_01CBE273.65A0E7AA--
+------_=_NextPart_001_01CBE273.65A0E7AA
+Content-Type: message/rfc822
+Content-Transfer-Encoding: 7bit
+
+X-MimeOLE: Produced By Microsoft Exchange V6.5
+Content-class: urn:content-classes:message
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+        boundary="----_=_NextPart_003_01CBE272.13692C80"
+From: [email protected]
+To: [email protected]
+
+This is a multi-part message in MIME format.
+
+------_=_NextPart_003_01CBE272.13692C80
+Content-Type: multipart/alternative;
+        boundary="----_=_NextPart_004_01CBE272.13692C80"
+
+
+------_=_NextPart_004_01CBE272.13692C80
+Content-Type: text/plain;
+        charset="iso-8859-1"
+Content-Transfer-Encoding: quoted-printable
+
+=20
+
+Viele Gr=FC=DFe
+
+------_=_NextPart_004_01CBE272.13692C80
+Content-Type: text/html;
+        charset="iso-8859-1"
+Content-Transfer-Encoding: quoted-printable
+
+<html>...</html>
+------_=_NextPart_004_01CBE272.13692C80--
+------_=_NextPart_003_01CBE272.13692C80
+Content-Type: application/x-zip-compressed;
+        name="abc.zip"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment;
+        filename="abc.zip"
+
+[base64-content]
+
+------_=_NextPart_003_01CBE272.13692C80--
+------_=_NextPart_001_01CBE273.65A0E7AA--`
+
 func TestNestedEmail(t *testing.T) {
+	//email = email2
 	p.inject([]byte(email))
 
-	if err := p.mime("", "1"); err != nil {
+	if err := p.mime(nil, ""); err != nil {
 		t.Error(err)
 	}
 	for part := range p.parts {
-		fmt.Println(p.parts[part].part, " ", p.parts[part].contentType)
+		email = replaceAtIndex(email, '#', p.parts[part].startingPos)
+		email = replaceAtIndex(email, '&', p.parts[part].startingPosBody)
+		email = replaceAtIndex(email, '*', p.parts[part].endingPosBody)
+		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)))
 	}
+	fmt.Print(email)
+	//fmt.Println(strings.Index(email, "--D7F------------D7FD5A0B8AB9C65CCDBFA872--"))
+
+	//	fmt.Println(email[p.parts[5].startingPosBody:p.parts[5].endingPosBody])
+}
 
+func replaceAtIndex(str string, replacement rune, index uint) string {
+	return str[:index] + string(replacement) + str[index+1:]
 }