diff --git a/src/core.c b/src/core.c index bd8b82b..8be2247 100644 --- a/src/core.c +++ b/src/core.c @@ -36,6 +36,7 @@ static int ifcore_rename(lua_State *lua); static int ifcore_subscribe(lua_State *lua); static int ifcore_unsubscribe(lua_State *lua); static int ifcore_idle(lua_State *lua); +static int ifcore_notify(lua_State *lua); /* Lua imapfilter core library functions. */ @@ -77,6 +78,7 @@ static const luaL_Reg ifcorelib[] = { { "store", ifcore_store }, { "copy", ifcore_copy }, { "idle", ifcore_idle }, + { "notify", ifcore_notify }, { NULL, NULL } }; @@ -992,11 +994,42 @@ ifcore_idle(lua_State *lua) luaL_error(lua, "wrong number of arguments"); luaL_checktype(lua, 1, LUA_TLIGHTUSERDATA); - if (get_option_boolean("reenter")) - r = request_idle((session *)(lua_topointer(lua, 1)), - &event); - else - r = request_idle((session *)(lua_topointer(lua, 1)), &event); + r = request_idle((session *)(lua_topointer(lua, 1)), &event); + + lua_pop(lua, 1); + + if (r < 0) + return 0; + + lua_pushboolean(lua, (r == STATUS_OK)); + + if (!event) + return 1; + + lua_pushstring(lua, event); + + xfree(event); + + return 2; +} + + +/* + * Core function to go to idle state. + */ +static int +ifcore_notify(lua_State *lua) +{ + int r; + char *event; + + event = NULL; + + if (lua_gettop(lua) != 1) + luaL_error(lua, "wrong number of arguments"); + luaL_checktype(lua, 1, LUA_TLIGHTUSERDATA); + + r = request_notify((session *)(lua_topointer(lua, 1)), &event); lua_pop(lua, 1); diff --git a/src/imapfilter.h b/src/imapfilter.h index 406b964..e5d22aa 100644 --- a/src/imapfilter.h +++ b/src/imapfilter.h @@ -35,6 +35,7 @@ #define CAPABILITY_XOAUTH2 0x10 #define CAPABILITY_ENABLE 0x20 #define CAPABILITY_UTF8 0x40 +#define CAPABILITY_NOTIFY 0x80 /* Status responses and response codes. */ #define STATUS_BYE -2 @@ -184,6 +185,7 @@ int request_rename(session *ssn, const char *oldmbox, const char *newmbox); int request_subscribe(session *ssn, const char *mbox); int request_unsubscribe(session *ssn, const char *mbox); int request_idle(session *ssn, char **event); +int request_notify(session *ssn, char **event); /* response.c */ int response_generic(session *ssn, int tag); @@ -207,6 +209,7 @@ int response_fetchsize(session *ssn, int tag, char **size); int response_fetchstructure(session *ssn, int tag, char **structure); int response_fetchbody(session *ssn, int tag, char **body, size_t *len); int response_idle(session *ssn, int tag, char **event); +int response_notify(session *ssn, char **event); /* signal.c */ void catch_signals(void); diff --git a/src/mailbox.lua b/src/mailbox.lua index c85f7bd..b88a618 100644 --- a/src/mailbox.lua +++ b/src/mailbox.lua @@ -1080,6 +1080,24 @@ function Mailbox.enter_idle(self) end +function Mailbox.await_notification(self) + if self._cached_select(self) ~= true then return false end + + self._check_connection(self) + local r, event = ifcore.notify(self._account._account.session) + self._check_result(self, 'notify', r) + if r == false then return false end + + if options.close == true then self._cached_close(self) end + + if type(event) == 'string' then + return true, string.upper(event) + else + return true + end +end + + Mailbox.open = _cached_select Mailbox.close = _cached_close diff --git a/src/request.c b/src/request.c index 6b57ba0..de538fe 100644 --- a/src/request.c +++ b/src/request.c @@ -776,3 +776,37 @@ request_idle(session *ssn, char **event) return r; } + + +int +request_notify(session *ssn, char **event) +{ + int t, r, ri; + + debug("request_notify: capa:%x, status:%d\n\n", + ssn->capabilities & CAPABILITY_NOTIFY, + ssn->notify_active + ); + + if (!(ssn->capabilities & CAPABILITY_NOTIFY)) + return STATUS_BAD; + + if (!ssn->notify_active) { + TRY(t = send_request(ssn, "NOTIFY SET (Subscribed (MessageNew MessageExpunge))")); + TRY(r = response_generic(ssn, t)); + if (r != STATUS_OK) { + return r; + } + ssn->notify_active = 1; + } + + do { + ri = 0; + + TRY(ri = response_notify(ssn, event)); + if (ri == STATUS_TIMEOUT) + request_noop(ssn); + } while (ri == STATUS_TIMEOUT); + + return r; +} diff --git a/src/response.c b/src/response.c index 29122bd..ede494b 100644 --- a/src/response.c +++ b/src/response.c @@ -354,6 +354,8 @@ response_capability(session *ssn, int tag) ssn->capabilities |= CAPABILITY_ENABLE; if (xstrcasestr(s, "UTF8=ACCEPT")) ssn->capabilities |= CAPABILITY_UTF8; + if (xstrcasestr(s, "NOTIFY")) + ssn->capabilities |= CAPABILITY_NOTIFY; xfree(s); } @@ -892,3 +894,53 @@ response_idle(session *ssn, int tag, char **event) return STATUS_UNTAGGED; } + + +/* + * Process the data that server sent due to IMAP NOTIFY client request. + */ +int +response_notify(session *ssn, char **event) +{ + regexp *re; + ssize_t n; + int eintr = 0; + + re = &responses[RESPONSE_UNTAGGED]; + + for (;;) { + buffer_reset(&ibuf); + + do { + buffer_check(&ibuf, ibuf.len + INPUT_BUF); + n = receive_response(ssn, ibuf.data + ibuf.len, + get_option_number("keepalive") * 60, 0, &eintr); + if (n < 0) { + if (eintr) + return STATUS_INTERRUPT; + else + return STATUS_ERROR; + } + if (n == 0) + return STATUS_TIMEOUT; + ibuf.len += n; + + if (check_bye(ibuf.data)) + return handle_bye(ssn); + + } while (regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0)); + + verbose("S (%d): %s", ssn->socket, ibuf.data); + + if (get_option_boolean("wakeonany")) + break; + if (!strncasecmp(ibuf.data + re->pmatch[1].rm_so, + "STATUS", strlen("STATUS"))) + break; + } + + *event = xstrndup(ibuf.data + re->pmatch[1].rm_so, + re->pmatch[1].rm_eo - re->pmatch[1].rm_so); + + return STATUS_UNTAGGED; +} diff --git a/src/session.c b/src/session.c index e186e73..70ddd06 100644 --- a/src/session.c +++ b/src/session.c @@ -42,6 +42,7 @@ session_init(session *ssn) ssn->ns.prefix = NULL; ssn->ns.delim = '\0'; ssn->utf8 = 0; + ssn->notify_active = 0; } diff --git a/src/session.h b/src/session.h index 8282dbb..fd58e98 100644 --- a/src/session.h +++ b/src/session.h @@ -17,6 +17,7 @@ typedef struct session { char delim; /* Namespace delimiter. */ } ns; int utf8; /* UTF8 enabled. */ + int notify_active; /* NOTIFY is active */ } session;