Skip to content

Commit

Permalink
mod_smtp_mailing_lists: Fix delivery bugs.
Browse files Browse the repository at this point in the history
* Fix using sender list when samesenders=no (default)
  instead of recipient list, resulting in messages
  not being delivered to recipients.
* Fix improper header manipulation resulting in malformed
  RFC822 message.
* Fix missing From header in mailing list posts.
* Add CLI commands for mailboxes and mailing lists.
  • Loading branch information
InterLinked1 committed Nov 9, 2023
1 parent 85393c5 commit 994842f
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 6 deletions.
46 changes: 45 additions & 1 deletion modules/mod_mail.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "include/base64.h"
#include "include/stringlist.h"
#include "include/range.h"
#include "include/cli.h"

#include "include/mod_mail.h"

Expand Down Expand Up @@ -2008,6 +2009,44 @@ int maildir_ordered_traverse(const char *path, int (*on_file)(const char *dir_na
return res;
}

static int cli_mailboxes(struct bbs_cli_args *a)
{
struct mailbox *mbox;
RWLIST_RDLOCK(&mailboxes);
RWLIST_TRAVERSE(&mailboxes, mbox, entry) {
if (mbox->name) {
bbs_dprintf(a->fdout, "%s\n", mbox->name);
} else {
bbs_dprintf(a->fdout, "User ID: %u\n", mbox->id);
}
}
RWLIST_UNLOCK(&mailboxes);
return 0;
}

static int cli_mailbox(struct bbs_cli_args *a)
{
struct mailbox *mbox = mailbox_get_by_name(a->argv[1], a->argc >= 3 ? a->argv[2] : NULL);
if (!mbox) {
bbs_dprintf(a->fdout, "No such mailbox: %s%s%s\n", a->argv[1], a->argc >= 3 ? "@" : "", a->argc >= 3 ? a->argv[2] : "");
return 0;
}
bbs_dprintf(a->fdout, "%-20s: %u\n", "User ID", mbox->id);
bbs_dprintf(a->fdout, "%-20s: %s\n", "Name", S_IF(mbox->name));
bbs_dprintf(a->fdout, "%-20s: %s\n", "Maildir", mbox->maildir);
bbs_dprintf(a->fdout, "%-20s: %9lu KB\n", "Total Quota", mailbox_quota(mbox) / 1024);
bbs_dprintf(a->fdout, "%-20s: %9lu KB\n", "Quota Used", mailbox_quota_used(mbox) / 1024);
bbs_dprintf(a->fdout, "%-20s: %9lu KB\n", "Quota Remaining", mailbox_quota_remaining(mbox) / 1024);
bbs_dprintf(a->fdout, "%-20s: %u\n", "# Mailbox Watchers", mbox->watchers);
bbs_dprintf(a->fdout, "%-20s: %s\n", "Activity Pending", BBS_YN(mbox->activity));
return 0;
}

static struct bbs_cli_entry cli_commands_mailboxes[] = {
BBS_CLI_COMMAND(cli_mailboxes, "mailboxes", 1, "List currently loaded mailboxes", NULL),
BBS_CLI_COMMAND(cli_mailbox, "mailbox", 2, "Show mailbox details", "mailbox <user> [<domain>]"),
};

static int load_config(void)
{
struct bbs_config *cfg;
Expand Down Expand Up @@ -2058,11 +2097,16 @@ static int load_config(void)

static int load_module(void)
{
return load_config();
if (load_config()) {
return -1;
}
bbs_cli_register_multiple(cli_commands_mailboxes);
return 0;
}

static int unload_module(void)
{
bbs_cli_unregister_multiple(cli_commands_mailboxes);
mailbox_cleanup();
return 0;
}
Expand Down
67 changes: 62 additions & 5 deletions modules/mod_smtp_mailing_lists.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "include/utils.h"
#include "include/stringlist.h"
#include "include/linkedlists.h"
#include "include/cli.h"

#include "include/mod_mail.h"
#include "include/net_smtp.h"
Expand Down Expand Up @@ -251,7 +252,7 @@ static int listify(struct mailing_list *l, struct smtp_response *resp, FILE *fp,
/* Actual bytes consumed from srcfd should be res + 2 (CR LF) */
consumed += (int) res + 2;
if (!res) {
fprintf(fp, "\r\n");
/* We're not done writing *our* headers, so don't end the headers just yet */
break; /* EOH (end of headers) */
}
/* Analyze the header received */
Expand Down Expand Up @@ -295,6 +296,7 @@ static int listify(struct mailing_list *l, struct smtp_response *resp, FILE *fp,
} else if (STARTS_WITH(buf, "From:")) {
const char *from = buf + STRLEN("From:");
if (!strlen_zero(from)) {
fprintf(fp, "From:%s\r\n", from); /* Use the original From header */
safe_strncpy(from_hdr, from, sizeof(from_hdr));
}
} else {
Expand All @@ -313,6 +315,7 @@ static int listify(struct mailing_list *l, struct smtp_response *resp, FILE *fp,
}

/* Now, add the message body */
fprintf(fp, "\r\n"); /* Finish headers */
fflush(fp);
body_bytes = (int) origlen - consumed;
res = bbs_copy_file(srcfd, fileno(fp), consumed, body_bytes);
Expand All @@ -338,9 +341,8 @@ static int list_post_message(struct mailing_list *l, const char *msgfile, size_t
const char *s;
struct stringlist local;
struct stringitem *i = NULL;
int localcount = 0, manuallocalcount = 0;
int localcount = 0, manuallocalcount = 0, extcount = 0;
char mailfrom[265];
struct stringlist *r = l->samesenders ? &l->recipients : &l->senders;

memset(&local, 0, sizeof(local));

Expand All @@ -353,7 +355,7 @@ static int list_post_message(struct mailing_list *l, const char *msgfile, size_t
* To get the best of both worlds, for any local users, we use a single SMTP transaction, since we trust our own bounces,
* and for external users, we can use VERP, since due to queuing, there's already 1 transaction per external recipient anyways. */

while ((s = stringlist_next(r, &i))) { /* This list is read only, we must not modify it */
while ((s = stringlist_next(&l->recipients, &i))) { /* This list is read only, we must not modify it */
char full[256];
if (!strcmp(s, "*")) {
/* Expands to all active local users */
Expand Down Expand Up @@ -401,15 +403,22 @@ static int list_post_message(struct mailing_list *l, const char *msgfile, size_t
safe_strncpy(replaced, s, sizeof(replaced));
bbs_strreplace(replaced, '@', '=');
snprintf(mailfrom, sizeof(mailfrom), "%s@%s+bounce=%s", l->user, S_OR(l->domain, bbs_hostname()), replaced); /* No <> */
smtp_inject(mailfrom, &local, msgfile, msglen);
smtp_inject(mailfrom, &external, msgfile, msglen); /* Deliver to the external recipient */
extcount++;
}
}

/* If there are any local recipients, deliver to them all at once */
if (localcount) {
snprintf(mailfrom, sizeof(mailfrom), "%s@%s+bounce", l->user, S_OR(l->domain, bbs_hostname())); /* No <> */
smtp_inject(mailfrom, &local, msgfile, msglen);
}

bbs_debug(2, "Delivered post to %d local user%s (%d explicitly) and %d external user%s\n",
localcount, ESS(localcount), manuallocalcount, extcount, ESS(extcount));
if (localcount + extcount == 0) {
bbs_warning("Mailing list %s@%s has no recipients?\n", l->user, S_OR(l->domain, bbs_hostname()));
}
return 0;
}

Expand Down Expand Up @@ -558,6 +567,52 @@ struct smtp_delivery_agent exploder = {
.deliver = blast_exploder,
};

static int cli_mailing_lists(struct bbs_cli_args *a)
{
struct mailing_list *l;

RWLIST_TRAVERSE(&lists, l, entry) {
bbs_dprintf(a->fdout, "%s%s%s\n", l->user, l->domain ? "@" : "", S_IF(l->domain));
}
return 0;
}

static int cli_mailing_list(struct bbs_cli_args *a)
{
struct mailing_list *l;
struct stringlist *r;
struct stringitem *i = NULL;
const char *s;

l = find_list(a->argv[2], a->argc >= 4 ? a->argv[3] : NULL);
if (!l) {
bbs_dprintf(a->fdout, "No such list: %s%s%s\n", a->argv[2], a->argc >= 4 ? "@" : "", a->argc >= 4 ? a->argv[3] : "");
return 0;
}

/* XXX Could also show other config properties */

/* Dump recipients and allowed senders */
r = l->samesenders ? &l->recipients : &l->senders;
bbs_dprintf(a->fdout, "Recipients:\n");
while ((s = stringlist_next(&l->recipients, &i))) {
bbs_dprintf(a->fdout, " - %s\n", s);
}
bbs_dprintf(a->fdout, "Authorized Senders: %s\n", stringlist_is_empty(r) ? "All Registered Users" : l->samesenders ? "Same As Recipients" : "");
if (!l->samesenders) {
while ((s = stringlist_next(r, &i))) {
bbs_dprintf(a->fdout, " - %s\n", s);
}
}

return 0;
}

static struct bbs_cli_entry cli_commands_smtp_mailing_lists[] = {
BBS_CLI_COMMAND(cli_mailing_lists, "smtp lists", 2, "Enumerate mailing lists", NULL),
BBS_CLI_COMMAND(cli_mailing_list, "smtp list", 3, "Show details of a mailing list", "smtp list <user> [<domain>]"),
};

static int load_config(void)
{
struct bbs_config *cfg;
Expand Down Expand Up @@ -663,12 +718,14 @@ static int load_module(void)
if (load_config()) {
return -1;
}
bbs_cli_register_multiple(cli_commands_smtp_mailing_lists);
return smtp_register_delivery_handler(&exploder, 5); /* Takes priority over individual user mailboxes */
}

static int unload_module(void)
{
int res = smtp_unregister_delivery_agent(&exploder);
bbs_cli_unregister_multiple(cli_commands_smtp_mailing_lists);
RWLIST_WRLOCK_REMOVE_ALL(&lists, entry, list_free);
return res;
}
Expand Down

0 comments on commit 994842f

Please sign in to comment.