Skip to content

Commit e977e94

Browse files
committed
初始化项目
0 parents  commit e977e94

File tree

12 files changed

+1104
-0
lines changed

12 files changed

+1104
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
main
3+
parser
4+
sqlite3

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
nodejs 还是 golang 呢?
2+
3+
如果使用 golang,可以顺便熟悉这种语言,而且 golang 内置了 smtp 的库,不需要依赖第三方的东东了
4+
5+
发送邮件
6+
http://golang.org/pkg/net/smtp/
7+
8+
解析邮件的内容(支持附件么?)
9+
http://golang.org/pkg/net/mail/
10+
11+
数据结构的设计:
12+
13+
Thread
14+
Message 1
15+
Message 2
16+
...
17+
Message N
18+
19+
Message
20+
List<Header>
21+
Body
22+
List<Attachment>
23+
24+
Header
25+
Key
26+
Value
27+
28+
Raw
29+
30+
Message-Id
31+
32+
Thread-Topic
33+
Thread-Index
34+
35+
36+
一些规范的阅读
37+
38+
rfc 2045
39+
rfc 2047
40+
http://en.wikipedia.org/wiki/Post_Office_Protocol

RFC2047/LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2013 Fam Zheng <[email protected]>
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is
8+
furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in
11+
all copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

RFC2047/README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
RFC2047 for Go
2+
==============
3+
An RFC2047 Decoder package for Golang
4+
5+
Usage
6+
-----
7+
8+
Just import "github.com/famz/RFC2047", then call "RFC2047.Decode" with your
9+
encoded string and get it decoded! See below for an example.
10+
11+
Install
12+
-------
13+
14+
Simple as `go get github.com/famz/RFC2047`
15+
16+
Example
17+
-------
18+
19+
```go
20+
package main
21+
import (
22+
"github.com/famz/RFC2047"
23+
"fmt"
24+
)
25+
26+
func main() {
27+
fmt.Println(RFC2047.Decode("=?UTF-8?B?5YmN5Y+w5pyJ5L2g5L+h?="))
28+
}
29+
```

RFC2047/RFC2047.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package RFC2047
2+
3+
import (
4+
"io"
5+
"io/ioutil"
6+
"strings"
7+
"encoding/base64"
8+
"errors"
9+
"fmt"
10+
"bytes"
11+
"strconv"
12+
13+
"github.com/qiniu/iconv"
14+
)
15+
16+
type qDecoder struct {
17+
r io.Reader
18+
scratch [2]byte
19+
}
20+
21+
func (qd qDecoder) Read(p []byte) (n int, err error) {
22+
// This method writes at most one byte into p.
23+
if len(p) == 0 {
24+
return 0, nil
25+
}
26+
if _, err := qd.r.Read(qd.scratch[:1]); err != nil {
27+
return 0, err
28+
}
29+
switch c := qd.scratch[0]; {
30+
case c == '=':
31+
if _, err := io.ReadFull(qd.r, qd.scratch[:2]); err != nil {
32+
return 0, err
33+
}
34+
x, err := strconv.ParseInt(string(qd.scratch[:2]), 16, 64)
35+
if err != nil {
36+
return 0, fmt.Errorf("mail: invalid RFC 2047 encoding: %q", qd.scratch[:2])
37+
}
38+
p[0] = byte(x)
39+
case c == '_':
40+
p[0] = ' '
41+
default:
42+
p[0] = c
43+
}
44+
return 1, nil
45+
}
46+
47+
func decodeRFC2047Word(s string) (string, error) {
48+
fields := strings.Split(s, "?")
49+
if len(fields) != 5 || fields[0] != "=" || fields[4] != "=" {
50+
return "", errors.New("string not RFC 2047 encoded")
51+
}
52+
charset, enc := strings.ToLower(fields[1]), strings.ToLower(fields[2])
53+
if charset != "iso-8859-1" &&
54+
charset != "utf-8" &&
55+
charset != "gb2312" &&
56+
charset != "gbk" {
57+
return "", fmt.Errorf("charset not supported: %q", charset)
58+
}
59+
60+
in := bytes.NewBufferString(fields[3])
61+
var r io.Reader
62+
switch enc {
63+
case "b":
64+
r = base64.NewDecoder(base64.StdEncoding, in)
65+
case "q":
66+
r = qDecoder{r: in}
67+
default:
68+
return "", fmt.Errorf("RFC 2047 encoding not supported: %q", enc)
69+
}
70+
71+
dec, err := ioutil.ReadAll(r)
72+
if err != nil {
73+
return "", err
74+
}
75+
76+
switch charset {
77+
case "iso-8859-1":
78+
b := new(bytes.Buffer)
79+
for _, c := range dec {
80+
b.WriteRune(rune(c))
81+
}
82+
return b.String(), nil
83+
case "gbk":
84+
cd, err := iconv.Open("utf-8", "gbk")
85+
if err != nil {
86+
return "", err
87+
}
88+
defer cd.Close()
89+
return cd.ConvString(string(dec)), nil
90+
case "gb2312":
91+
cd, err := iconv.Open("utf-8", "gb2312")
92+
if err != nil {
93+
return "", err
94+
}
95+
defer cd.Close()
96+
return cd.ConvString(string(dec)), nil
97+
case "utf-8":
98+
return string(dec), nil
99+
}
100+
panic("unreachable")
101+
}
102+
103+
func Decode(s string) (ret string) {
104+
sep := ""
105+
for _, p := range strings.Split(s, " ") {
106+
r, err := decodeRFC2047Word(p)
107+
if err != nil {
108+
ret += sep + p
109+
} else {
110+
ret += sep + r
111+
}
112+
sep = " "
113+
}
114+
return
115+
}

base/parser.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package base
2+
3+
import (
4+
"strings"
5+
"regexp"
6+
7+
"../RFC2047"
8+
)
9+
10+
// 工具类
11+
// Content-Disposition: attachment;
12+
// filename="=?gb2312?B?ob7Wqsq2zca546G/OC4yMcjrv9q/qs2ow/u1pS54bHN4?=";
13+
// size=14296; creation-date="Thu, 21 Aug 2014 11:17:27 GMT";
14+
// modification-date="Thu, 21 Aug 2014 11:19:56 GMT"
15+
16+
func ParseContentDisposition(cd string) (map[string]string) {
17+
var r = regexp.MustCompile(`^"|"$`)
18+
19+
rv := make(map[string]string)
20+
for _, item := range strings.Split(cd, "; ") {
21+
var key string
22+
var value string
23+
24+
chunks := strings.SplitN(item, "=", 2)
25+
key = chunks[0]
26+
27+
if len(chunks) == 1 {
28+
value = key
29+
} else if len(chunks) == 2 {
30+
value = chunks[1]
31+
}
32+
33+
if strings.HasPrefix(value, "\"") {
34+
value = string(r.ReplaceAll([]byte(value), []byte("")))
35+
}
36+
37+
if key == "filename" {
38+
value = RFC2047.Decode(value)
39+
}
40+
41+
rv[key] = value
42+
}
43+
44+
return rv
45+
}

base/parser_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package base_test
2+
3+
import (
4+
"testing"
5+
6+
"../base"
7+
)
8+
9+
func TestParseContentDisposition(t *testing.T) {
10+
rv := base.ParseContentDisposition("hello")
11+
if rv["hello"] != "hello" {
12+
t.Errorf("base.ParseContentDisposition(%s): expected %s, actual %s",
13+
"hello", "hello", rv["hello"])
14+
}
15+
16+
17+
expected := "【知识推广】8.21入口开通名单.xlsx"
18+
input := "attachment; filename=\"=?gb2312?B?ob7Wqsq2zca546G/OC4yMcjrv9q/qs2ow/u1pS54bHN4?=\"; size=14296; creation-date=\"Thu, 21 Aug 2014 11:17:27 GMT\"; modification-date=\"Thu, 21 Aug 2014 11:19:56 GMT\""
19+
20+
rv = base.ParseContentDisposition(input)
21+
22+
if rv["filename"] != expected {
23+
t.Errorf("base.ParseContentDisposition(%s): expected %s, actual %s",
24+
input, expected, rv["filename"])
25+
}
26+
}

main.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package main
2+
3+
import (
4+
"log"
5+
"net/mail"
6+
"bytes"
7+
"io/ioutil"
8+
pop3 "github.com/bytbox/go-pop3"
9+
)
10+
11+
func main() {
12+
client, err := pop3.DialTLS("email.baidu.com:995")
13+
if err != nil { panic(err) }
14+
15+
err = client.Auth("liyubei", "zhenxixiaohui@^@262")
16+
if err != nil { panic(err) }
17+
18+
count, size, err := client.Stat()
19+
if err != nil { panic(err) }
20+
21+
log.Printf("Count = %d, Size = %d\n", count, size)
22+
23+
size, err = client.List(10)
24+
if err != nil { panic(err) }
25+
26+
log.Println(size)
27+
28+
raw, err := client.Retr(count)
29+
if err != nil { panic(err) }
30+
31+
ioutil.WriteFile("raw.txt", []byte(raw), 0644)
32+
33+
log.Println(raw)
34+
msg, err := mail.ReadMessage(bytes.NewBuffer([]byte(raw)))
35+
if err != nil { panic(err) }
36+
37+
log.Println(msg.Header)
38+
log.Println(msg.Header.Get("From"))
39+
log.Println(msg.Header.Get("Subject"))
40+
41+
address, err := mail.ParseAddress(msg.Header.Get("From"))
42+
// if err != nil { panic(err) }
43+
44+
log.Println(address.Name)
45+
log.Println(address.Address)
46+
47+
body, err := ioutil.ReadAll(msg.Body)
48+
if err != nil { panic(err) }
49+
50+
log.Println(string(body))
51+
}
52+

0 commit comments

Comments
 (0)