aboutsummaryrefslogtreecommitdiff
path: root/src/mail.c
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2013-03-10 22:39:23 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2013-03-10 22:39:23 +0200
commit87602f5492b842f734dffe22f4e2f85dbc6ce713 (patch)
tree3e7e04bca15e8af5c981a78619e2d95f03b45c67 /src/mail.c
parent826bb71c57d903f760c89406f93d19fe0c131de0 (diff)
downloadwydawca-87602f5492b842f734dffe22f4e2f85dbc6ce713.tar.gz
wydawca-87602f5492b842f734dffe22f4e2f85dbc6ce713.tar.bz2
Replace mail notification with module event notification framework.
The new framework is to be general-purpose; the mail notification is implemented as a module. Althoug the program is operational, the change is not completed yet. In particular, the docs are out of date and the tests will mostly fail. * .gitignore: Update. * Makefile.am (SUBDIRS): Add modules. * configure.ac: Mailutils is now optional (though highly recommended). (AC_CONFIG_FILES): Build modules/Makefile and modules/mailutils/Makefile. * modules/Makefile.am: New file. * modules/mailutils/Makefile.am: New file. * modules/mailutils/mod_mailutils.c: New file. * src/mail.c: Remove. * src/mail.h: Remove. * src/event.c: New file. * src/Makefile.am (wydawca_SOURCES): Update. (LDADD): Remove MAILUTILS_LIBS. * src/config.c: Remove mail-related configuration statements. Add module-related ones instead. * src/wydawca.h (notification_event) <ev_statistics>: New event. (notification_target): Remove. (notification) <tgt,sign_keys,msg>: Remove. <statmask>: New member. (register_message_template): Remove. (notify_stats,notify_flush): New protos. (notification_target_str): Remove. (format_fn): Remove. (module) <next,modinit,open> <flush,close>: New members. <notify>: Change signature. (modules_close, module_set_init) (module_flush): New protos. (debug_level): Rename to wy_debug_level. All uses changed. (wy_version): New extern. (admin_stat_message,admin_stat_sign_key): Remove. (default_notification): New global. (triplet_expand_param,triplet_expand_dictionary_query): The file_triplet argument is const pointer. (assert_string_arg): Rename to wy_assert_string_arg. * src/wydawca.c (debug_level): Rename to wy_debug_level. (wy_version): New global. (logstats): Call notify_stats. (main): Call modules_close. * src/module.c: Keep modules in a singly-linked list instead of a symtab. Provide new functions. * src/net.c: Update. * src/dictionary.c: Update. * src/directive.c: Update. * src/diskio.c: Update. * src/exec.c: Update. * src/gpg.c: Update. * src/job.c (wydawca_scanner): Call notify_flush. * src/null.c: Update. * src/process.c: Update. * src/sql.c: Update. * src/tcpwrap.c: Update. * src/triplet.c: Update. * src/verify.c: Update. * src/watcher.c: Update.
Diffstat (limited to 'src/mail.c')
-rw-r--r--src/mail.c615
1 files changed, 0 insertions, 615 deletions
diff --git a/src/mail.c b/src/mail.c
deleted file mode 100644
index dd16510..0000000
--- a/src/mail.c
+++ /dev/null
@@ -1,615 +0,0 @@
-/* wydawca - automatic release submission daemon
- Copyright (C) 2007-2013 Sergey Poznyakoff
-
- Wydawca is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the
- Free Software Foundation; either version 3 of the License, or (at your
- option) any later version.
-
- Wydawca is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with wydawca. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "wydawca.h"
-#include <mail.h>
-#include <gpgme.h>
-
-int mailer_opened;
-mu_mailer_t mailer;
-mu_address_t admin_address;
-mu_address_t from_address;
-unsigned long mail_admin_mask;
-unsigned long owner_notification_flags;
-char *user_message_template[MAX_EVENT];
-
-char *admin_stat_message;
-char *admin_stat_sign_key;
-
-void
-mail_init()
-{
- if (!mailer) {
- int rc;
- if ((rc = mu_mailer_create(&mailer, NULL))) {
- const char *url = NULL;
- mu_mailer_get_url_default(&url);
- logmsg(LOG_ERR,
- _("cannot create default mailer `%s': %s"), url,
- mu_strerror(rc));
- }
- }
-}
-
-static ssize_t
-mu_stream_data_read_cb(void *handle, void *buffer, size_t size)
-{
- mu_stream_t str = handle;
- size_t nread;
- int rc;
-
- rc = mu_stream_read(str, buffer, size, &nread);
- if (rc) {
- logmsg(LOG_ERR, "mu_stream_read: %s", mu_strerror(rc));
- errno = EIO;
- return -1;
- }
- return nread;
-}
-
-static int
-check_sign_result(gpgme_sign_result_t result, gpgme_sig_mode_t type)
-{
- gpgme_new_signature_t sign;
-
- if (result->invalid_signers) {
- logmsg(LOG_ERR, _("GPGME: invalid signer found: %s"),
- result->invalid_signers->fpr);
- return 1;
- }
-
- if (!result->signatures) {
- logmsg(LOG_ERR, _("GPGME: no signatures created"));
- return 1;
- }
-
- for (sign = result->signatures; sign; sign = sign->next) {
- if (sign->type != type) {
- logmsg(LOG_ERR,
- _("GPGME: wrong type of signature created"));
- return 1;
- }
- }
- /* FIXME: fingerprint? */
- return 0;
-}
-
-static int
-gpg_sign(gpgme_data_t * output, gpgme_data_t input, const char *sign_keys)
-{
- gpgme_ctx_t ctx;
- gpgme_error_t err = 0;
- gpgme_key_t key;
- int ret;
-
- err = gpgme_new(&ctx);
- if (err) {
- logmsg(LOG_ERR, _("GPGME: cannot create context: %s"),
- gpgme_strerror(err));
- return 1;
- }
-
- err = gpgme_op_keylist_start(ctx, sign_keys, 0);
- if (!err) {
- while ((err = gpgme_op_keylist_next(ctx, &key)) == 0) {
- err = gpgme_signers_add(ctx, key);
- gpgme_key_release(key);
- }
- }
-
- if (err && gpg_err_code(err) != GPG_ERR_EOF) {
- logmsg(LOG_ERR, _("GPGME: cannot list keys: %s"),
- gpgme_strerror(err));
- gpgme_release(ctx);
- return 1;
- }
-
- err = gpgme_data_new(output);
- if (err) {
- logmsg(LOG_ERR, _("%s: GPGME error: %s"),
- "gpgme_data_new", gpgme_strerror(err));
- gpgme_release(ctx);
- return 1;
- }
-
- /* FIXME: Passphrase */
- gpgme_set_textmode(ctx, 1);
- gpgme_set_armor(ctx, 1);
-
- err = gpgme_op_sign(ctx, input, *output, GPGME_SIG_MODE_CLEAR);
- if (err) {
- logmsg(LOG_ERR, _("%s: GPGME error: %s"),
- "gpgme_op_sign", gpgme_strerror(err));
- ret = 1;
- } else {
- ret = check_sign_result(gpgme_op_sign_result(ctx),
- GPGME_SIG_MODE_CLEAR);
-#if 0
- /* FIXME: */
- if (debug_level > 1)
- gpgme_debug_info(ctx);
-#endif
- }
-
- gpgme_release(ctx);
- return ret;
-}
-
-static int
-sign_message(mu_message_t * pmsg, const char *key)
-{
- mu_message_t msg = *pmsg;
- mu_message_t newmsg;
- mu_body_t body;
- mu_header_t hdr;
- mu_stream_t istr, ostr, bodystr;
- int rc;
- struct gpgme_data_cbs cbs;
- gpgme_data_t input, output;
- gpgme_error_t err;
- char *buf = NULL;
- size_t size = 0;
- size_t nread;
-
- if (debug_level)
- logmsg(LOG_DEBUG, _("signing message as %s"), key);
-
- if (wydawca_gpg_homedir) {
- if (debug_level > 1)
- logmsg(LOG_DEBUG,
- _("setting GNUPG home directory: %s"),
- wydawca_gpg_homedir);
- setenv("GNUPGHOME", wydawca_gpg_homedir, 1);
- }
-
- if ((rc = mu_message_get_body(msg, &body))) {
- logmsg(LOG_ERR, "mu_message_get_body: %s", mu_strerror(rc));
- return 1;
- }
-
- if ((rc = mu_body_get_streamref(body, &bodystr))) {
- logmsg(LOG_ERR, "mu_message_get_stream: %s", mu_strerror(rc));
- return 1;
- }
-
- memset(&cbs, 0, sizeof(cbs));
- cbs.read = mu_stream_data_read_cb;
-
- err = gpgme_data_new_from_cbs(&input, &cbs, &bodystr);
- if (err) {
- logmsg(LOG_ERR, "gpgme_data_new_from_cbs: %s",
- gpgme_strerror(rc));
- return 1;
- }
-
- rc = gpg_sign(&output, input, key);
- mu_stream_unref(bodystr);
- if (rc)
- return 1;
-
- if (gpgme_data_seek(output, 0, SEEK_SET) == -1) {
- logmsg(LOG_ERR, "gpgme_data_seek: %s", strerror(errno));
- return 1;
- }
-
- mu_message_create(&newmsg, NULL);
- mu_message_get_streamref(newmsg, &ostr);
-
- /* Copy headers */
- mu_message_get_header(msg, &hdr);
- mu_header_get_streamref(hdr, &istr);
-
- rc = mu_stream_copy(ostr, istr, 0, NULL);
- if (rc)
- logmsg(LOG_ERR, "mu_stream_copy: %s", mu_strerror(rc));
- else {
- while ((nread = gpgme_data_read(output, buf, size)) > 0) {
- rc = mu_stream_write(ostr, buf, nread, NULL);
- if (rc) {
- logmsg(LOG_ERR, "mu_stream_write: %s",
- mu_strerror(rc));
- break;
- }
- }
-
- if (rc == 0) {
- mu_message_destroy(&msg, mu_message_get_owner(msg));
- *pmsg = newmsg;
- }
- }
- mu_stream_unref(istr);
- mu_stream_unref(ostr);
-
- if (rc)
- mu_message_destroy(&newmsg, mu_message_get_owner(msg));
- gpgme_data_release(output);
- free(buf);
-
- return rc;
-}
-
-void
-mail_send_message(mu_address_t rcpt, const char *text, const char *signer_key)
-{
- int rc;
- mu_message_t msg;
- mu_stream_t stream = NULL;
- mu_header_t hdr;
- static char *x_mailer = "wydawca (" PACKAGE_STRING ")";
- const char *sval;
-
- mu_static_memory_stream_create(&stream, text, strlen(text));
- rc = mu_stream_to_message(stream, &msg);
- mu_stream_unref(stream);
- if (rc) {
- logmsg(LOG_CRIT, _("cannot create message: %s"),
- mu_strerror(rc));
- return;
- }
- mu_message_get_header(msg, &hdr);
- mu_header_append(hdr, "X-Mailer", x_mailer);
-
- if (rcpt) {
- const char *s;
-
- if (mu_address_sget_printable(rcpt, &s) == 0)
- mu_header_set_value(hdr, "To", s, 1);
-
- if (from_address && mu_header_sget_value(hdr, "From", &sval)) {
- if (mu_address_sget_printable(from_address, &s) == 0)
- mu_header_set_value(hdr, "From", s, 1);
- }
- }
-
- if (debug_level > 1) {
- mu_debug_level_t level;
-
- mu_debug_get_category_level(MU_DEBCAT_MAILER, &level);
- level |= MU_DEBUG_LEVEL_MASK(MU_DEBUG_TRACE0) |
- MU_DEBUG_LEVEL_MASK(MU_DEBUG_PROT);
- if (debug_level > 2)
- level |= MU_DEBUG_LEVEL_MASK(MU_DEBUG_TRACE7);
- mu_debug_set_category_level(MU_DEBCAT_MAILER, level);
- }
-
- if (!mailer_opened) {
- if ((rc = mu_mailer_open(mailer, 0))) {
- mu_url_t url = NULL;
- mu_mailer_get_url(mailer, &url);
- logmsg(LOG_CRIT, _("opening mailer `%s' failed: %s"),
- url ? mu_url_to_string(url) : "(unknown URL)",
- mu_strerror(rc));
- return;
- }
- mailer_opened = 1;
- }
-
- if (signer_key)
- sign_message(&msg, signer_key);
-
- if (!dry_run_mode) {
- rc = mu_mailer_send_message(mailer, msg, from_address, rcpt);
- if (rc)
- logmsg(LOG_CRIT, _("cannot send message: %s"),
- mu_strerror(rc));
- }
-
- mu_message_destroy(&msg, mu_message_get_owner(msg));
-}
-
-void
-mail_finish()
-{
- if (mailer_opened) {
- mu_mailer_close(mailer);
- mailer_opened = 0;
- }
-}
-
-struct message_template {
- char *name;
- char *text;
- /* int mime; for future use */
-};
-
-static struct grecs_symtab *tmpl_table;
-
-/* Register a template. */
-void
-register_message_template(const char *name, const char *text)
-{
- struct message_template key, *tmpl;
- int install = 1;
-
- key.name = (char *)text;
-
- if (!tmpl_table) {
- tmpl_table = grecs_symtab_create_default(sizeof(key));
- if (!tmpl_table)
- grecs_alloc_die();
- }
-
- tmpl = grecs_symtab_lookup_or_install(tmpl_table, &key, &install);
- if (!tmpl)
- grecs_alloc_die();
- if (!install)
- grecs_warning(NULL, 0, "template %s already registered", text);
-}
-
-const char *
-resolve_message_template(const char *name)
-{
- if (name[0] == '@') {
- if (name[1] == '@')
- return name + 1;
- else if (!tmpl_table)
- return NULL;
- else {
- struct message_template *p, key;
-
- key.name = (char *)name + 1;
- p = grecs_symtab_lookup_or_install(tmpl_table, &key,
- NULL);
- return p ? p->text : NULL;
- }
- }
- return name;
-}
-
-void
-mail_stats()
-{
- struct metadef *exp;
- time_t t;
- const char *tmpl;
- char *text;
- size_t tc;
-
- if (!admin_stat_message || !stat_mask_p(mail_admin_mask) || !mailer)
- return;
-
- if (!admin_address) {
- logmsg(LOG_ERR,
- _("cannot mail statistics: admin-address not defined"));
- return;
- }
-
- if (debug_level) {
- const char *s;
-
- if (mu_address_sget_printable(admin_address, &s) == 0)
- logmsg(LOG_DEBUG, _("sending stats to %s"), s);
- }
-
- tc = timer_get_count() * 3;
- exp = make_stat_expansion(tc + 1);
- time(&t);
- exp[0].kw = "date";
- exp[0].value = exp[0].storage = grecs_strdup(ctime(&t));
- exp[0].value[strlen(exp[0].value) - 1] = 0;
- timer_fill_meta(exp + 1, tc);
-
- tmpl = resolve_message_template(admin_stat_message);
- if (!tmpl) {
- logmsg(LOG_ERR, _("undefined message reference: %s"),
- admin_stat_message);
- return;
- }
- text = meta_expand_string(tmpl, exp, NULL, NULL, NULL);
-
- mail_send_message(admin_address, text, admin_stat_sign_key);
-
- free(text);
- meta_free(exp);
- timer_free_meta(exp + 1, tc);
- free(exp);
-}
-
-mu_address_t
-get_uploader_email(struct uploader_info * info, struct file_triplet * trp,
- const char **errp)
-{
- mu_address_t addr;
- mu_address_t rcpt = NULL;
- int rc;
-
- if (!info)
- return NULL;
- rc = mu_address_create(&addr, info->email);
- if (rc) {
- *errp = mu_strerror(rc);
- return NULL;
- }
-
- mu_address_set_personal(addr, 1, info->realname);
-
- /* This hack makes sure that mu_address_to_string (rcpt) will
- return full email address (with personal part) */
- mu_address_union(&rcpt, addr);
- mu_address_destroy(&addr);
-
- return rcpt;
-}
-
-mu_address_t
-get_recipient(struct dictionary * dict, struct file_triplet * trp,
- const char **errp)
-{
- unsigned nrows, ncols, i;
- mu_address_t rcpt = NULL;
- char *text;
- int rc;
- void *md;
-
- if (dict->type == dictionary_none) {
- *errp = N_("dictionary is not configured");
- return NULL;
- }
-
- md = dictionary_open(dict);
- if (!md) {
- *errp = N_("failed to open dictionary");
- return NULL;
- }
-
- text = triplet_expand_dictionary_query(dict, md, trp);
-
- rc = dictionary_lookup(dict, md, text);
- free(text);
- if (rc) {
- *errp = N_("cannot obtain recipient emails");
- dictionary_close(dict, md);
- return NULL;
- }
-
- nrows = dictionary_num_rows(dict);
- ncols = dictionary_num_cols(dict);
-
- if (nrows == 0) {
- *errp = N_("cannot obtain recipient emails");
- return NULL;
- }
-
- for (i = 0; i < nrows; i++) {
- mu_address_t addr;
- const char *str = dictionary_result(dict, md, i, 0);
- if (mu_address_create(&addr, str))
- continue;
- if (ncols > 0) {
- str = dictionary_result(dict, md, i, 1);
- if (str)
- mu_address_set_personal(addr, 1, str);
- }
- mu_address_union(&rcpt, addr);
- mu_address_destroy(&addr);
- }
- dictionary_close(dict, md);
-
- return rcpt;
-}
-
-void
-do_notify(struct file_triplet *trp, enum notification_event ev,
- struct notification *ntf)
-{
- mu_address_t rcpt = NULL;
- const char *errp;
- const char *msg;
-
- if (ntf->modname) {
- module_notify(ntf->modname, ntf->modcfg, ev, trp);
- return;
- }
-
- switch (ntf->tgt) {
- case notify_read:
- rcpt = NULL;
- break;
-
- case notify_admin:
- rcpt = mu_address_dup(admin_address);
- break;
-
- case notify_user:
- rcpt = get_uploader_email(trp->uploader, trp, &errp);
- break;
-
- case notify_owner:
- rcpt = get_recipient(trp->spool->dictionary[project_owner_dict],
- trp, &errp);
- }
-
- if (!rcpt && ntf->tgt != notify_read) {
- logmsg(LOG_ERR,
- _("not notifying %s (project %s) about %s: %s"),
- notification_target_str(ntf->tgt),
- trp->project, notification_event_str(ev),
- gettext(errp));
- return;
- }
-
- if (debug_level) {
- if (rcpt) {
- const char *s;
-
- if (mu_address_sget_printable(rcpt, &s) == 0)
- logmsg(LOG_DEBUG,
- _("notifying %s (project %s) about %s"),
- s, trp->project,
- notification_event_str(ev));
- } else
- logmsg(LOG_DEBUG,
- _("notifying message recipients (project %s) "
- "about %s"),
- trp->project, notification_event_str(ev));
- }
-
- msg = resolve_message_template(ntf->msg);
- if (!msg)
- logmsg(LOG_ERR, _("undefined message reference: %s"),
- ntf->msg);
- else {
- char *text = triplet_expand_param(msg, trp);
- mail_send_message(rcpt, text, ntf->sign_keys);
- free(text);
- }
-
- mu_address_destroy(&rcpt);
-}
-
-void
-notify(struct notification *notification_list,
- struct file_triplet *trp, enum notification_event ev)
-{
- struct notification *p;
-
- for (p = notification_list; p; p = p->next)
- if (p->ev == ev)
- do_notify(trp, ev, p);
-}
-
-const char *
-expand_email_admin(struct metadef *def, void *data)
-{
- if (mu_address_aget_printable(admin_address, &def->storage) == 0)
- def->value = def->storage;
- else
- def->value = "";
- return def->value;
-}
-
-const char *
-expand_email_owner(struct metadef *def, void *data)
-{
- struct file_triplet *trp = data;
- mu_address_t addr;
- const char *errp;
-
- addr = get_recipient(trp->spool->dictionary[project_owner_dict],
- trp, &errp);
- if (!addr) {
- logmsg(LOG_ERR, _("cannot get email of the %s's owner: %s"),
- trp->project, gettext(errp));
- def->value = "";
- } else {
- if (mu_address_aget_printable(addr, &def->storage) == 0)
- def->value = def->storage;
- else
- def->value = "";
- mu_address_destroy(&addr);
- }
- return def->value;
-}

Return to:

Send suggestions and report system problems to the System administrator.