mailinabox.go raw
1 package mailinabox
2
3 import (
4 "encoding/json"
5 "io"
6 "net/http"
7 "net/url"
8 "time"
9
10 "github.com/nrdcg/mailinabox/errutils"
11 )
12
13 type service struct {
14 client *Client
15 }
16
17 // Client the Mail-in-a-Box client.
18 type Client struct {
19 httpClient *http.Client
20 baseURL *url.URL
21
22 email string
23 password string
24
25 common service // Reuse a single struct instead of allocating one for each service on the heap.
26
27 DNS *DNSService
28 User *UserService
29 Mail *MailService
30 System *SystemService
31 }
32
33 // New creates a new Client.
34 func New(apiURL, email, password string, opts ...Option) (*Client, error) {
35 baseURL, err := url.Parse(apiURL)
36 if err != nil {
37 return nil, err
38 }
39
40 client := &Client{
41 httpClient: &http.Client{Timeout: 10 * time.Second},
42 baseURL: baseURL,
43 email: email,
44 password: password,
45 }
46
47 for _, opt := range opts {
48 err := opt(client)
49 if err != nil {
50 return nil, err
51 }
52 }
53
54 client.common.client = client
55
56 client.DNS = (*DNSService)(&client.common)
57 client.User = (*UserService)(&client.common)
58 client.Mail = (*MailService)(&client.common)
59 client.System = (*SystemService)(&client.common)
60
61 return client, nil
62 }
63
64 func (c *Client) doJSON(req *http.Request, result any) error {
65 req.SetBasicAuth(c.email, c.password)
66 req.Header.Set("Accept", "application/json")
67
68 resp, err := c.httpClient.Do(req)
69 if err != nil {
70 return errutils.NewHTTPDoError(req, err)
71 }
72
73 defer func() { _ = resp.Body.Close() }()
74
75 if resp.StatusCode != http.StatusOK {
76 return errutils.NewUnexpectedResponseStatusCodeError(req, resp)
77 }
78
79 if result == nil {
80 return nil
81 }
82
83 raw, err := io.ReadAll(resp.Body)
84 if err != nil {
85 return errutils.NewReadResponseError(req, resp.StatusCode, err)
86 }
87
88 err = json.Unmarshal(raw, result)
89 if err != nil {
90 return errutils.NewUnmarshalError(req, resp.StatusCode, raw, err)
91 }
92
93 return nil
94 }
95
96 func (c *Client) doPlain(req *http.Request) ([]byte, error) {
97 req.SetBasicAuth(c.email, c.password)
98
99 resp, err := c.httpClient.Do(req)
100 if err != nil {
101 return nil, errutils.NewHTTPDoError(req, err)
102 }
103
104 defer func() { _ = resp.Body.Close() }()
105
106 if resp.StatusCode != http.StatusOK {
107 return nil, errutils.NewUnexpectedResponseStatusCodeError(req, resp)
108 }
109
110 raw, err := io.ReadAll(resp.Body)
111 if err != nil {
112 return nil, errutils.NewReadResponseError(req, resp.StatusCode, err)
113 }
114
115 return raw, nil
116 }
117
118 func boolToIntStr(v bool) string {
119 if v {
120 return "1"
121 }
122
123 return "0"
124 }
125
126 type Option func(*Client) error
127
128 func WithHTTPClient(client *http.Client) Option {
129 return func(c *Client) error {
130 if client != nil {
131 c.httpClient = client
132 }
133
134 return nil
135 }
136 }
137