/*
* Incoming Mail Checker
*
- * (c) 2005--2008 Martin Mares <mj@ucw.cz>
+ * (c) 2005--2010 Martin Mares <mj@ucw.cz>
*/
#define _GNU_SOURCE
#include <unistd.h>
#include <pwd.h>
#include <time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
#ifdef CONFIG_WIDE_CURSES
#include <ncursesw/ncurses.h>
static int check_interval = 30;
static int force_refresh;
static int allow_bells = 1;
+static int allow_osd = 1;
static int minimum_priority;
static time_t last_scan_time;
static char *run_cmd = "mutt -f %s";
int sender_personal;
int sender_mbox;
int hotkey;
+ int led;
+ int osd;
};
struct option_node {
static struct mbox **mbox_array;
static int num_mboxes, mbox_array_size;
+struct osd_opt_node {
+ cnode n;
+ char *val;
+ char key[1];
+};
+
+static clist osd_opts;
+
static void redraw_line(int i);
static void rethink_display(void);
o->sender_personal = -1;
o->sender_mbox = -1;
o->hotkey = -1;
+ o->led = -1;
+ o->osd = -1;
}
static void
MERGE(sender_personal);
MERGE(sender_mbox);
MERGE(hotkey);
+ MERGE(led);
+ MERGE(osd);
}
}
+static void
+add_osd_opt(char *arg)
+{
+ struct osd_opt_node *n = xmalloc(sizeof(*n) + strlen(arg));
+ strcpy(n->key, arg);
+ n->val = strchr(n->key, '=');
+ if (!n->val)
+ die("Malformed OSD option");
+ *n->val++ = 0;
+ clist_add_tail(&osd_opts, &n->n);
+}
+
static char *
mbox_name(char *path)
{
{
char buf[1024], sender[1024], subject[1024];
int c;
+ int compressed = 0;
const char from[] = "\nFrom ";
if (!st->st_size)
b->total = b->new = b->flagged = -1;
return;
}
+
+ char signature[2];
+ c = read(mb_fd, signature, 2);
+ lseek(mb_fd, 0, SEEK_SET);
+
+ if (c == 2 && !memcmp(signature, "\037\213", 2)) //gzip
+ {
+ debug("[decompressing] ");
+ int fds[2];
+ if (pipe(fds))
+ die("pipe failed: %m");
+ int pid = fork();
+ if (pid < 0)
+ die("fork failed: %m");
+ if (!pid)
+ {
+ if (dup2(mb_fd, 0) < 0 || dup2(fds[1], 1) < 0)
+ die("dup2 failed: %m");
+ close(fds[0]);
+ close(fds[1]);
+ close(mb_fd);
+ execlp("gzip", "gzip", "-cd", NULL);
+ die("Cannot execute gzip: %m");
+ }
+ close(fds[1]);
+ close(mb_fd);
+ mb_fd = fds[0];
+ compressed = 1;
+ }
mb_reset(0);
int incremental = 0;
- if (b->last_size && b->last_pos && st->st_size > b->last_size && !b->force_refresh)
+ if (b->last_size && b->last_pos && st->st_size > b->last_size && !b->force_refresh && !compressed)
{
mb_seek(b->last_pos);
if (mb_check(from, 6))
done:
close(mb_fd);
+ if (compressed)
+ {
+ int status;
+ if (wait(&status) < 0 || !WIFEXITED(status) || WEXITSTATUS(status))
+ b->total = b->new = b->flagged = -1;
+ }
}
static void
rethink_display();
}
+#ifdef CONFIG_X11
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+static Display *x11_dpy;
+static unsigned leds_care, leds_have, leds_want;
+static unsigned osd_care, osd_have, osd_want;
+static Atom osd_pty;
+
+static void
+x11_init(void)
+{
+ leds_care = (global_options.led >= 0 ? (1 << global_options.led) : 0);
+ osd_care = (global_options.osd >= 0);
+ CLIST_FOR_EACH(struct option_node *, o, options)
+ {
+ if (o->o.led > 0)
+ leds_care |= (1 << o->o.led);
+ if (o->o.osd > 0)
+ osd_care = 1;
+ }
+
+ if (!leds_care && !osd_care)
+ {
+ debug("X11: No mailbox wants LEDs or OSD\n");
+ return;
+ }
+ if (!getenv("DISPLAY"))
+ {
+ debug("X11: Do not have X display\n");
+ return;
+ }
+ if (!(x11_dpy = XOpenDisplay(NULL)))
+ die("Cannot open X display, although the DISPLAY variable is set");
+
+ if (osd_care)
+ {
+ osd_pty = XInternAtom(x11_dpy, "OSD_QUEUE", False);
+ if (!osd_pty)
+ die("Cannot intern OSD_QUEUE atom");
+
+ // If OSD options contain no message, add one
+ int seen_msg = 0;
+ CLIST_FOR_EACH(struct osd_opt_node *, n, osd_opts)
+ if (!n->key[0])
+ seen_msg = 1;
+ if (!seen_msg)
+ add_osd_opt("=You have new mail");
+ }
+
+ leds_have = ~0U;
+ debug("X11: Initialized\n");
+}
+
+static void
+sync_leds(void)
+{
+ if (leds_want == leds_have)
+ return;
+
+ debug("LEDS: have %02x, want %02x, care %02x\n", leds_have, leds_want, leds_care);
+ for (int i=1; i<10; i++)
+ if (leds_care & (leds_have ^ leds_want) & (1 << i))
+ {
+ XKeyboardControl cc;
+ cc.led = i;
+ cc.led_mode = (leds_want & (1 << i)) ? LedModeOn : LedModeOff;
+ XChangeKeyboardControl(x11_dpy, KBLed | KBLedMode, &cc);
+ }
+ XFlush(x11_dpy);
+ leds_have = leds_want;
+}
+
+static void
+sync_osd(void)
+{
+ if (!osd_want || !allow_osd)
+ {
+ osd_have = 0;
+ return;
+ }
+ if (osd_have)
+ return;
+ debug("OSD: Displaying\n");
+
+ char msg[1024];
+ unsigned pos = 0;
+ CLIST_FOR_EACH(struct osd_opt_node *, n, osd_opts)
+ {
+ pos += snprintf(msg+pos, sizeof(msg)-pos-1, "%s:%s\n", n->key, n->val);
+ if (pos > sizeof(msg)-1)
+ {
+ pos = sprintf(msg, "OSD message too long!\n");
+ break;
+ }
+ }
+ msg[pos++] = '\n';
+
+ XChangeProperty(x11_dpy, DefaultRootWindow(x11_dpy), osd_pty, XA_STRING, 8, PropModeAppend, (unsigned char *) msg, pos);
+ XFlush(x11_dpy);
+}
+
+static void
+rethink_leds(void)
+{
+ if (!x11_dpy)
+ return;
+
+ leds_want = 0;
+ osd_want = 0;
+ CLIST_FOR_EACH(struct mbox *, b, mboxes)
+ {
+ if (b->o.led > 0 && b->new)
+ leds_want |= (1 << b->o.led);
+ if (b->o.osd > 0 && b->new)
+ osd_want = 1;
+ }
+ sync_leds();
+ sync_osd();
+}
+
+static void
+x11_cleanup(void)
+{
+ if (!x11_dpy)
+ return;
+
+ leds_want = 0;
+ sync_leds();
+}
+
+#else
+
+static void x11_init(void) { }
+static void rethink_leds(void) { }
+static void x11_cleanup(void) { }
+
+#endif
+
static int cursor_at, cursor_max;
enum {
redraw_all();
refresh();
}
+ rethink_leds();
if (beeeep && allow_bells)
beep();
}
-o <pattern>=<opts>\tSet mailbox options\n\
-o <opts>\t\tSet default options for all mailboxes\n\
-p <pri>\t\tSet minimum priority to show\n\
+-s <key>=<val>\t\tSet on-screen display options (consult OSDD docs)\n\
\n\
Mailbox options (set with `-o', use upper case to negate):\n\
0-9\t\t\tSet mailbox priority (0=default)\n\
b\t\t\tBeep when a message arrives\n\
+d\t\t\tSend an on-screen-display message (requires OSDD)\n\
e\t\t\tHide from display if empty\n\
f\t\t\tShow flagged messages if there are no new ones\n\
h\t\t\tHide from display\n\
+l<led>\t\t\tLight a keyboard led (1-9) if running on X display\n\
m\t\t\tShow mailbox name of the sender\n\
p\t\t\tShow personal info (full name) of the sender\n\
s\t\t\tShow message snippets\n\
o->priority = x - '0';
else if (x == '!' && *c)
o->hotkey = *c++;
+ else if (x == 'l' && *c >= '1' && *c <= '9')
+ o->led = *c++ - '0';
else
{
int value = !!islower(x);
case 'b':
o->beep = value;
break;
+ case 'd':
+ o->osd = value;
+ break;
case 'e':
o->hide_if_empty = value;
break;
clist_init(&mboxes);
clist_init(&options);
clist_init(&patterns);
+ clist_init(&osd_opts);
int c;
- while ((c = getopt(argc, argv, "c:dim:o:p:")) >= 0)
+ while ((c = getopt(argc, argv, "c:dim:o:p:s:")) >= 0)
switch (c)
{
case 'c':
case 'p':
minimum_priority = atol(optarg);
break;
+ case 's':
+ add_osd_opt(optarg);
+ break;
default:
usage();
}
charset_init();
term_init();
+ x11_init();
scan_and_redraw();
next_active(0, 1);
allow_bells = 0;
print_status("Bells and whistles are now disabled. Pssst!");
break;
+ case 'd':
+ allow_osd = 1;
+ print_status("On-screen display is now enabled.");
+ break;
+ case 'D':
+ allow_osd = 0;
+ print_status("On-screen display is now disabled. Watch your step.");
+ break;
default:
if (ch >= '0' && ch <= '9')
{
}
}
+ x11_cleanup();
term_cleanup();
return 0;
}