Skip to content

Commit

Permalink
System notification via event notification system
Browse files Browse the repository at this point in the history
Introduces `devel_new_mail_command` config option. When used instead of
`new_mail_command`, Neomutt's event notification system will be used for
new mail notifications. This is part of a bigger refactoring of the
legacy notification approach. The initial implementation uses only very
limited set of expandos.
  • Loading branch information
elshize committed Jun 6, 2022
1 parent 1c0a259 commit 1976ce5
Show file tree
Hide file tree
Showing 14 changed files with 295 additions and 7 deletions.
2 changes: 1 addition & 1 deletion Makefile.autosetup
Expand Up @@ -563,7 +563,7 @@ LIBMUTTOBJS= mutt/atoi.o mutt/base64.o mutt/buffer.o mutt/charset.o \
mutt/date.o mutt/envlist.o mutt/exit.o mutt/file.o \
mutt/filter.o mutt/hash.o mutt/list.o mutt/logging.o \
mutt/mapping.o mutt/mbyte.o mutt/md5.o mutt/memory.o \
mutt/notify.o mutt/path.o mutt/pool.o mutt/prex.o \
mutt/new_mail.o mutt/notify.o mutt/path.o mutt/pool.o mutt/prex.o \
mutt/qsort_r.o mutt/random.o mutt/regex.o mutt/signal.o \
mutt/slist.o mutt/state.o mutt/string.o

Expand Down
2 changes: 2 additions & 0 deletions core/mailbox.h
Expand Up @@ -178,6 +178,8 @@ enum NotifyMailbox
NT_MAILBOX_RESORT, ///< Email list needs resorting
NT_MAILBOX_UPDATE, ///< Update internal tables
NT_MAILBOX_UNTAG, ///< Clear the 'last-tagged' pointer

NT_MAILBOX_NEW_MAIL, ///< New messages have been added
};

/**
Expand Down
11 changes: 11 additions & 0 deletions docs/config.c
Expand Up @@ -986,6 +986,17 @@
** or when you save it to another folder.
*/

{ "devel_new_mail_command", DT_COMMAND, 0 },
/*
** .pp
** If \fIset\fP, NeoMutt will call this command after a new message is received.
** The following \fCprintf(3)\fP-like sequences are supported:
** .dl
** .dt %n .dd Mailbox name
** .dt %f .dd Mailbox folder path
** .de
*/

{ "digest_collapse", DT_BOOL, true },
/*
** .pp
Expand Down
1 change: 1 addition & 0 deletions main.c
Expand Up @@ -944,6 +944,7 @@ main
notify_observer_add(NeoMutt->notify, NT_CONFIG, main_hist_observer, NULL);
notify_observer_add(NeoMutt->notify, NT_CONFIG, main_log_observer, NULL);
notify_observer_add(NeoMutt->notify, NT_CONFIG, main_config_observer, NULL);
notify_observer_add(NeoMutt->notify, NT_MAILBOX, new_mail_observer, NULL);

if (sendflags & SEND_POSTPONED)
{
Expand Down
2 changes: 2 additions & 0 deletions mutt/lib.h
Expand Up @@ -46,6 +46,7 @@
* | mutt/mbyte.c | @subpage mutt_mbyte |
* | mutt/md5.c | @subpage mutt_md5 |
* | mutt/memory.c | @subpage mutt_memory |
* | mutt/new_mail.c | @subpage mutt_new_mail |
* | mutt/notify.c | @subpage mutt_notify |
* | mutt/path.c | @subpage mutt_path |
* | mutt/pool.c | @subpage mutt_pool |
Expand Down Expand Up @@ -86,6 +87,7 @@
#include "md5.h"
#include "memory.h"
#include "message.h"
#include "new_mail.h"
#include "notify.h"
#include "notify_type.h"
#include "observer.h"
Expand Down
99 changes: 99 additions & 0 deletions mutt/new_mail.c
@@ -0,0 +1,99 @@
/**
* @file
* New mail notification observer implementation.
*
* @authors
* Copyright (C) 2022 Michal Siedlaczek <michal@siedlaczek.me>
*
* @copyright
* This program 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 2 of the License, or (at your option) any later
* version.
*
* This program 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
* this program. If not, see <http://www.gnu.org/licenses/>.
*/

/**
* @page new_mail Notify about new incoming mail
*
* Use an external command to send a system notification when new mail arrives.
*/

#include "config/lib.h"
#include "new_mail.h"
#include "mutt_mailbox.h"
#include "muttlib.h"
#include "protos.h"

/**
* new_mail_format_str - Format a string for the new mail notification.
* @param[out] buf Buffer in which to save string
* @param[in] buflen Buffer length
* @param[in] col Starting column
* @param[in] cols Number of screen columns
* @param[in] src Printf-like format string
* @param[in] prec Field precision, e.g. "-3.4"
* ############# TODO #############
*
* | Expando | Description
* | :------ | :-------------------------------------------------------
* | \%n | Folder name
* | \%f | Folder path
*/
const char *new_mail_format_str(char *buf, size_t buflen, size_t col, int cols,
char op, const char *src, const char *prec,
const char *if_str, const char *else_str,
intptr_t data, MuttFormatFlags flags)
{
struct Mailbox *mailbox = (struct Mailbox *) data;

switch (op)
{
case 'n':
snprintf(buf, buflen, "%s", NONULL(mailbox->name));
break;
case 'f':
snprintf(buf, buflen, "%s", NONULL(mailbox_path(mailbox)));
break;
}
return src;
}

int handle_new_mail_event(const char *cmd, struct NotifyCallback *nc, Execute *execute)
{
if (nc->event_subtype != NT_MAILBOX_NEW_MAIL)
return 0;

struct EventMailbox *ev_m = nc->event_data;
if (!ev_m || !ev_m->mailbox)
return 0;

struct Mailbox *mailbox = ev_m->mailbox;
char expanded_cmd[1024];
mutt_expando_format(expanded_cmd, 1024, 0, 1024, cmd, new_mail_format_str,
(intptr_t) mailbox, MUTT_FORMAT_NO_FLAGS);
execute(expanded_cmd);
return 0;
}

int execute_cmd(const char *cmd)
{
if (mutt_system(cmd) != 0)
mutt_error(_("Error running \"%s\""), cmd);
return 0;
}

int new_mail_observer(struct NotifyCallback *nc)
{
const char *c_devel_new_mail_command = cs_subset_string(NeoMutt->sub, "devel_new_mail_command");
if (!c_devel_new_mail_command)
return 0;
return handle_new_mail_event(c_devel_new_mail_command, nc, execute_cmd);
}
40 changes: 40 additions & 0 deletions mutt/new_mail.h
@@ -0,0 +1,40 @@
/**
* @file
* New mail notification observer.
*
* @authors
* Copyright (C) 2022 Michal Siedlaczek <michal@siedlaczek.me>
*
* @copyright
* This program 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 2 of the License, or (at your option) any later
* version.
*
* This program 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
* this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef MUTT_NEW_MAIL_H
#define MUTT_NEW_MAIL_H

#include <stdint.h>
#include <stdio.h>
#include "mutt/notify.h"
#include "format_flags.h"

typedef int(Execute)(const char *cmd);

const char *new_mail_format_str(char *buf, size_t buflen, size_t col, int cols,
char op, const char *src, const char *prec,
const char *if_str, const char *else_str,
intptr_t data, MuttFormatFlags flags);
int handle_new_mail_event(const char *cmd, struct NotifyCallback *nc, Execute *execute);
int new_mail_observer(struct NotifyCallback *nc);

#endif
3 changes: 3 additions & 0 deletions mutt_config.c
Expand Up @@ -407,6 +407,9 @@ static struct ConfigDef MainVars[] = {
{ "new_mail_command", DT_STRING|DT_COMMAND, 0, 0, NULL,
"External command to run when new mail arrives"
},
{ "devel_new_mail_command", DT_STRING|DT_COMMAND, 0, 0, NULL,
"External command to run when new mail arrives (in development, clashes with new_mail_command)"
},
{ "pager", DT_STRING|DT_COMMAND, IP "builtin", 0, NULL,
"External command for viewing messages, or 'builtin' to use NeoMutt's"
},
Expand Down
6 changes: 6 additions & 0 deletions mx.c
Expand Up @@ -1808,6 +1808,12 @@ enum MxStatus mx_mbox_check_stats(struct Mailbox *m, uint8_t flags)
struct EventMailbox ev_m = { m };
notify_send(m->notify, NT_MAILBOX, NT_MAILBOX_CHANGE, &ev_m);
}
if (rc == MX_STATUS_NEW_MAIL && !m->notified)
{
struct EventMailbox ev_m = { m };
notify_send(m->notify, NT_MAILBOX, NT_MAILBOX_NEW_MAIL, &ev_m);
m->notified = true;
}

return rc;
}
Expand Down
4 changes: 4 additions & 0 deletions test/Makefile.autosetup
Expand Up @@ -355,6 +355,9 @@ NEOMUTT_OBJS = test/neo/neomutt_account_add.o \
test/neo/neomutt_mailboxlist_get_all.o \
test/neo/neomutt_new.o

NEW_MAIL_OBJS = test/new_mail/new_mail_observer.o \
test/new_mail/new_mail_format_str.o

NOTIFY_OBJS = test/notify/notify_free.o \
test/notify/notify_new.o \
test/notify/notify_observer_add.o \
Expand Down Expand Up @@ -610,6 +613,7 @@ TEST_OBJS = test/main.o test/common.o \
$(MD5_OBJS) \
$(MEMORY_OBJS) \
$(NEOMUTT_OBJS) \
$(NEW_MAIL_OBJS) \
$(NOTIFY_OBJS) \
$(NOTMUCH_OBJS) \
$(PARAMETER_OBJS) \
Expand Down
4 changes: 4 additions & 0 deletions test/main.c
Expand Up @@ -396,6 +396,10 @@
NEOMUTT_TEST_ITEM(test_neomutt_mailboxlist_get_all) \
NEOMUTT_TEST_ITEM(test_neomutt_new) \
\
/* new_mail */ \
NEOMUTT_TEST_ITEM(test_new_mail_observer) \
NEOMUTT_TEST_ITEM(test_new_mail_format_str) \
\
/* notify */ \
NEOMUTT_TEST_ITEM(test_notify_free) \
NEOMUTT_TEST_ITEM(test_notify_new) \
Expand Down
55 changes: 55 additions & 0 deletions test/new_mail/new_mail_format_str.c
@@ -0,0 +1,55 @@
/**
* @file
* Test code for new_mail_format_str()
*
* @authors
* Copyright (C) 2022 Michal Siedlaczek <michal@siedlaczek.me>
*
* @copyright
* This program 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 2 of the License, or (at your option) any later
* version.
*
* This program 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
* this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "mutt/string2.h"
#define TEST_NO_MAIN
#include "config.h"
#include "acutest.h"
#include "mutt/lib.h"
#include "core/lib.h"

void test_new_mail_format_str(void)
{
// const char *new_mail_format_str(char *buf, size_t buflen, size_t col, int cols,
// char op, const char *src, const char *prec,
// const char *if_str, const char *else_str,
// intptr_t data, MuttFormatFlags flags);

char buf[64];
size_t col = 0;
int cols = 64;
struct Mailbox *mailbox = mailbox_new();
mailbox->name = mutt_str_dup("MailBox");
mailbox->pathbuf = mutt_buffer_make(16);
mutt_buffer_strcpy(&mailbox->pathbuf, "/path");
intptr_t data = (intptr_t) mailbox;

new_mail_format_str((char *) buf, 64, col, cols, 'n', NULL, NULL, NULL, NULL, data, 0);
TEST_CHECK(mutt_str_equal(buf, "MailBox"));
TEST_MSG("Check failed: %s != MailBox", buf);

new_mail_format_str((char *) buf, 64, col, cols, 'f', NULL, NULL, NULL, NULL, data, 0);
TEST_CHECK(mutt_str_equal(buf, "/path"));
TEST_MSG("Check failed: %s != /path", buf);

mailbox_free(&mailbox);
}
65 changes: 65 additions & 0 deletions test/new_mail/new_mail_observer.c
@@ -0,0 +1,65 @@
/**
* @file
* Test code for new_mail_observer()
*
* @authors
* Copyright (C) 2022 Michal Siedlaczek <michal@siedlaczek.me>
*
* @copyright
* This program 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 2 of the License, or (at your option) any later
* version.
*
* This program 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
* this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "mutt/notify.h"
#include "mutt/observer.h"
#include "mutt/string2.h"
#define TEST_NO_MAIN
#include "config.h"
#include "acutest.h"
#include "mutt/lib.h"
#include "core/lib.h"

static char *message = NULL;

int dummy_execute_cmd(const char *cmd)
{
message = mutt_str_dup(cmd);
return 0;
}

int dummy_new_mail_observer(struct NotifyCallback *nc)
{
return handle_new_mail_event("New messages", nc, dummy_execute_cmd);
}

void test_new_mail_observer(void)
{
struct Notify *notify = notify_new();
notify_observer_add(notify, NT_MAILBOX, dummy_new_mail_observer, NULL);
struct Mailbox *mailbox = mailbox_new();
mailbox->name = mutt_str_dup("Mailbox");
struct EventMailbox event;
event.mailbox = mailbox;

notify_send(notify, NT_MAILBOX, NT_MAILBOX_NEW_MAIL, NULL);
TEST_CHECK(message == NULL);

notify_send(notify, NT_MAILBOX, NT_MAILBOX_NEW_MAIL, &event);
TEST_CHECK(message != NULL);
TEST_CHECK(mutt_str_equal(message, "New messages"));
TEST_MSG("Check failed: \"%s\" != \"New messages\"", message);

notify_free(&notify);
mailbox_free(&mailbox);
FREE(&message);
}

0 comments on commit 1976ce5

Please sign in to comment.