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