-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
156 lines (132 loc) · 3.13 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package main
import (
"crypto/md5"
"fmt"
"os"
"path/filepath"
"sync"
"time"
"github.com/jhillyerd/enmime"
)
const version string = "0.2.5"
type Attachment struct {
Name string
Data []byte
}
func main() {
if len(os.Args) == 1 {
fmt.Println("Error: missing arguments, Use: -h")
os.Exit(1)
}
for _, arg := range os.Args[1:] {
if arg == "-h" || arg == "--help" {
ShowHelp()
os.Exit(0)
}
if arg == "--version" {
ShowVersion()
os.Exit(0)
}
}
// resolve all input files
var emails []string
for _, input := range os.Args[1:] {
files, err := filepath.Glob(input)
if err != nil || len(files) == 0 {
fmt.Fprintf(os.Stderr, "Error: failed to resolve %s\n", input)
continue
}
for _, file := range files {
stat, _ := os.Stat(file)
if stat.IsDir() {
continue
}
emails = append(emails, file)
}
}
if len(emails) == 0 {
fmt.Fprintln(os.Stderr, "Error: no valid files were passed")
os.Exit(1)
}
// Timestamp to make output dir unique
ts := time.Now().Format("20060102150405")
dir := fmt.Sprintf("%s_emlex", ts)
err := os.Mkdir(dir, os.ModePerm)
if err != nil {
fmt.Fprintln(os.Stderr, "Error: failed to create output directory")
os.Exit(1)
}
wg := new(sync.WaitGroup)
sem := make(chan bool, 8)
for _, email := range emails {
sem <- true
wg.Add(1)
go func() {
defer func() { wg.Done(); <-sem }()
attachments, err := ExtractAttachments(email)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
hash := md5.Sum([]byte(email))
attachDir := filepath.Join(dir, fmt.Sprintf("%s.%x", filepath.Base(email), hash[0:4]))
err = SaveAttachments(attachDir, attachments)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
}()
}
wg.Wait()
}
func ExtractAttachments(path string) (*[]Attachment, error) {
file, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("failed to open %s", path)
}
defer file.Close()
msg, err := enmime.ReadEnvelope(file)
if err != nil {
return nil, fmt.Errorf("failed to parse %s", path)
}
if len(msg.Attachments) == 0 {
return nil, fmt.Errorf("%s does not contain attachments", path)
}
var attachments []Attachment
for _, attachment := range msg.Attachments {
attachments = append(attachments, Attachment{
Name: attachment.FileName,
Data: attachment.Content,
})
}
return &attachments, nil
}
func SaveAttachments(dir string, attachments *[]Attachment) error {
err := os.Mkdir(dir, os.ModePerm)
if err != nil {
return fmt.Errorf("failed to create %s", dir)
}
for _, attachment := range *attachments {
err := os.WriteFile(filepath.Join(dir, attachment.Name), attachment.Data, 0644)
if err != nil {
return fmt.Errorf("failed to save %s in %s", attachment.Name, dir)
}
}
return nil
}
func ShowVersion() {
fmt.Printf("emlex %s\n", version)
}
func ShowHelp() {
fmt.Println(`emlex [flags] email [email...]
Extract attachments from multiple .eml files
Examples:
emlex msg1.eml msg2.eml msg3.eml
emlex *.eml
emlex ./**/*.eml
Required:
email Path to .eml file(s)
Optional:
--version Show program version
-h, --help Show this message and exit`)
}