1// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2// See LICENSE.txt for license information.
3
4package mail
5
6import (
7	"bytes"
8	"encoding/json"
9	"fmt"
10	"io"
11	"io/ioutil"
12	"net/http"
13	"os"
14	"strings"
15	"time"
16)
17
18const (
19	InbucketAPI = "/api/v1/mailbox/"
20)
21
22// OutputJSONHeader holds the received Header to test sending emails (inbucket)
23type JSONMessageHeaderInbucket []struct {
24	Mailbox             string
25	ID                  string `json:"Id"`
26	From, Subject, Date string
27	To                  []string
28	Size                int
29}
30
31// OutputJSONMessage holds the received Message fto test sending emails (inbucket)
32type JSONMessageInbucket struct {
33	Mailbox             string
34	ID                  string `json:"Id"`
35	From, Subject, Date string
36	Size                int
37	Header              map[string][]string
38	Body                struct {
39		Text string
40		HTML string `json:"Html"`
41	}
42	Attachments []struct {
43		Filename     string
44		ContentType  string `json:"content-type"`
45		DownloadLink string `json:"download-link"`
46		Bytes        []byte `json:"-"`
47	}
48}
49
50func ParseEmail(email string) string {
51	pos := strings.Index(email, "@")
52	parsedEmail := email[0:pos]
53	return parsedEmail
54}
55
56func GetMailBox(email string) (results JSONMessageHeaderInbucket, err error) {
57
58	parsedEmail := ParseEmail(email)
59
60	url := fmt.Sprintf("%s%s%s", getInbucketHost(), InbucketAPI, parsedEmail)
61	resp, err := http.Get(url)
62
63	if err != nil {
64		return nil, err
65	}
66
67	defer func() {
68		io.Copy(ioutil.Discard, resp.Body)
69		resp.Body.Close()
70	}()
71
72	if resp.Body == nil {
73		return nil, fmt.Errorf("no mailbox")
74	}
75
76	var record JSONMessageHeaderInbucket
77	err = json.NewDecoder(resp.Body).Decode(&record)
78	switch {
79	case err == io.EOF:
80		return nil, fmt.Errorf("error: %s", err)
81	case err != nil:
82		return nil, fmt.Errorf("error: %s", err)
83	}
84	if len(record) == 0 {
85		return nil, fmt.Errorf("no mailbox")
86	}
87
88	return record, nil
89}
90
91func GetMessageFromMailbox(email, id string) (JSONMessageInbucket, error) {
92	parsedEmail := ParseEmail(email)
93
94	var record JSONMessageInbucket
95
96	url := fmt.Sprintf("%s%s%s/%s", getInbucketHost(), InbucketAPI, parsedEmail, id)
97	emailResponse, err := http.Get(url)
98	if err != nil {
99		return record, err
100	}
101	defer func() {
102		io.Copy(ioutil.Discard, emailResponse.Body)
103		emailResponse.Body.Close()
104	}()
105
106	if err = json.NewDecoder(emailResponse.Body).Decode(&record); err != nil {
107		return record, err
108	}
109
110	// download attachments
111	if record.Attachments != nil && len(record.Attachments) > 0 {
112		for i := range record.Attachments {
113			var bytes []byte
114			bytes, err = downloadAttachment(record.Attachments[i].DownloadLink)
115			if err != nil {
116				return record, err
117			}
118			record.Attachments[i].Bytes = make([]byte, len(bytes))
119			copy(record.Attachments[i].Bytes, bytes)
120		}
121	}
122
123	return record, err
124}
125
126func downloadAttachment(url string) ([]byte, error) {
127	attachmentResponse, err := http.Get(url)
128	if err != nil {
129		return nil, err
130	}
131	defer attachmentResponse.Body.Close()
132
133	buf := new(bytes.Buffer)
134	io.Copy(buf, attachmentResponse.Body)
135	return buf.Bytes(), nil
136}
137
138func DeleteMailBox(email string) (err error) {
139
140	parsedEmail := ParseEmail(email)
141
142	url := fmt.Sprintf("%s%s%s", getInbucketHost(), InbucketAPI, parsedEmail)
143	req, err := http.NewRequest("DELETE", url, nil)
144	if err != nil {
145		return err
146	}
147
148	client := &http.Client{}
149
150	resp, err := client.Do(req)
151	if err != nil {
152		return err
153	}
154	defer resp.Body.Close()
155
156	return nil
157}
158
159func RetryInbucket(attempts int, callback func() error) (err error) {
160	for i := 0; ; i++ {
161		err = callback()
162		if err == nil {
163			return nil
164		}
165
166		if i >= (attempts - 1) {
167			break
168		}
169
170		time.Sleep(5 * time.Second)
171
172		fmt.Println("retrying...")
173	}
174	return fmt.Errorf("after %d attempts, last error: %s", attempts, err)
175}
176
177func getInbucketHost() (host string) {
178
179	inbucket_host := os.Getenv("CI_INBUCKET_HOST")
180	if inbucket_host == "" {
181		inbucket_host = "localhost"
182	}
183
184	inbucket_port := os.Getenv("CI_INBUCKET_PORT")
185	if inbucket_port == "" {
186		inbucket_port = "10080"
187	}
188	return fmt.Sprintf("http://%s:%s", inbucket_host, inbucket_port)
189}
190