File: attach.go

package info (click to toggle)
golang-github-xhit-go-simple-mail 2.16.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 304 kB
  • sloc: makefile: 2
file content (157 lines) | stat: -rw-r--r-- 3,797 bytes parent folder | download | duplicates (2)
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
157
package mail

import (
	"encoding/base64"
	"errors"
	"io/ioutil"
	"mime"
	"path/filepath"
)

// File represents the file that can be added to the email message.
// You can add attachment from file in path, from base64 string or from []byte.
// You can define if attachment is inline or not.
// Only one, Data, B64Data or FilePath is supported. If multiple are set, then
// the first in that order is used.
type File struct {
	// FilePath is the path of the file to attach.
	FilePath string
	// Name is the name of file in attachment. Required for Data and B64Data. Optional for FilePath.
	Name string
	// MimeType of attachment. If empty then is obtained from Name (if not empty) or FilePath. If cannot obtained, application/octet-stream is set.
	MimeType string
	// B64Data is the base64 string to attach.
	B64Data string
	// Data is the []byte of file to attach.
	Data []byte
	// Inline defines if attachment is inline or not.
	Inline bool
}

type attachType int

const (
	attachData attachType = iota
	attachB64
	attachFile
)

// Attach allows you to add an attachment to the email message.
// The attachment can be inlined
func (email *Email) Attach(file *File) *Email {
	if email.Error != nil {
		return email
	}

	var name = file.Name
	var mimeType = file.MimeType

	// if no alternative name was provided, get the filename
	if len(name) == 0 && len(file.FilePath) > 0 {
		_, name = filepath.Split(file.FilePath)
	}

	// get the mimetype
	if mimeType == "" {
		mimeType = mime.TypeByExtension(filepath.Ext(name))
		if mimeType == "" {
			mimeType = "application/octet-stream"
		}
	}

	attachTy, err := getAttachmentType(file)
	if err != nil {
		email.Error = errors.New("Mail Error: Failed to add attachment with following error: " + err.Error())
		return email
	}

	file.Name = name
	file.MimeType = mimeType

	switch attachTy {
	case attachData:
		email.attachData(file)
	case attachB64:
		email.Error = email.attachB64(file)
	case attachFile:
		email.Error = email.attachFile(file)
	}

	return email
}

func getAttachmentType(file *File) (attachType, error) {
	// 1- data
	// 2- base64
	// 3- file

	// first check if Data
	if len(file.Data) > 0 {
		// data requires a name
		if len(file.Name) == 0 {
			return 0, errors.New("attach from bytes requires a name")
		}
		return attachData, nil
	}

	// check if base64
	if len(file.B64Data) > 0 {
		// B64Data requires a name
		if len(file.Name) == 0 {
			return 0, errors.New("attach from base64 string requires a name")
		}
		return attachB64, nil
	}

	// check if file
	if len(file.FilePath) > 0 {
		return attachFile, nil
	}

	return 0, errors.New("empty attachment")
}

// attachB64 does the low level attaching of the files but decoding base64
func (email *Email) attachB64(file *File) error {

	// decode the string
	dec, err := base64.StdEncoding.DecodeString(file.B64Data)
	if err != nil {
		return errors.New("Mail Error: Failed to decode base64 attachment with following error: " + err.Error())
	}

	email.attachData(&File{
		Name:     file.Name,
		MimeType: file.MimeType,
		Data:     dec,
		Inline:   file.Inline,
	})

	return nil
}

func (email *Email) attachFile(file *File) error {
	data, err := ioutil.ReadFile(file.FilePath)
	if err != nil {
		return errors.New("Mail Error: Failed to add file with following error: " + err.Error())
	}

	email.attachData(&File{
		Name:     file.Name,
		MimeType: file.MimeType,
		Data:     data,
		Inline:   file.Inline,
	})

	return nil
}

// attachData does the low level attaching of the in-memory data
func (email *Email) attachData(file *File) {
	// use inlines and attachments because is necessary to know if message has related parts and mixed parts
	if file.Inline {
		email.inlines = append(email.inlines, file)
	} else {
		email.attachments = append(email.attachments, file)
	}
}