Page MenuHomePhorge

No OneTemporary

Authored By
Unknown
Size
36 KB
Referenced Files
None
Subscribers
None
diff --git a/imap/mboxevent.c b/imap/mboxevent.c
index 845f2ab32..e82ac4b71 100644
--- a/imap/mboxevent.c
+++ b/imap/mboxevent.c
@@ -1,1125 +1,1124 @@
/*
* Copyright (c) 1994-2012 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any legal
* details, please contact
* Carnegie Mellon University
* Center for Technology Transfer and Enterprise Creation
* 4615 Forbes Avenue
* Suite 302
* Pittsburgh, PA 15213
* (412) 268-7393, fax: (412) 268-7395
* innovation@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Author: Sébastien Michel from Atos Worldline
*/
#include <config.h>
#ifndef ENABLE_MBOXEVENT
#include "mboxevent.h"
#else
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <syslog.h>
#include <stdlib.h>
#include <jansson.h>
#include "assert.h"
#include "imapurl.h"
#include "libconfig.h"
#include "hash.h"
#include "times.h"
#include "xmalloc.h"
#include "map.h"
#include "mboxevent.h"
#include "mboxname.h"
#include "notify.h"
#define MESSAGE_EVENTS (EVENT_MESSAGE_APPEND|EVENT_MESSAGE_EXPIRE|\
EVENT_MESSAGE_EXPUNGE|EVENT_MESSAGE_NEW|\
EVENT_MESSAGE_COPY|EVENT_MESSAGE_MOVE)
#define FLAGS_EVENTS (EVENT_FLAGS_SET|EVENT_FLAGS_CLEAR|EVENT_MESSAGE_READ|\
EVENT_MESSAGE_TRASH)
#define MAILBOX_EVENTS (EVENT_MAILBOX_CREATE|EVENT_MAILBOX_DELETE|\
EVENT_MAILBOX_RENAME|EVENT_MAILBOX_SUBSCRIBE|\
EVENT_MAILBOX_UNSUBSCRIBE)
#define QUOTA_EVENTS (EVENT_QUOTA_EXCEED|EVENT_QUOTA_WITHIN|EVENT_QUOTA_CHANGE)
#define FILL_STRING_PARAM(e,p,v) e->params[p].value = (uint64_t)v; \
e->params[p].type = EVENT_PARAM_STRING; \
e->params[p].filled = 1
#define FILL_ARRAY_PARAM(e,p,v) e->params[p].value = (uint64_t)v; \
e->params[p].type = EVENT_PARAM_ARRAY; \
e->params[p].filled = 1
-#define FILL_INT_PARAM(e,p,v) if (e->params[p].value >= 0) { \
- e->params[p].value = v; \
+#define FILL_UNSIGNED_PARAM(e,p,v) e->params[p].value = (uint64_t)v; \
e->params[p].type = EVENT_PARAM_INT; \
- e->params[p].filled = 1; \
- }
+ e->params[p].filled = 1
static const char *notifier = NULL;
static struct namespace namespace;
static strarray_t *excluded_flags;
static strarray_t *excluded_specialuse;
static int enable_subfolder = 1;
static int enabled_events = 0;
static unsigned long extra_params;
static struct mboxevent event_template =
{ 0,
/* ordered to optimize the parsing of the notification message */
{ { EVENT_TIMESTAMP, "timestamp", EVENT_PARAM_STRING, 0, 0 },
{ EVENT_SERVICE, "service", EVENT_PARAM_STRING, 0, 0 },
{ EVENT_SERVER_ADDRESS, "serverAddress", EVENT_PARAM_STRING, 0, 0 },
{ EVENT_CLIENT_ADDRESS, "clientAddress", EVENT_PARAM_STRING, 0, 0 },
{ EVENT_OLD_MAILBOX_ID, "oldMailboxID", EVENT_PARAM_STRING, 0, 0 },
{ EVENT_OLD_UIDSET, "vnd.cmu.oldUidset", EVENT_PARAM_STRING, 0, 0 },
{ EVENT_MAILBOX_ID, "mailboxID", EVENT_PARAM_STRING, 0, 0 },
{ EVENT_URI, "uri", EVENT_PARAM_STRING, 0, 0 },
{ EVENT_MODSEQ, "modseq", EVENT_PARAM_INT, 0, 0 },
{ EVENT_DISK_QUOTA, "diskQuota", EVENT_PARAM_INT, 0, 0 },
{ EVENT_DISK_USED, "diskUsed", EVENT_PARAM_INT, 0, 0 },
{ EVENT_MAX_MESSAGES, "maxMessages", EVENT_PARAM_INT, 0, 0 },
{ EVENT_MESSAGES, "messages", EVENT_PARAM_INT, 0, 0 },
{ EVENT_UNSEEN_MESSAGES, "vnd.cmu.unseenMessages", EVENT_PARAM_INT, 0, 0 },
{ EVENT_UIDNEXT, "uidnext", EVENT_PARAM_INT, 0, 0 },
{ EVENT_UIDSET, "uidset", EVENT_PARAM_STRING, 0, 0 },
{ EVENT_MIDSET, "vnd.cmu.midset", EVENT_PARAM_STRING, 0, 0 },
{ EVENT_FLAG_NAMES, "flagNames", EVENT_PARAM_STRING, 0, 0 },
{ EVENT_USER, "user", EVENT_PARAM_STRING, 0, 0 },
{ EVENT_MESSAGE_SIZE, "messageSize", EVENT_PARAM_INT, 0, 0 },
/* always at end to let the parser to easily truncate this part */
{ EVENT_BODYSTRUCTURE, "bodyStructure", EVENT_PARAM_STRING, 0, 0 },
{ EVENT_MESSAGE_CONTENT, "messageContent", EVENT_PARAM_STRING, 0, 0 }
},
STRARRAY_INITIALIZER, { 0, 0 }, NULL, STRARRAY_INITIALIZER, NULL, NULL, NULL
};
static char *json_formatter(enum event_type type, struct event_parameter params[]);
#ifndef NDEBUG
static int filled_params(enum event_type type, struct mboxevent *mboxevent);
#endif
static int mboxevent_expected_param(enum event_type type, enum event_param param);
#endif
EXPORTED void mboxevent_init(void)
{
#ifdef ENABLE_MBOXEVENT
const char *options;
int groups;
if (!(notifier = config_getstring(IMAPOPT_EVENT_NOTIFIER)))
return;
/* some don't want to notify events for some IMAP flags */
options = config_getstring(IMAPOPT_EVENT_EXCLUDE_FLAGS);
excluded_flags = strarray_split(options, NULL, 0);
/* some don't want to notify events on some folders (ie. Sent, Spam) */
/* identify those folders with IMAP SPECIAL-USE */
options = config_getstring(IMAPOPT_EVENT_EXCLUDE_SPECIALUSE);
excluded_specialuse = strarray_split(options, NULL, 0);
/* special meaning to disable event notification on all sub folders */
if (strarray_find_case(excluded_specialuse, "ALL", 0) >= 0)
enable_subfolder = 0;
/* get event types's extra parameters */
extra_params = config_getbitfield(IMAPOPT_EVENT_EXTRA_PARAMS);
/* groups of related events to turn on notification */
groups = config_getbitfield(IMAPOPT_EVENT_GROUPS);
if (groups & IMAP_ENUM_EVENT_GROUPS_MESSAGE)
enabled_events |= MESSAGE_EVENTS;
if (groups & IMAP_ENUM_EVENT_GROUPS_QUOTA)
enabled_events |= QUOTA_EVENTS;
if (groups & IMAP_ENUM_EVENT_GROUPS_FLAGS)
enabled_events |= FLAGS_EVENTS;
if (groups & IMAP_ENUM_EVENT_GROUPS_ACCESS)
enabled_events |= (EVENT_LOGIN|EVENT_LOGOUT);
if (groups & IMAP_ENUM_EVENT_GROUPS_MAILBOX)
enabled_events |= MAILBOX_EVENTS;
#endif
}
EXPORTED void mboxevent_setnamespace(struct namespace *n)
{
#ifdef ENABLE_MBOXEVENT
namespace = *n;
/* standardize IMAP URL format */
namespace.isadmin = 0;
#endif
}
#ifdef ENABLE_MBOXEVENT
static int mboxevent_enabled_for_mailbox(struct mailbox *mailbox)
{
int i = 0;
if (!enable_subfolder && (mboxname_isusermailbox(mailbox->name, 1)) == NULL) {
return 0;
}
/* test if the mailbox has a special-use attribute in the exclude list */
if (strarray_size(excluded_specialuse) > 0) {
strarray_t *specialuse = NULL;
const char *attribute;
/* get info and set flags */
specialuse = strarray_split(mailbox->specialuse, NULL, 0);
for (i = 0; i < strarray_size(specialuse) ; i++) {
attribute = strarray_nth(specialuse, i);
if (strarray_find(excluded_specialuse, attribute, 0) >= 0)
return 0;
}
}
return 1;
}
#endif
EXPORTED struct mboxevent *mboxevent_new(enum event_type type)
{
struct mboxevent *mboxevent = NULL;
#ifdef ENABLE_MBOXEVENT
/* event notification is completely disabled */
if (!notifier)
return NULL;
/* the group to which belong the event is not enabled */
if (!(enabled_events & type))
return NULL;
mboxevent = xmalloc(sizeof(struct mboxevent));
memcpy(mboxevent, &event_template, sizeof(struct mboxevent));
mboxevent->type = type;
/* From RFC 5423:
* the time at which the event occurred that triggered the notification
* (...). This MAY be an approximate time.
*
* so it seems appropriate here */
if (mboxevent_expected_param(type, EVENT_TIMESTAMP))
gettimeofday(&mboxevent->timestamp, NULL);
#endif
return mboxevent;
}
struct mboxevent *mboxevent_enqueue(enum event_type type,
struct mboxevent **mboxevents)
{
struct mboxevent *mboxevent = NULL;
#ifdef ENABLE_MBOXEVENT
struct mboxevent *ptr;
if (!(mboxevent = mboxevent_new(type)))
return NULL;
if (mboxevents) {
if (*mboxevents == NULL)
*mboxevents = mboxevent;
else {
/* append the newly created event at end of the chained list */
ptr = *mboxevents;
while (ptr->next)
ptr = ptr->next;
ptr->next = mboxevent;
mboxevent->prev = ptr;
}
}
#endif
return mboxevent;
}
EXPORTED void mboxevent_free(struct mboxevent **mboxevent)
{
#ifdef ENABLE_MBOXEVENT
struct mboxevent *event = *mboxevent;
int i;
if (!event)
return;
seqset_free(event->uidset);
seqset_free(event->olduidset);
strarray_fini(&event->midset);
for (i = 0; i <= MAX_PARAM; i++) {
if (event->params[i].filled && event->params[i].type == EVENT_PARAM_STRING)
free((char *)event->params[i].value);
}
if (event->prev)
event->prev->next = event->next;
free(event);
*mboxevent = NULL;
#endif
}
void mboxevent_freequeue(struct mboxevent **mboxevent)
{
#ifdef ENABLE_MBOXEVENT
struct mboxevent *next, *event = *mboxevent;
if (!event)
return;
do {
next = event->next;
mboxevent_free(&event);
event = next;
}
while (event);
*mboxevent = NULL;
#endif
}
#ifdef ENABLE_MBOXEVENT
static int mboxevent_expected_param(enum event_type type, enum event_param param)
{
switch (param) {
case EVENT_BODYSTRUCTURE:
return (extra_params & IMAP_ENUM_EVENT_EXTRA_PARAMS_BODYSTRUCTURE) &&
(type & (EVENT_MESSAGE_NEW|EVENT_MESSAGE_APPEND));
case EVENT_CLIENT_ADDRESS:
return (extra_params & IMAP_ENUM_EVENT_EXTRA_PARAMS_CLIENTADDRESS) &&
(type & EVENT_LOGIN);
case EVENT_DISK_QUOTA:
return type & QUOTA_EVENTS;
case EVENT_DISK_USED:
return (type & (EVENT_QUOTA_EXCEED|EVENT_QUOTA_WITHIN) ||
/* quota usage is not known on event MessageNew, MessageAppend,
* MessageCopy and MessageExpunge.
* Thus, some code refactoring is needed to support diskUsed
* extra parameter */
((extra_params & IMAP_ENUM_EVENT_EXTRA_PARAMS_DISKUSED) &&
(type & (EVENT_QUOTA_CHANGE))));
case EVENT_FLAG_NAMES:
return (type & (EVENT_FLAGS_SET|EVENT_FLAGS_CLEAR)) ||
((extra_params & IMAP_ENUM_EVENT_EXTRA_PARAMS_FLAGNAMES) &&
(type & (EVENT_MESSAGE_APPEND|EVENT_MESSAGE_NEW)));
case EVENT_MAILBOX_ID:
return (type & MAILBOX_EVENTS);
case EVENT_MAX_MESSAGES:
return type & QUOTA_EVENTS;
case EVENT_MESSAGE_CONTENT:
return (extra_params & IMAP_ENUM_EVENT_EXTRA_PARAMS_MESSAGECONTENT) &&
(type & (EVENT_MESSAGE_APPEND|EVENT_MESSAGE_NEW));
case EVENT_MESSAGE_SIZE:
return (extra_params & IMAP_ENUM_EVENT_EXTRA_PARAMS_MESSAGESIZE) &&
(type & (EVENT_MESSAGE_APPEND|EVENT_MESSAGE_NEW));
case EVENT_MESSAGES:
if (type & (EVENT_QUOTA_EXCEED|EVENT_QUOTA_WITHIN))
return 1;
if (!(extra_params & IMAP_ENUM_EVENT_EXTRA_PARAMS_MESSAGES))
return 0;
break;
case EVENT_MODSEQ:
if (!(extra_params & IMAP_ENUM_EVENT_EXTRA_PARAMS_MODSEQ))
return 0;
break;
case EVENT_OLD_MAILBOX_ID:
return type & (EVENT_MESSAGE_COPY|EVENT_MESSAGE_MOVE|EVENT_MAILBOX_RENAME);
case EVENT_SERVER_ADDRESS:
return type & (EVENT_LOGIN|EVENT_LOGOUT);
case EVENT_SERVICE:
return extra_params & IMAP_ENUM_EVENT_EXTRA_PARAMS_SERVICE;
case EVENT_TIMESTAMP:
return extra_params & IMAP_ENUM_EVENT_EXTRA_PARAMS_TIMESTAMP;
case EVENT_UIDNEXT:
if (!(extra_params & IMAP_ENUM_EVENT_EXTRA_PARAMS_UIDNEXT))
return 0;
break;
case EVENT_UIDSET:
if (type & (EVENT_MESSAGE_NEW|EVENT_MESSAGE_APPEND))
return 0;
break;
case EVENT_URI:
return 1;
case EVENT_USER:
return type & (EVENT_MAILBOX_SUBSCRIBE|EVENT_MAILBOX_UNSUBSCRIBE|\
EVENT_LOGIN|EVENT_LOGOUT);
case EVENT_MIDSET:
if (!(extra_params & IMAP_ENUM_EVENT_EXTRA_PARAMS_VND_CMU_MIDSET))
return 0;
break;
case EVENT_UNSEEN_MESSAGES:
if (!(extra_params & IMAP_ENUM_EVENT_EXTRA_PARAMS_VND_CMU_UNSEENMESSAGES))
return 0;
break;
case EVENT_OLD_UIDSET:
return type & (EVENT_MESSAGE_COPY|EVENT_MESSAGE_MOVE);
}
/* test if the parameter is related to a message event */
return type & (MESSAGE_EVENTS|FLAGS_EVENTS);
}
#endif
#define TIMESTAMP_MAX 32
EXPORTED void mboxevent_notify(struct mboxevent *mboxevents)
{
#ifdef ENABLE_MBOXEVENT
enum event_type type;
struct mboxevent *event, *next;
char stimestamp[TIMESTAMP_MAX+1];
char *formatted_message;
/* nothing to notify */
if (!mboxevents)
return;
event = mboxevents;
/* swap FlagsSet and FlagsClear notification order depending the presence of
* the \Seen flag because it changes the value of vnd.cmu.unseenMessages */
if (event->type == EVENT_FLAGS_SET &&
event->next &&
event->next->type == EVENT_FLAGS_CLEAR &&
strarray_find_case(&event->next->flagnames, "\\Seen", 0) >= 0) {
next = event->next;
event->next = next->next;
next->next = event;
event = next;
}
/* loop over the chained list of events */
do {
if (event->type == EVENT_CANCELLED)
goto next;
/* verify that at least one message has been added depending the event type */
if (event->type & (MESSAGE_EVENTS|FLAGS_EVENTS)) {
if (event->type & (EVENT_MESSAGE_NEW|EVENT_MESSAGE_APPEND)) {
if (!event->params[EVENT_URI].filled)
goto next;
}
else
if (event->uidset == NULL)
goto next;
}
/* others quota are not supported by RFC 5423 */
if ((event->type & QUOTA_EVENTS) &&
!event->params[EVENT_DISK_QUOTA].filled &&
!event->params[EVENT_MAX_MESSAGES].filled)
goto next;
/* finish to fill event parameters structure */
if (mboxevent_expected_param(event->type, EVENT_SERVICE)) {
FILL_STRING_PARAM(event, EVENT_SERVICE, xstrdup(config_ident));
}
if (mboxevent_expected_param(event->type, EVENT_TIMESTAMP)) {
timeval_to_iso8601(&event->timestamp, timeval_ms,
stimestamp, sizeof(stimestamp));
FILL_STRING_PARAM(event, EVENT_TIMESTAMP, xstrdup(stimestamp));
}
if (event->uidset) {
FILL_STRING_PARAM(event, EVENT_UIDSET, seqset_cstring(event->uidset));
}
if (strarray_size(&event->midset) > 0) {
FILL_ARRAY_PARAM(event, EVENT_MIDSET, &event->midset);
}
if (event->olduidset) {
FILL_STRING_PARAM(event, EVENT_OLD_UIDSET, seqset_cstring(event->olduidset));
}
/* may split FlagsSet event in several event notifications */
do {
type = event->type;
/* prefer MessageRead and MessageTrash to FlagsSet as advised in the RFC */
if (type == EVENT_FLAGS_SET) {
int i;
if ((i = strarray_find(&event->flagnames, "\\Deleted", 0)) >= 0) {
type = EVENT_MESSAGE_TRASH;
strarray_remove(&event->flagnames, i);
}
else if ((i = strarray_find(&event->flagnames, "\\Seen", 0)) >= 0) {
type = EVENT_MESSAGE_READ;
strarray_remove(&event->flagnames, i);
}
}
if (strarray_size(&event->flagnames) > 0) {
/* don't send flagNames parameter for those events */
if (type != EVENT_MESSAGE_TRASH && type != EVENT_MESSAGE_READ) {
char *flagnames = strarray_join(&event->flagnames, " ");
FILL_STRING_PARAM(event, EVENT_FLAG_NAMES, flagnames);
/* stop to loop for flagsSet event here */
strarray_fini(&event->flagnames);
}
}
/* check if expected event parameters are filled */
assert(filled_params(type, event));
/* notification is ready to send */
formatted_message = json_formatter(type, event->params);
notify(notifier, "EVENT", NULL, NULL, NULL, 0, NULL, formatted_message);
free(formatted_message);
}
while (strarray_size(&event->flagnames) > 0);
next:
event = event->next;
}
while (event);
return;
#endif
}
void mboxevent_add_flags(struct mboxevent *event, char *flagnames[MAX_USER_FLAGS],
bit32 system_flags, bit32 user_flags[MAX_USER_FLAGS/32])
{
#ifdef ENABLE_MBOXEVENT
unsigned flag, flagmask;
/* add system flags */
if (system_flags & FLAG_DELETED) {
if (strarray_find_case(excluded_flags, "\\Deleted", 0) < 0)
strarray_add_case(&event->flagnames, "\\Deleted");
}
if (system_flags & FLAG_ANSWERED) {
if (strarray_find_case(excluded_flags, "\\Answered", 0) < 0)
strarray_add_case(&event->flagnames, "\\Answered");
}
if (system_flags & FLAG_FLAGGED) {
if (strarray_find_case(excluded_flags, "\\Flagged", 0) < 0)
strarray_add_case(&event->flagnames, "\\Flagged");
}
if (system_flags & FLAG_DRAFT) {
if (strarray_find_case(excluded_flags, "\\Draft", 0) < 0)
strarray_add_case(&event->flagnames, "\\Draft");
}
if (system_flags & FLAG_SEEN) {
if (strarray_find_case(excluded_flags, "\\Seen", 0) < 0)
strarray_add_case(&event->flagnames, "\\Seen");
}
/* add user flags */
for (flag = 0; flag < MAX_USER_FLAGS; flag++) {
if ((flag & 31) == 0) {
flagmask = user_flags[flag/32];
}
if (!(flagnames[flag] && (flagmask & (1<<(flag & 31)))))
continue;
if (strarray_find_case(excluded_flags, flagnames[flag], 0) < 0)
strarray_add_case(&event->flagnames, flagnames[flag]);
}
#endif
}
void mboxevent_add_flag(struct mboxevent *event, const char *flag)
{
#ifdef ENABLE_MBOXEVENT
if (!event)
return;
if (mboxevent_expected_param(event->type, EVENT_FLAG_NAMES))
strarray_add_case(&event->flagnames, flag);
#endif
}
EXPORTED void mboxevent_set_access(struct mboxevent *event,
const char *serveraddr, const char *clientaddr,
const char *userid, const char *mailboxname)
{
#ifdef ENABLE_MBOXEVENT
char url[MAX_MAILBOX_PATH+1];
struct imapurl imapurl;
char extname[MAX_MAILBOX_NAME];
if (!event)
return;
/* only notify Logout after successful Login */
if (!userid && event->type & EVENT_LOGOUT) {
event->type = EVENT_CANCELLED;
return;
}
/* all events needs uri parameter */
if (!event->params[EVENT_URI].filled) {
memset(&imapurl, 0, sizeof(struct imapurl));
imapurl.server = config_servername;
if (mailboxname != NULL) {
/* translate internal mailbox name to external */
assert(namespace.mboxname_toexternal != NULL);
(*namespace.mboxname_toexternal)(&namespace, mailboxname,
mboxname_to_userid(mailboxname),
extname);
imapurl.mailbox = extname;
imapurl.user = mboxname_to_userid(mailboxname);
}
imapurl_toURL(url, &imapurl);
FILL_STRING_PARAM(event, EVENT_URI, xstrdup(url));
if (event->type & MAILBOX_EVENTS) {
FILL_STRING_PARAM(event, EVENT_MAILBOX_ID, xstrdup(url));
}
}
if (serveraddr && mboxevent_expected_param(event->type, EVENT_SERVER_ADDRESS)) {
FILL_STRING_PARAM(event, EVENT_SERVER_ADDRESS, xstrdup(serveraddr));
}
if (clientaddr && mboxevent_expected_param(event->type, EVENT_CLIENT_ADDRESS)) {
FILL_STRING_PARAM(event, EVENT_CLIENT_ADDRESS, xstrdup(clientaddr));
}
if (userid && mboxevent_expected_param(event->type, EVENT_USER)) {
FILL_STRING_PARAM(event, EVENT_USER, xstrdup(userid));
}
#endif
}
EXPORTED void mboxevent_extract_record(struct mboxevent *event, struct mailbox *mailbox,
struct index_record *record)
{
#ifdef ENABLE_MBOXEVENT
char *msgid = NULL;
if (!event)
return;
/* add modseq only on first call, cancel otherwise */
if (mboxevent_expected_param(event->type, EVENT_MODSEQ)) {
if (event->uidset == NULL) {
- FILL_INT_PARAM(event, EVENT_MODSEQ, record->modseq);
+ FILL_UNSIGNED_PARAM(event, EVENT_MODSEQ, record->modseq);
}
else {
/* From RFC 5423:
* modseq May be included with any notification referring
* to one message.
*
* thus cancel inclusion of modseq parameter
*/
event->params[EVENT_MODSEQ].filled = 0;
}
}
/* add UID to uidset */
if (event->uidset == NULL)
event->uidset = seqset_init(0, SEQ_SPARSE);
seqset_add(event->uidset, record->uid, 1);
if (event->type == EVENT_CANCELLED)
return;
/* add Message-Id to midset or NIL if doesn't exists */
if (mboxevent_expected_param(event->type, (EVENT_MIDSET))) {
msgid = mailbox_cache_get_msgid(mailbox, record);
strarray_add(&event->midset, msgid ? msgid : "NIL");
if (msgid)
free(msgid);
}
/* add message size */
if (mboxevent_expected_param(event->type, EVENT_MESSAGE_SIZE)) {
- FILL_INT_PARAM(event, EVENT_MESSAGE_SIZE, record->size);
+ FILL_UNSIGNED_PARAM(event, EVENT_MESSAGE_SIZE, record->size);
}
/* add bodyStructure */
if (mboxevent_expected_param(event->type, EVENT_BODYSTRUCTURE)) {
FILL_STRING_PARAM(event, EVENT_BODYSTRUCTURE,
xstrndup(cacheitem_base(record, CACHE_BODYSTRUCTURE),
cacheitem_size(record, CACHE_BODYSTRUCTURE)));
}
#endif
}
void mboxevent_extract_copied_record(struct mboxevent *event,
const struct mailbox *mailbox, uint32_t uid)
{
#ifdef ENABLE_MBOXEVENT
int first = 0;
if (!event)
return;
/* add the source message's UID to oldUidset */
if (event->olduidset == NULL) {
event->olduidset = seqset_init(0, SEQ_SPARSE);
first = 1;
}
seqset_add(event->olduidset, uid, 1);
/* generate an IMAP URL to reference the old mailbox */
if (first)
mboxevent_extract_old_mailbox(event, mailbox);
#endif
}
void mboxevent_extract_content(struct mboxevent *event,
const struct index_record *record, FILE* content)
{
#ifdef ENABLE_MBOXEVENT
const char *base = NULL;
unsigned long len = 0;
- int offset, size, truncate;
+ size_t offset, size, truncate;
if (!event)
return;
if (!mboxevent_expected_param(event->type, EVENT_MESSAGE_CONTENT))
return;
truncate = config_getint(IMAPOPT_EVENT_CONTENT_SIZE);
switch (config_getenum(IMAPOPT_EVENT_CONTENT_INCLUSION_MODE)) {
/* include message up to 'truncate' in size with the notification */
case IMAP_ENUM_EVENT_CONTENT_INCLUSION_MODE_STANDARD:
if (!truncate || record->size <= truncate) {
offset = 0;
size = record->size;
}
else {
/* XXX RFC 5423 suggests to include a URLAUTH [RFC4467] reference
* for larger messages. IMAP URL of mailboxID seems enough though */
return;
}
break;
/* include message truncated to a size of 'truncate' */
case IMAP_ENUM_EVENT_CONTENT_INCLUSION_MODE_MESSAGE:
offset = 0;
size = (truncate && (record->size > truncate)) ?
truncate : record->size;
break;
/* include headers truncated to a size of 'truncate' */
case IMAP_ENUM_EVENT_CONTENT_INCLUSION_MODE_HEADER:
offset = 0;
size = (truncate && (record->header_size > truncate)) ?
truncate : record->header_size;
break;
/* include body truncated to a size of 'truncate' */
case IMAP_ENUM_EVENT_CONTENT_INCLUSION_MODE_BODY:
offset = record->header_size;
size = (truncate && ((record->size - record->header_size) > truncate)) ?
truncate : record->size - record->header_size;
break;
/* include full headers and body truncated to a size of 'truncate' */
case IMAP_ENUM_EVENT_CONTENT_INCLUSION_MODE_HEADERBODY:
offset = 0;
size = (truncate && ((record->size - record->header_size) > truncate)) ?
record->header_size + truncate : record->size;
break;
/* never happen */
default:
return;
}
map_refresh(fileno(content), 1, &base, &len, record->size, "new message", 0);
FILL_STRING_PARAM(event, EVENT_MESSAGE_CONTENT, xstrndup(base+offset, size));
map_free(&base, &len);
#endif
}
void mboxevent_extract_quota(struct mboxevent *event, const struct quota *quota,
enum quota_resource res)
{
#ifdef ENABLE_MBOXEVENT
struct imapurl imapurl;
char url[MAX_MAILBOX_PATH+1];
char extname[MAX_MAILBOX_NAME];
if (!event)
return;
switch(res) {
case QUOTA_STORAGE:
if (mboxevent_expected_param(event->type, EVENT_DISK_QUOTA)) {
if (quota->limits[res] >= 0)
- FILL_INT_PARAM(event, EVENT_DISK_QUOTA, quota->limits[res]);
+ FILL_UNSIGNED_PARAM(event, EVENT_DISK_QUOTA, quota->limits[res]);
}
if (mboxevent_expected_param(event->type, EVENT_DISK_USED)) {
- FILL_INT_PARAM(event, EVENT_DISK_USED,
+ FILL_UNSIGNED_PARAM(event, EVENT_DISK_USED,
quota->useds[res] / quota_units[res]);
}
break;
case QUOTA_MESSAGE:
if (quota->limits[res] >= 0)
- FILL_INT_PARAM(event, EVENT_MAX_MESSAGES, quota->limits[res]);
- FILL_INT_PARAM(event, EVENT_MESSAGES, quota->useds[res]);
+ FILL_UNSIGNED_PARAM(event, EVENT_MAX_MESSAGES, quota->limits[res]);
+ FILL_UNSIGNED_PARAM(event, EVENT_MESSAGES, quota->useds[res]);
break;
default:
/* others quota are not supported by RFC 5423 */
break;
}
/* From RFC 5423 :
* The parameters SHOULD include at least the relevant user
* and quota and, optionally, the mailbox.
*
* It seems that it does not correspond to the concept of
* quota root specified in RFC 2087. Thus we fill uri with quota root
*/
if (!event->params[EVENT_URI].filled && event->type & QUOTA_EVENTS) {
/* translate internal mailbox name to external */
assert(namespace.mboxname_toexternal != NULL);
(*namespace.mboxname_toexternal)(&namespace, quota->root,
mboxname_to_userid(quota->root),
extname);
memset(&imapurl, 0, sizeof(struct imapurl));
imapurl.server = config_servername;
imapurl.mailbox = extname;
imapurl.user = mboxname_to_userid(quota->root);
imapurl_toURL(url, &imapurl);
FILL_STRING_PARAM(event, EVENT_URI, xstrdup(url));
}
#endif
}
EXPORTED void mboxevent_set_numunseen(struct mboxevent *event,
struct mailbox *mailbox, int numunseen)
{
#ifdef ENABLE_MBOXEVENT
if (!event)
return;
if (mboxevent_expected_param(event->type, EVENT_UNSEEN_MESSAGES)) {
+ unsigned count = (numunseen >= 0) ? (unsigned)numunseen
+ : mailbox_count_unseen(mailbox);
/* as event notification is focused on mailbox, we don't care about the
* authenticated user but the mailbox's owner.
* it could be a problem only if it is a shared or public folder */
- FILL_INT_PARAM(event, EVENT_UNSEEN_MESSAGES, numunseen >= 0 ? numunseen :
- mailbox_count_unseen(mailbox));
+ FILL_UNSIGNED_PARAM(event, EVENT_UNSEEN_MESSAGES, count);
}
#endif
}
EXPORTED void mboxevent_extract_mailbox(struct mboxevent *event,
struct mailbox *mailbox)
{
#ifdef ENABLE_MBOXEVENT
struct imapurl imapurl;
char url[MAX_MAILBOX_PATH+1];
char extname[MAX_MAILBOX_NAME];
if (!event)
return;
/* mboxevent_extract_mailbox should be called only once */
if (event->params[EVENT_URI].filled)
return;
/* verify if event notification should be disabled for this mailbox */
if (!mboxevent_enabled_for_mailbox(mailbox)) {
event->type = EVENT_CANCELLED;
return;
}
/* translate internal mailbox name to external */
assert(namespace.mboxname_toexternal != NULL);
(*namespace.mboxname_toexternal)(&namespace, mailbox->name,
mboxname_to_userid(mailbox->name), extname);
/* all events needs uri parameter */
memset(&imapurl, 0, sizeof(struct imapurl));
imapurl.server = config_servername;
imapurl.mailbox = extname;
imapurl.user = mboxname_to_userid(mailbox->name);
imapurl.uidvalidity = mailbox->i.uidvalidity;
if (event->type & (EVENT_MESSAGE_NEW|EVENT_MESSAGE_APPEND)) {
imapurl.uid = seqset_first(event->uidset);
/* don't add uidset parameter to MessageNew and MessageAppend events */
seqset_free(event->uidset);
event->uidset = NULL;
}
imapurl_toURL(url, &imapurl);
FILL_STRING_PARAM(event, EVENT_URI, xstrdup(url));
/* mailbox related events also require mailboxID */
if (event->type & MAILBOX_EVENTS) {
FILL_STRING_PARAM(event, EVENT_MAILBOX_ID, xstrdup(url));
}
if (mboxevent_expected_param(event->type, EVENT_UIDNEXT)) {
- FILL_INT_PARAM(event, EVENT_UIDNEXT, mailbox->i.last_uid+1);
+ FILL_UNSIGNED_PARAM(event, EVENT_UIDNEXT, mailbox->i.last_uid+1);
}
/* From RFC 5423 :
* messages
* Included with QuotaExceed and QuotaWithin notifications relating
* to a user or mailbox message count quota. May be included with
* other notifications.
*
* Number of messages in the mailbox. This is typically included
* with message addition and deletion events.
*
* we are in case messages is relative to the number of messages in the
* mailbox and not the message count quota.
*/
if (mboxevent_expected_param(event->type, EVENT_MESSAGES)) {
- FILL_INT_PARAM(event, EVENT_MESSAGES, mailbox->i.exists);
+ FILL_UNSIGNED_PARAM(event, EVENT_MESSAGES, mailbox->i.exists);
}
#endif
}
void mboxevent_extract_old_mailbox(struct mboxevent *event,
const struct mailbox *mailbox)
{
#ifdef ENABLE_MBOXEVENT
struct imapurl imapurl;
char url[MAX_MAILBOX_PATH+1];
char extname[MAX_MAILBOX_NAME];
if (!event)
return;
/* translate internal mailbox name to external */
assert(namespace.mboxname_toexternal != NULL);
(*namespace.mboxname_toexternal)(&namespace, mailbox->name,
mboxname_to_userid(mailbox->name), extname);
memset(&imapurl, 0, sizeof(struct imapurl));
imapurl.server = config_servername;
imapurl.mailbox = extname;
imapurl.user = mboxname_to_userid(mailbox->name);
imapurl.uidvalidity = mailbox->i.uidvalidity;
imapurl_toURL(url, &imapurl);
FILL_STRING_PARAM(event, EVENT_OLD_MAILBOX_ID, xstrdup(url));
#endif
}
#ifdef ENABLE_MBOXEVENT
static const char *event_to_name(enum event_type type)
{
switch (type) {
case EVENT_MESSAGE_APPEND:
return "MessageAppend";
case EVENT_MESSAGE_EXPIRE:
return "MessageExpire";
case EVENT_MESSAGE_EXPUNGE:
return "MessageExpunge";
case EVENT_MESSAGE_NEW:
return "MessageNew";
case EVENT_MESSAGE_COPY:
return "vnd.cmu.MessageCopy";
case EVENT_MESSAGE_MOVE:
return "vnd.cmu.MessageMove";
case EVENT_QUOTA_EXCEED:
return "QuotaExceed";
case EVENT_QUOTA_WITHIN:
return "QuotaWithin";
case EVENT_QUOTA_CHANGE:
return "QuotaChange";
case EVENT_MESSAGE_READ:
return "MessageRead";
case EVENT_MESSAGE_TRASH:
return "MessageTrash";
case EVENT_FLAGS_SET:
return "FlagsSet";
case EVENT_FLAGS_CLEAR:
return "FlagsClear";
case EVENT_LOGIN:
return "Login";
case EVENT_LOGOUT:
return "Logout";
case EVENT_MAILBOX_CREATE:
return "MailboxCreate";
case EVENT_MAILBOX_DELETE:
return "MailboxDelete";
case EVENT_MAILBOX_RENAME:
return "MailboxRename";
case EVENT_MAILBOX_SUBSCRIBE:
return "MailboxSubscribe";
case EVENT_MAILBOX_UNSUBSCRIBE:
return "MailboxUnSubscribe";
default:
fatal("Unknown message event", EC_SOFTWARE);
}
/* never happen */
return NULL;
}
static char *json_formatter(enum event_type type, struct event_parameter params[])
{
int param, ival;
char *val, *ptr, *result;
json_t *event_json = json_object();
json_t *jarray;
json_object_set_new(event_json, "event", json_string(event_to_name(type)));
for (param = 0; param <= MAX_PARAM; param++) {
if (!params[param].filled)
continue;
switch (params[param].id) {
case EVENT_CLIENT_ADDRESS:
/* come from saslprops structure */
val = strdup((char *)params[param].value);
ptr = strchr(val, ';');
*ptr++ = '\0';
json_object_set_new(event_json, "clientIP", json_string(val));
if (parseint32(ptr, (const char **)&ptr, &ival) >= 0)
json_object_set_new(event_json, "clientPort", json_integer(ival));
free(val);
break;
case EVENT_SERVER_ADDRESS:
/* come from saslprops structure */
val = strdup((char *)params[param].value);
ptr = strchr(val, ';');
*ptr++ = '\0';
json_object_set_new(event_json, "serverDomain", json_string(val));
if (parseint32(ptr, (const char **)&ptr, &ival) >= 0)
json_object_set_new(event_json, "serverPort", json_integer(ival));
free(val);
break;
default:
switch (params[param].type) {
case EVENT_PARAM_INT:
json_object_set_new(event_json, params[param].name,
json_integer(params[param].value));
break;
case EVENT_PARAM_STRING:
json_object_set_new(event_json, params[param].name,
json_string((char *)params[param].value));
break;
case EVENT_PARAM_ARRAY:
jarray = json_array();
strarray_t *sarray = (strarray_t *)params[param].value;
int i;
for (i = 0; i < strarray_size(sarray); i++) {
json_array_append_new(jarray, json_string(strarray_nth(sarray, i)));
}
json_object_set_new(event_json, params[param].name, jarray);
break;
}
break;
}
}
result = json_dumps(event_json, JSON_PRESERVE_ORDER|JSON_COMPACT);
json_decref(event_json);
return result;
}
#ifndef NDEBUG
/* overrides event->type with event_type because FlagsSet may be derived to
* MessageTrash or MessageRead */
static int filled_params(enum event_type type, struct mboxevent *event)
{
struct buf missing = BUF_INITIALIZER;
int param, ret = 1;
for (param = 0; param <= MAX_PARAM; param++) {
if (mboxevent_expected_param(type, param) &&
!event->params[param].filled) {
switch (event->params[param].id) {
case EVENT_DISK_QUOTA:
return event->params[EVENT_MAX_MESSAGES].filled;
case EVENT_DISK_USED:
return event->params[EVENT_MESSAGES].filled;
case EVENT_FLAG_NAMES:
/* flagNames may be included with MessageAppend and MessageNew
* also we don't expect it here. */
if (!(type & (EVENT_MESSAGE_APPEND|EVENT_MESSAGE_NEW)))
buf_appendcstr(&missing, " flagNames");
break;
case EVENT_MAX_MESSAGES:
return event->params[EVENT_DISK_QUOTA].filled;
case EVENT_MESSAGE_CONTENT:
/* messageContent is not included in standard mode if the size
* of the message exceed the limit */
if (config_getenum(IMAPOPT_EVENT_CONTENT_INCLUSION_MODE) !=
IMAP_ENUM_EVENT_CONTENT_INCLUSION_MODE_STANDARD)
buf_appendcstr(&missing, " messageContent");
break;
case EVENT_MESSAGES:
return event->params[EVENT_DISK_USED].filled;
case EVENT_MODSEQ:
/* modseq is not included if notification refers to several
* messages */
if (seqset_first(event->uidset) == seqset_last(event->uidset))
buf_appendcstr(&missing, " modseq");
break;
default:
buf_appendcstr(&missing, " ");
buf_appendcstr(&missing, event->params[param].name);
break;
}
}
}
if (buf_len(&missing)) {
syslog(LOG_ALERT, "Cannot notify event %s: missing parameters:%s",
event_to_name(type), buf_cstring(&missing));
ret = 0;
}
buf_free(&missing);
return ret;
}
#endif /* NDEBUG */
#endif /* ENABLE_MBOXEVENT */

File Metadata

Mime Type
text/x-diff
Expires
Sat, Apr 4, 1:39 AM (1 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18821965
Default Alt Text
(36 KB)

Event Timeline