#include "mail.h" #include #include #include #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.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; }