Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System notification via event notification system #3429

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 16 additions & 1 deletion Makefile.autosetup
Expand Up @@ -623,6 +623,21 @@ $(LIBNCRYPT): $(PWD)/ncrypt $(LIBNCRYPTOBJS)
$(PWD)/ncrypt:
$(MKDIR_P) $(PWD)/ncrypt

###############################################################################
# libnewmail
@if USE_DEVEL_NEW_MAIL
LIBNEWMAIL= libnewmail.a
LIBNEWMAILOBJS= newmail/new_mail.o
CLEANFILES+= $(LIBNEWMAIL) $(LIBNEWMAILOBJS)
ALLOBJS+= $(LIBNEWMAILOBJS)

$(LIBNEWMAIL): $(PWD)/newmail $(LIBNEWMAILOBJS)
$(AR) cr $@ $(LIBNEWMAILOBJS)
$(RANLIB) $@
$(PWD)/newmail:
$(MKDIR_P) $(PWD)/newmail
@endif

###############################################################################
# libnntp
LIBNNTP= libnntp.a
Expand Down Expand Up @@ -853,7 +868,7 @@ MUTTLIBS+= $(LIBINDEX) $(LIBPAGER) $(LIBAUTOCRYPT) $(LIBPOP) \
$(LIBNCRYPT) $(LIBIMAP) $(LIBCONN) $(LIBHCACHE) \
$(LIBCOMPRESS) $(LIBSIDEBAR) $(LIBBCACHE) $(LIBHISTORY) \
$(LIBCORE) $(LIBPARSE) $(LIBCONFIG) $(LIBEMAIL) $(LIBADDRESS) \
$(LIBDEBUG) $(LIBMUTT)
$(LIBDEBUG) $(LIBNEWMAIL) $(LIBMUTT)

# neomutt
$(NEOMUTT): $(GENERATED) $(NEOMUTTOBJS) $(MUTTLIBS)
Expand Down
8 changes: 7 additions & 1 deletion auto.def
Expand Up @@ -125,6 +125,7 @@ set valid_options {
debug-notify=0 => "DEBUG: Enable Notifications dump"
debug-queue=0 => "DEBUG: Enable TAILQ debugging"
debug-window=0 => "DEBUG: Enable windows dump"
devel-new-mail=0 => "Enable new work-in-progress system notifications"
}

set deprecated_options {
Expand Down Expand Up @@ -168,7 +169,7 @@ if {1} {
# Keep sorted, please.
foreach opt {
asan autocrypt bdb compile-commands coverage debug-backtrace debug-color debug-email
debug-graphviz debug-notify debug-queue debug-window doc
debug-graphviz devel-new-mail debug-notify debug-queue debug-window doc
everything fmemopen full-doc fuzzing gdbm gnutls gpgme gsasl gss homespool
idn2 include-path-in-cflags inotify kyotocabinet lmdb locales-fix lua lz4
mixmaster nls notmuch pcre2 pgp qdbm rocksdb sasl smime sqlite ssl
Expand Down Expand Up @@ -1058,6 +1059,11 @@ if {[get-define want-debug-window]} {
define USE_DEBUG_WINDOW 1
}

# WIP notifications implementation
if {[get-define want-devel-new-mail]} {
define USE_DEVEL_NEW_MAIL 1
}

###############################################################################
# Address Sanitizer
if {[get-define want-asan]} {
Expand Down
4 changes: 4 additions & 0 deletions core/mailbox.c
Expand Up @@ -76,6 +76,10 @@ struct Mailbox *mailbox_new(void)
m->emails = mutt_mem_calloc(m->email_max, sizeof(struct Email *));
m->v2r = mutt_mem_calloc(m->email_max, sizeof(int));
m->gen = mailbox_gen();
#ifdef USE_DEVEL_NEW_MAIL
m->last_notified.tv_sec = 0;
m->last_notified.tv_nsec = 0;
#endif

return m;
}
Expand Down
10 changes: 9 additions & 1 deletion core/mailbox.h
Expand Up @@ -92,6 +92,9 @@ struct Mailbox
int msg_new; ///< Number of new messages
int msg_deleted; ///< Number of deleted messages
int msg_tagged; ///< How many messages are tagged?
#ifdef USE_DEVEL_NEW_MAIL
int msg_unnotified; ///< Number of messages we have not notified about
#endif

struct Email **emails; ///< Array of Emails
int email_max; ///< Number of pointers in emails
Expand All @@ -103,6 +106,9 @@ struct Mailbox
bool newly_created; ///< Mbox or mmdf just popped into existence
struct timespec mtime; ///< Time Mailbox was last changed
struct timespec last_visited; ///< Time of last exit from this mailbox
#ifdef USE_DEVEL_NEW_MAIL
struct timespec last_notified; ///< Time when the user was last notified about new messages.
#endif

const struct MxOps *mx_ops; ///< MXAPI callback functions

Expand Down Expand Up @@ -176,14 +182,16 @@ 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
};

/**
* struct EventMailbox - An Event that happened to a Mailbox
*/
struct EventMailbox
{
struct Mailbox *mailbox; ///< The Mailbox this Event relates to
struct Mailbox *mailbox; ///< The Mailbox this Event relates to
};

void mailbox_changed (struct Mailbox *m, enum NotifyMailbox action);
Expand Down
13 changes: 13 additions & 0 deletions docs/config.c
Expand Up @@ -1010,6 +1010,19 @@
** or when you save it to another folder.
*/

#ifdef USE_DEVEL_NEW_MAIL
{ "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
*/
#endif

elshize marked this conversation as resolved.
Show resolved Hide resolved
{ "digest_collapse", DT_BOOL, true },
/*
** .pp
Expand Down
36 changes: 36 additions & 0 deletions maildir/maildir.c
Expand Up @@ -46,10 +46,12 @@
#include <utime.h>
#include "private.h"
#include "mutt/lib.h"
#include "mutt/notify.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "lib.h"
#include "newmail/lib.h"
#include "progress/lib.h"
#include "copy.h"
#include "edata.h"
Expand Down Expand Up @@ -95,6 +97,10 @@ static void maildir_check_dir(struct Mailbox *m, const char *dir_name,
struct Buffer *msgpath = buf_pool_get();
buf_printf(path, "%s/%s", mailbox_path(m), dir_name);

#ifdef USE_DEVEL_NEW_MAIL
bool check_unnotified = check_new;
#endif

/* when $mail_check_recent is set, if the new/ directory hasn't been modified since
* the user last exited the m, then we know there is no recent mail. */
const bool c_mail_check_recent = cs_subset_bool(NeoMutt->sub, "mail_check_recent");
Expand Down Expand Up @@ -136,6 +142,22 @@ static void maildir_check_dir(struct Mailbox *m, const char *dir_name,
{
if (check_stats)
m->msg_unread++;

#ifdef USE_DEVEL_NEW_MAIL
if (check_unnotified)
{
struct timespec created;
buf_printf(msgpath, "%s/%s", buf_string(path), de->d_name);
stat(buf_string(msgpath), &st);
mutt_file_get_stat_timespec(&created, &st, MUTT_STAT_CTIME);
if (mutt_file_timespec_compare(&m->last_notified, &created) < 0)
{
mutt_debug(LL_DEBUG1, "Unnotified %s/%s\n", buf_string(path), de->d_name);
m->msg_unnotified++;
}
}
#endif

if (check_new)
{
if (c_mail_check_recent)
Expand Down Expand Up @@ -1361,6 +1383,9 @@ static enum MxStatus maildir_mbox_check_stats(struct Mailbox *m, uint8_t flags)
m->msg_count = 0;
m->msg_unread = 0;
m->msg_flagged = 0;
#ifdef USE_DEVEL_NEW_MAIL
m->msg_unnotified = 0;
#endif
}

maildir_check_dir(m, "new", check_new, check_stats);
Expand All @@ -1370,6 +1395,17 @@ static enum MxStatus maildir_mbox_check_stats(struct Mailbox *m, uint8_t flags)
if (check_new || check_stats)
maildir_check_dir(m, "cur", check_new, check_stats);

#ifdef USE_DEVEL_NEW_MAIL
if (check_stats && m->msg_unnotified > 0)
{
mutt_debug(LL_DEBUG1, "Unnotified = %d\n", m->msg_unnotified);
struct EventMailbox ev_m = { m };
notify_send(m->notify, NT_MAILBOX, NT_MAILBOX_NEW_MAIL, &ev_m);
clock_gettime(CLOCK_REALTIME, &m->last_notified);
mutt_debug(LL_DEBUG2, "resetting time\n");
}
#endif

return m->msg_new ? MX_STATUS_NEW_MAIL : MX_STATUS_OK;
}

Expand Down
5 changes: 5 additions & 0 deletions main.c
Expand Up @@ -159,6 +159,7 @@
#include "index/lib.h"
#include "menu/lib.h"
#include "ncrypt/lib.h"
#include "newmail/lib.h"
#include "postpone/lib.h"
#include "question/lib.h"
#include "send/lib.h"
Expand Down Expand Up @@ -921,6 +922,10 @@ main
notify_observer_add(NeoMutt->notify, NT_CONFIG, main_log_observer, NULL);
notify_observer_add(NeoMutt->notify, NT_CONFIG, main_config_observer, NULL);

#ifdef USE_DEVEL_NEW_MAIL
notify_observer_add(NeoMutt->notify, NT_MAILBOX, new_mail_observer, NULL);
#endif

elshize marked this conversation as resolved.
Show resolved Hide resolved
if (sendflags & SEND_POSTPONED)
{
if (!OptNoCurses)
Expand Down
5 changes: 5 additions & 0 deletions mutt_config.c
Expand Up @@ -371,6 +371,11 @@ static struct ConfigDef MainVars[] = {
{ "new_mail_command", DT_STRING|DT_COMMAND, 0, 0, NULL,
"External command to run when new mail arrives"
},
#ifdef USE_DEVEL_NEW_MAIL
{ "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)"
},
elshize marked this conversation as resolved.
Show resolved Hide resolved
#endif
{ "pipe_decode", DT_BOOL, false, 0, NULL,
"Decode the message when piping it"
},
Expand Down
2 changes: 1 addition & 1 deletion mx.c
Expand Up @@ -1798,7 +1798,7 @@ int mx_ac_remove(struct Mailbox *m, bool keep_account)
/**
* mx_mbox_check_stats - Check the statistics for a mailbox - Wrapper for MxOps::mbox_check_stats()
*
* @note Emits: #NT_MAILBOX_CHANGE
* @note Emits: #NT_MAILBOX_CHANGE and #NT_MAILBOX_NEW_MAIL
*/
enum MxStatus mx_mbox_check_stats(struct Mailbox *m, uint8_t flags)
{
Expand Down
42 changes: 42 additions & 0 deletions newmail/lib.h
@@ -0,0 +1,42 @@
/**
* @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/lib.h"
#include "format_flags.h"

struct NotifyCallback;

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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only new_mail_observer() is used from the outside, so we can drop everything else.
Including the #includes. They could be replaced with a forward declaration:

struct NotifyCallback;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's actually not entirely true. I am using those functions in tests. I split them to be able to write some unit tests. If I merge all of them together, it will be very difficult to write any tests. I suppose you can hack it and set config to use some specific command that writes to a file and we then read it to verify... Would you prefer I did that?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! I always forget the tests.
Leave them as they are and I'll re-educate myself on their usage :-)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure; if you see a better way to test these, let me know.


#endif