func (msg Jmessage) DecSubject() string { header := msg.Header splitsubj := strings.Fields(header.Get("Subject")) var bufSubj bytes.Buffer for seq, parts := range splitsubj { switch { case !strings.HasPrefix(parts, "=?"): // エンコードなし if seq > 0 { // 先頭以外はSpaceで区切りなおし bufSubj.WriteByte(' ') } bufSubj.WriteString(parts) case len(parts) > len(SUBJ_PREFIX_ISO2022JP_B) && strings.HasPrefix(strings.ToLower(parts[0:len(SUBJ_PREFIX_ISO2022JP_B)]), SUBJ_PREFIX_ISO2022JP_B): // iso-2022-jp / base64 beforeDecode := parts[len(SUBJ_PREFIX_ISO2022JP_B):strings.LastIndex(parts, "?=")] afterDecode := base64.NewDecoder(base64.StdEncoding, bytes.NewBufferString(beforeDecode)) subj_bytes, _ := ioutil.ReadAll(transform.NewReader(afterDecode, japanese.ISO2022JP.NewDecoder())) bufSubj.Write(subj_bytes) case len(parts) > len(SUBJ_PREFIX_ISO2022JP_Q) && strings.HasPrefix(strings.ToLower(parts[0:len(SUBJ_PREFIX_ISO2022JP_Q)]), SUBJ_PREFIX_ISO2022JP_Q): // iso-2022-jp / quoted-printable beforeDecode := parts[len(SUBJ_PREFIX_ISO2022JP_Q):strings.LastIndex(parts, "?=")] afterDecode := quotedprintable.NewReader(bytes.NewBufferString(beforeDecode)) subj_bytes, _ := ioutil.ReadAll(transform.NewReader(afterDecode, japanese.ISO2022JP.NewDecoder())) bufSubj.Write(subj_bytes) case len(parts) > len(SUBJ_PREFIX_UTF8_B) && strings.HasPrefix(strings.ToLower(parts[0:len(SUBJ_PREFIX_UTF8_B)]), SUBJ_PREFIX_UTF8_B): // utf-8 / base64 beforeDecode := parts[len(SUBJ_PREFIX_UTF8_B):strings.LastIndex(parts, "?=")] subj_bytes, _ := base64.StdEncoding.DecodeString(beforeDecode) bufSubj.Write(subj_bytes) case len(parts) > len(SUBJ_PREFIX_UTF8_Q) && strings.HasPrefix(strings.ToLower(parts[0:len(SUBJ_PREFIX_UTF8_Q)]), SUBJ_PREFIX_UTF8_Q): // utf-8 / quoted-printable beforeDecode := parts[len(SUBJ_PREFIX_UTF8_Q):strings.LastIndex(parts, "?=")] afterDecode := quotedprintable.NewReader(bytes.NewBufferString(beforeDecode)) subj_bytes, _ := ioutil.ReadAll(afterDecode) bufSubj.Write(subj_bytes) } } return bufSubj.String() }
func ReadPlainMail(msg *mail.Message) (email JsonMail, err error) { const cte = "Content-Transfer-Encoding" if msg.Header.Get(cte) == "quoted-printable" { msg.Body = quotedprintable.NewReader(msg.Body) } body, err := ioutil.ReadAll(msg.Body) if err != nil { return email, errors.New("ERROR: reading non multipart mail body") } if msg.Header.Get(cte) == "base64" { msg.Body = quotedprintable.NewReader(msg.Body) body, err = base64.StdEncoding.DecodeString(string(body)) if err != nil { return email, errors.New("ERROR: decoding base64") } } b := `<pre>` + string(body) + `</pre>` subject := msg.Header.Get("Subject") decoder := WordDecoder() decodedSubject, err := decoder.Decode(subject) if err == nil { subject = decodedSubject } decodedFrom, err := decoder.Decode(msg.Header.Get("From")) if err != nil { parts := strings.Split(email.From, " ") for i, part := range parts { decodedPart, err := decoder.Decode(part) if err == nil { parts[i] = decodedPart } } decodedFrom = strings.Join(parts, " ") } email.From = decodedFrom email.Subject = subject email.BodyText = b email.Body = b return email, nil }
// contentReader ... func contentReader(headers Header, bodyReader io.Reader) *bufio.Reader { if headers.Get("Content-Transfer-Encoding") == "quoted-printable" { headers.Del("Content-Transfer-Encoding") return bufioReader(quotedprintable.NewReader(bodyReader)) } if headers.Get("Content-Transfer-Encoding") == "base64" { headers.Del("Content-Transfer-Encoding") return bufioReader(base64.NewDecoder(base64.StdEncoding, bodyReader)) } return bufioReader(bodyReader) }
// Read body from text/plain func readPlainText(header textproto.MIMEHeader, body io.Reader) (mailbody []byte, err error) { contentType := header.Get("Content-Type") encoding := header.Get("Content-Transfer-Encoding") _, params, err := mime.ParseMediaType(contentType) if encoding == ENC_QUOTED_PRINTABLE { if strings.ToLower(params["charset"]) == CHARSET_ISO2022JP { mailbody, err = ioutil.ReadAll(transform.NewReader(quotedprintable.NewReader(body), japanese.ISO2022JP.NewDecoder())) } else { mailbody, err = ioutil.ReadAll(quotedprintable.NewReader(body)) } } else if encoding == ENC_BASE64 { mailbody, err = ioutil.ReadAll(base64.NewDecoder(base64.StdEncoding, body)) } else if len(contentType) == 0 || strings.ToLower(params["charset"]) == CHARSET_ISO2022JP { // charset=ISO-2022-JP mailbody, err = ioutil.ReadAll(transform.NewReader(body, japanese.ISO2022JP.NewDecoder())) } else { // encoding = 8bit or 7bit mailbody, err = ioutil.ReadAll(body) } return mailbody, err }
// DecodeTransfer decodes base64, quoted-printable or plain text. func decodeTransfer(r io.Reader, label string) io.Reader { switch strings.ToLower(label) { case "base64": return base64.NewDecoder(base64.StdEncoding, transform.NewReader(r, nonASCIITransformer{})) case "quoted-printable": return quotedprintable.NewReader(transform.NewReader(r, transform.Chain(nonASCIITransformer{}, newlineAppendTransformer{}))) case "", "7bit", "8bit", "binary": return r default: return failReader{fmt.Errorf("unsupported transfer encoding: %v", label)} } }
func Decode(e string, bstr []byte) ([]byte, error) { var err error switch strings.ToUpper(e) { case "Q": bstr, err = ioutil.ReadAll(quotedprintable.NewReader(bytes.NewReader(bstr))) case "B": bstr, err = base64.StdEncoding.DecodeString(string(bstr)) default: //not set encoding type } return bstr, err }
func ExampleNewReader() { for _, s := range []string{ `=48=65=6C=6C=6F=2C=20=47=6F=70=68=65=72=73=21`, `invalid escape: =B`, "Hello, Gophers! This symbol will be unescaped: =3D and this will be written in =\r\none line.", } { b, err := ioutil.ReadAll(quotedprintable.NewReader(strings.NewReader(s))) fmt.Printf("%s %v\n", b, err) } // Output: // Hello, Gophers! <nil> // invalid escape: unexpected EOF // Hello, Gophers! This symbol will be unescaped: = and this will be written in one line. <nil> }
func newPart(mr *Reader) (*Part, error) { bp := &Part{ Header: make(map[string][]string), mr: mr, } if err := bp.populateHeaders(); err != nil { return nil, err } bp.r = partReader{bp} const cte = "Content-Transfer-Encoding" if bp.Header.Get(cte) == "quoted-printable" { bp.Header.Del(cte) bp.r = quotedprintable.NewReader(bp.r) } return bp, nil }
// Decode body text and store it in a string func extractText(con *data.Context, header emailHeader, bodyReader io.Reader) (*TextBody, error) { var ret TextBody s, err := ioutil.ReadAll(bodyReader) if err != nil { return nil, err } con.Log.Debugf("header: %v", header) ret.ContentType = header.Get("Content-Type") encoding := header.Get("Content-Transfer-Encoding") con.Log.Debugf("extractTextBody encoding: %v", encoding) if encoding == "base64" { b, err := base64.StdEncoding.DecodeString(string(s)) if err != nil { return nil, err } ret.Body = string(b) return &ret, nil } if encoding == "7bit" { // that is just US ASCII (7bit) // https://stackoverflow.com/questions/25710599/content-transfer-encoding-7bit-or-8-bit ret.Body = string(s) return &ret, nil } if encoding == "quoted-printable" { // https://stackoverflow.com/questions/24883742/how-to-decode-mail-body-in-go r := quotedprintable.NewReader(bytes.NewReader(s)) b, err := ioutil.ReadAll(r) if err != nil { return nil, err } ret.Body = string(b) return &ret, nil } // ok, let's guess this is just plain text and put it into a string ret.Body = string(s) return &ret, nil }
func main() { flag.Parse() var in io.Reader var out io.Writer if input == nil || strings.TrimSpace(*input) == "" { in = bufio.NewReader(os.Stdin) } else { fileBytes, err := ioutil.ReadFile(*input) if err != nil { log.Fatal(err) } in = bytes.NewReader(fileBytes) } if decode != nil && *decode == true { in = qp.NewReader(in) } inBytes, err := ioutil.ReadAll(in) if err != nil { log.Fatal(err) } if output == nil || strings.TrimSpace(*output) == "" { out = os.Stdout } else { out, err = os.OpenFile(*output, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { log.Fatal(err) } } if decode == nil || *decode == false { out = qp.NewWriter(out) } n, err := out.Write(inBytes) if err != nil { log.Fatal(err) } if n != len(inBytes) { log.Fatalf("Partial write (%d != %d)\n", n, len(inBytes)) } }
func addContent(email *Email, header Header, r io.Reader) error { switch header.Get("Content-Transfer-Encoding") { case "quoted-printable": r = quotedprintable.NewReader(r) case "base64": r = base64.NewDecoder(base64.StdEncoding, r) } data, err := ioutil.ReadAll(r) if err != nil { return err } mediaType, params, err := mime.ParseMediaType(header.Get("Content-Type")) if err != nil { return err } if isPlainText(mediaType) && email.Text == "" { email.Text = string(data) email.Text = strings.Replace(email.Text, "\n", "<br>\n", -1) } else if isHtml(mediaType) && email.Html == nil { html := string(data) email.Html = &Attachment{Data: []byte(html), Filename: "email-content.html"} } else if isMultipart(mediaType) { if err := handleMultipart(email, bytes.NewReader(data), params["boundary"]); err != nil { return err } } else { // figure out filename filename := getFilename(header.Get("Content-Disposition")) if filename == "" { if isPlainText(mediaType) { filename = "attachment.txt" } else if isHtml(mediaType) { filename = "attachment.html" } else { return errors.New("don't know how to generate filename for " + mediaType) } } email.Attachments = append(email.Attachments, Attachment{Data: data, Filename: filename}) } return nil }
// decodeSection attempts to decode the data from reader using the algorithm listed in // the Content-Transfer-Encoding header, returning the raw data if it does not known // the encoding type. func decodeSection(encoding string, reader io.Reader) ([]byte, error) { // Default is to just read input into bytes decoder := reader switch strings.ToLower(encoding) { case "quoted-printable": decoder = quotedprintable.NewReader(reader) case "base64": cleaner := NewBase64Cleaner(reader) decoder = base64.NewDecoder(base64.StdEncoding, cleaner) } // Read bytes into buffer buf := new(bytes.Buffer) _, err := buf.ReadFrom(decoder) if err != nil { return nil, err } return buf.Bytes(), nil }
func decodeContent(content Content) { for contentType, curPart := range content { switch curPart.Encoding { case "quoted-printable": { var buf bytes.Buffer _, err := io.Copy(&buf, quotedprintable.NewReader(strings.NewReader(curPart.Body))) if err != nil { fmt.Printf("[watney] ERROR while trying to decode content of type " + "'quoted-printable'\n") continue } content[contentType] = ContentPart{ Encoding: curPart.Encoding, Charset: curPart.Charset, Body: buf.String(), } } } } }
// *Since the mime library in use by ```email``` is now in the stdlib, this test is deprecated func Test_quotedPrintDecode(t *testing.T) { text := []byte("Dear reader!\r\n\r\n" + "This is a test email to try and capture some of the corner cases that exist=\r\n" + " within\r\n" + "the quoted-printable encoding.\r\n" + "There are some wacky parts like =3D, and this input assumes UNIX line break=\r\n" + "s so\r\n" + "it can come out a little weird. Also, we need to support unicode so here's=\r\n" + " a fish: =F0=9F=90=9F\r\n") expected := []byte("Dear reader!\r\n\r\n" + "This is a test email to try and capture some of the corner cases that exist within\r\n" + "the quoted-printable encoding.\r\n" + "There are some wacky parts like =, and this input assumes UNIX line breaks so\r\n" + "it can come out a little weird. Also, we need to support unicode so here's a fish: 🐟\r\n") qp := quotedprintable.NewReader(bytes.NewReader(text)) got, err := ioutil.ReadAll(qp) if err != nil { t.Fatal("quotePrintDecode: ", err) } if !bytes.Equal(got, expected) { t.Errorf("quotedPrintDecode generated incorrect results: %#q != %#q", got, expected) } }
func getDecodingContent(lang, encoding, content string) string { if encoding == "b" { decoded, err := base64.StdEncoding.DecodeString(content) if err != nil { log.Printf("decode error:%s", err) return "" } if lang == "utf-8" { //log.Printf("utf-8, noneed to convert: %s\n", string(decoded)) return string(decoded) } //非u8编码语言需要统一转为u8 dec := mahonia.NewDecoder(lang) if u8Decoded, ok := dec.ConvertStringOK(string(decoded)); ok { //log.Printf("%s convert to utf-8: %s\n", lang, string(decoded)) return string(u8Decoded) //注意作用域 } } else if encoding == "q" { //通过字符串构造Reader r := quotedprintable.NewReader(strings.NewReader(content)) body, err := ioutil.ReadAll(r) if err != nil { log.Printf("read content error: %s", err) return "" } log.Printf("quoted decode: %s\n", string(body)) return string(body) } //转换失败,返回空 return "" }
func partWalker(r io.Reader, path []int, header map[string][]string, parse ParseFn, pc *ParserContext, depth int) error { depth-- if depth < 0 { return nil } cds := getHeader(header, "content-disposition") contentDisposition_str, cdParams, err := mime.ParseMediaType(cds) var contentDisposition ContentDisposition if err != nil { contentDisposition = CDInline } else { contentDisposition = ContentDispositionFromStr(contentDisposition_str) } contentType := getHeader(header, "content-type") mediaType, params, err := mime.ParseMediaType(contentType) if err != nil { mediaType = "text/plain" } isMultipart := true boundary := "" mediaType = strings.ToLower(mediaType) if !strings.HasPrefix(mediaType, "multipart/") { isMultipart = false } else { var ok bool boundary, ok = params["boundary"] if !ok { isMultipart = false } } partIndex := 0 if isMultipart { err = parse(pc, path, nil, PartDescr{mediaType, params, contentDisposition, cdParams}) if err != nil { return err } mr := multipart.NewReader(r, boundary) for { p, err := mr.NextPart() if err == io.EOF { break } if err != nil { return err } err = partWalker(p, append(path, partIndex), p.Header, parse, pc, depth) if err != nil { return err } partIndex++ } return nil } qp := false cte := strings.ToLower(getHeader(header, "Content-Transfer-Encoding")) buf, err := ioutil.ReadAll(r) if err != nil { return err } br := bytes.NewReader(buf) var reader io.Reader switch cte { case "base64": reader = base64.NewDecoder(base64.StdEncoding, br) case "quoted-printable": qp = true reader = quotedprintable.NewReader(br) default: reader = br } retry: decodedBuf, err := ioutil.ReadAll(reader) if err != nil { if qp { /* qp tends to fail often, retry in non-qp */ qp = false br.Seek(0, 0) reader = br goto retry } return err } return parse(pc, path, bytes.NewBuffer(decodedBuf), PartDescr{mediaType, params, contentDisposition, cdParams}) }