aboutsummaryrefslogtreecommitdiff
path: root/src/mail.c
blob: ca35f6d356a58ab0565e343516224a8ba365b61c (plain)
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
158
159
160
161
162
163
164
165
166
167
168
169
#include "mail.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "db.h"
#include "secret.h"
#include "smtp.h"
#include "util.h"

struct mail_cfg {
    char *host;
    char *port;
    char *from;
    char *reply_to;
    char *security;
    char *user;
    char *password;
    char from_name[256];
};

static void mail_cfg_free(struct mail_cfg *c)
{
    free(c->host);
    free(c->port);
    free(c->from);
    free(c->reply_to);
    free(c->security);
    free(c->user);
    free(c->password);
    memset(c, 0, sizeof *c);
}

static void set_err(char *err, size_t errlen, const char *msg)
{
    if (err && errlen)
        snprintf(err, errlen, "%s", msg);
}

static int mail_cfg_load(sqlite3 *db, int64_t org_id, struct mail_cfg *c,
                         char *err, size_t errlen)
{
    memset(c, 0, sizeof *c);
    c->host = db_setting(db, org_id, "smtp_host");
    c->port = db_setting(db, org_id, "smtp_port");
    c->from = db_setting(db, org_id, "smtp_from");
    c->reply_to = db_setting(db, org_id, "smtp_reply_to");
    c->security = db_setting(db, org_id, "smtp_security");
    c->user = db_setting(db, org_id, "smtp_user");
    c->password = db_setting(db, org_id, "smtp_password");

    if (!c->host || !*c->host || !c->from || !*c->from) {
        set_err(err, errlen, "smtp_host and smtp_from must be set");
        goto fail;
    }
    if (c->user && *c->user) {
        if (!c->password || !*c->password) {
            set_err(err, errlen,
                    "smtp_user is set but smtp_password is missing");
            goto fail;
        }
        if (!secret_available()) {
            set_err(err, errlen,
                    "smtp_password is set but BOKFD_SECRET_KEY is missing or"
                    " invalid");
            goto fail;
        }
        char *plain = NULL;
        if (secret_decrypt(c->password, &plain) != 0 || !plain) {
            free(plain);
            set_err(err, errlen,
                    "cannot decrypt smtp_password, check BOKFD_SECRET_KEY");
            goto fail;
        }
        free(c->password);
        c->password = plain;
    }

    sqlite3_stmt *st = NULL;
    int ok = 0;
    if (sqlite3_prepare_v2(db, "SELECT name FROM orgs WHERE id=?1", -1, &st,
                           NULL) == SQLITE_OK) {
        sqlite3_bind_int64(st, 1, org_id);
        if (sqlite3_step(st) == SQLITE_ROW) {
            const unsigned char *name = sqlite3_column_text(st, 0);
            snprintf(c->from_name, sizeof c->from_name, "%s",
                     name ? (const char *)name : "");
            ok = 1;
        }
    }
    sqlite3_finalize(st);
    if (!ok) {
        set_err(err, errlen, "org not found");
        goto fail;
    }
    return 0;

fail:
    mail_cfg_free(c);
    return -1;
}

static int mail_cfg_port(const char *s)
{
    if (s && *s) {
        long v = strtol(s, NULL, 10);
        if (v >= 1 && v <= 65535)
            return (int)v;
    }
    return 587;
}

int mail_config_check(sqlite3 *db, int64_t org_id, char *err, size_t errlen)
{
    struct mail_cfg c;
    if (err && errlen)
        err[0] = '\0';
    if (!db) {
        set_err(err, errlen, "no database");
        return -1;
    }
    if (mail_cfg_load(db, org_id, &c, err, errlen) != 0)
        return -1;
    mail_cfg_free(&c);
    return 0;
}

int mail_send_pdf(sqlite3 *db, int64_t org_id, const struct mail_message *m,
                  char *err, size_t errlen)
{
    if (err && errlen)
        err[0] = '\0';
    if (!m || !m->to || !*m->to) {
        set_err(err, errlen, "no recipient");
        return -2;
    }
    struct mail_cfg c;
    if (mail_cfg_load(db, org_id, &c, err, errlen) != 0)
        return -1;

    struct smtp_message sm;
    memset(&sm, 0, sizeof sm);
    sm.host = c.host;
    sm.port = mail_cfg_port(c.port);
    sm.security = c.security && *c.security ? c.security : "starttls";
    sm.user = c.user && *c.user ? c.user : "";
    sm.password = c.password ? c.password : "";
    sm.from = c.from;
    sm.from_name = c.from_name;
    sm.reply_to = c.reply_to && *c.reply_to ? c.reply_to : NULL;
    sm.to = m->to;
    sm.subject = m->subject;
    sm.body = m->body;
    sm.attach_name = m->attach_name;
    sm.attach = m->attach;
    sm.attach_len = m->attach_len;

    char send_err[512];
    send_err[0] = '\0';
    int rc = smtp_send(&sm, send_err, sizeof send_err);
    mail_cfg_free(&c);
    if (rc != 0) {
        set_err(err, errlen,
                send_err[0] ? send_err : "smtp send failed");
        return -2;
    }
    return 0;
}