| 180 | } |
| 181 | |
| 182 | func parseEmail(raw []byte, recipients []string) (*ParsedEmail, []parsedAttachment, error) { |
| 183 | msg, err := mail.ReadMessage(bytes.NewReader(raw)) |
| 184 | if err != nil { |
| 185 | return nil, nil, err |
| 186 | } |
| 187 | |
| 188 | // Parse Message-ID. |
| 189 | var msgID *string |
| 190 | if id := msg.Header.Get("Message-ID"); id != "" { |
| 191 | msgID = &id |
| 192 | } |
| 193 | |
| 194 | to := parseAddresses(msg.Header, "To") |
| 195 | cc := parseAddresses(msg.Header, "Cc") |
| 196 | |
| 197 | // Calculate BCC: recipients not in To or CC. |
| 198 | visibleEmails := make(map[string]bool) |
| 199 | for _, addr := range to { |
| 200 | visibleEmails[addr.Email] = true |
| 201 | } |
| 202 | for _, addr := range cc { |
| 203 | visibleEmails[addr.Email] = true |
| 204 | } |
| 205 | var bcc []string |
| 206 | for _, r := range recipients { |
| 207 | if !visibleEmails[r] { |
| 208 | bcc = append(bcc, r) |
| 209 | } |
| 210 | } |
| 211 | if bcc == nil { |
| 212 | bcc = []string{} |
| 213 | } |
| 214 | |
| 215 | // Decode RFC 2047 encoded-word in Subject header (e.g. =?utf-8?Q?...?=). |
| 216 | subject := msg.Header.Get("Subject") |
| 217 | dec := new(mime.WordDecoder) |
| 218 | if decoded, err := dec.DecodeHeader(subject); err == nil { |
| 219 | subject = decoded |
| 220 | } |
| 221 | |
| 222 | parsed := &ParsedEmail{ |
| 223 | ID: msgID, |
| 224 | Subject: subject, |
| 225 | Raw: string(raw), |
| 226 | From: parseAddresses(msg.Header, "From"), |
| 227 | To: to, |
| 228 | Cc: cc, |
| 229 | Bcc: bcc, |
| 230 | ReplyTo: parseAddresses(msg.Header, "Reply-To"), |
| 231 | } |
| 232 | |
| 233 | contentType := msg.Header.Get("Content-Type") |
| 234 | if contentType == "" { |
| 235 | contentType = "text/plain" |
| 236 | } |
| 237 | |
| 238 | mediaType, params, err := mime.ParseMediaType(contentType) |
| 239 | if err != nil || !strings.HasPrefix(mediaType, "multipart/") { |