f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
0
|
package main |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
1
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
2
|
import ( |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
3
|
"fmt" |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
4
|
"log" |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
5
|
"os" |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
6
|
"path/filepath" |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
7
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
8
|
"github.com/alexflint/go-arg" |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
9
|
"github.com/emersion/go-imap" |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
10
|
"github.com/emersion/go-imap/client" |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
11
|
) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
12
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
13
|
const Version = "0.0.1" |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
14
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
15
|
func main() { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
16
|
var args struct { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
17
|
Address string `arg:"positional,required"` |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
18
|
Username string `arg:"positional,required"` |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
19
|
Password string `arg:"positional,required"` |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
20
|
Output string `arg:"-o" help:"Output directory" default:"mail"` |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
21
|
} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
22
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
23
|
if err := arg.Parse(&args); err != nil { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
24
|
switch err { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
25
|
case arg.ErrHelp: |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
26
|
fmt.Print(help) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
27
|
os.Exit(0) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
28
|
case arg.ErrVersion: |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
29
|
fmt.Print(version) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
30
|
os.Exit(0) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
31
|
} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
32
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
33
|
fmt.Println(err.Error()) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
34
|
os.Exit(-1) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
35
|
} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
36
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
37
|
c, err := client.DialTLS(args.Address, nil) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
38
|
if err != nil { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
39
|
log.Fatalln(err.Error()) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
40
|
} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
41
|
defer c.Logout() |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
42
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
43
|
if err := c.Login(args.Username, args.Password); err != nil { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
44
|
log.Fatalln(err.Error()) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
45
|
} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
46
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
47
|
mailboxes, err := Mailboxes(c) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
48
|
if err != nil { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
49
|
log.Fatalln(err.Error()) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
50
|
} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
51
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
52
|
for _, m := range mailboxes { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
53
|
if m.Name == "Trash" { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
54
|
log.Println("Skipping Trash") |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
55
|
continue |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
56
|
} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
57
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
58
|
log.Println(m.Name) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
59
|
if err := FetchMailbox(c, m, args.Output); err != nil { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
60
|
log.Fatalln(err.Error()) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
61
|
} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
62
|
} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
63
|
} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
64
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
65
|
/* Get list of Mailboxes. */ |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
66
|
func Mailboxes(c *client.Client) ([]*imap.MailboxInfo, error) { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
67
|
ch := make(chan *imap.MailboxInfo, 6) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
68
|
done := make(chan error, 1) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
69
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
70
|
go func() { done <- c.List("", "*", ch) }() |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
71
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
72
|
var mailboxes []*imap.MailboxInfo |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
73
|
for m := range ch { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
74
|
mailboxes = append(mailboxes, m) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
75
|
} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
76
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
77
|
if err := <-done; err != nil { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
78
|
return nil, err |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
79
|
} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
80
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
81
|
return mailboxes, nil |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
82
|
} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
83
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
84
|
func FetchMailbox(c *client.Client, mailbox *imap.MailboxInfo, dir string) error { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
85
|
mbox, err := c.Select(mailbox.Name, true) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
86
|
if err != nil { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
87
|
return err |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
88
|
} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
89
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
90
|
if err := os.MkdirAll(filepath.Join(dir, mailbox.Name), 0o777); err != nil { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
91
|
return err |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
92
|
} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
93
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
94
|
if mbox.Messages == 0 { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
95
|
return nil |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
96
|
} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
97
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
98
|
seqset := new(imap.SeqSet) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
99
|
seqset.AddRange(1, mbox.Messages) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
100
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
101
|
section := &imap.BodySectionName{Peek: false} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
102
|
items := []imap.FetchItem{imap.FetchAll, section.FetchItem()} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
103
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
104
|
messages := make(chan *imap.Message, 10) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
105
|
done := make(chan error, 1) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
106
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
107
|
go func() { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
108
|
done <- c.Fetch(seqset, items, messages) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
109
|
}() |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
110
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
111
|
for msg := range messages { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
112
|
body := msg.GetBody(section) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
113
|
if body == nil { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
114
|
return fmt.Errorf("message body is nil") |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
115
|
} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
116
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
117
|
data := make([]byte, body.Len()) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
118
|
if _, err := body.Read(data); err != nil { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
119
|
return err |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
120
|
} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
121
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
122
|
file, err := os.Create( |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
123
|
filepath.Join(dir, mailbox.Name, fmt.Sprintf( |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
124
|
"%s_%d.eml", msg.InternalDate.UTC().Format("20060102T150405Z"), msg.SeqNum, |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
125
|
)), |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
126
|
) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
127
|
if err != nil { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
128
|
return err |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
129
|
} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
130
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
131
|
if _, err := file.Write(data); err != nil { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
132
|
file.Close() |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
133
|
return err |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
134
|
} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
135
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
136
|
log.Println(mailbox.Name, "->", msg.Envelope.Subject) |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
137
|
file.Close() |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
138
|
} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
139
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
140
|
if err := <-done; err != nil { |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
141
|
return err |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
142
|
} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
143
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
144
|
return nil |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
145
|
} |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
146
|
|
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
147
|
const help = `` |
f28abe4 |
Jakob Wakeling |
2024-01-28 21:13:23 |
148
|
const version = "backup-imap, version " + Version + "\n" |
|
|
|
149
|
|