diff --git a/configure.ac b/configure.ac
index 646aa62..0e9b0f7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -358,6 +358,8 @@ PKG_CHECK_MODULES(GNUTLS, gnutls,
AM_PATH_LIBGCRYPT()
+AC_CHECK_GMIME()
+
AM_PATH_PYTHON(2.3)
PYTHON_INST_PREFIX=`$PYTHON -c 'import sys; print sys.prefix'`
PYTHON_CPPFLAGS="-I$PYTHON_INST_PREFIX/include/python$PYTHON_VERSION"
diff --git a/m4/gmime.m4 b/m4/gmime.m4
new file mode 100644
index 0000000..1c5cb7f
--- /dev/null
+++ b/m4/gmime.m4
@@ -0,0 +1,36 @@
+dnl Check for GMime Library.
+
+AC_DEFUN([AC_CHECK_GMIME], [
+ have_gmime="no"
+ ac_gmime="no"
+
+ # exported variables
+ GMIME_LIBS=""
+ GMIME_CFLAGS=""
+
+ AC_ARG_WITH(gmime, AC_HELP_STRING([--with-gmime[=dir]], [Compile with libgmime at given dir]),
+ [ ac_gmime="$withval"
+ if test "x$withval" != "xno" -a "x$withval" != "xyes"; then
+ ac_gmime="yes"
+ ac_gmime_dir=$withval
+ fi ],
+ [ ac_gmime="auto" ] )
+
+ AC_MSG_CHECKING([for GMime-2.4])
+
+ if test "x$PKG_CONFIG" != "xno"; then
+ if test "x$ac_gmime" = "xyes"; then
+ ac_gmime_tmppkgconfig=$PKG_CONFIG_PATH
+ export PKG_CONFIG_PATH=${ac_gmime_dir}/lib/pkgconfig/
+ echo Setting path to $PKG_CONFIG_PATH
+ PKG_CHECK_MODULES(GMIME, gmime-2.4, ac_gmime="no")
+ export PKG_CONFIG_PATH=$ac_gmime_tmppkgconfig
+ fi
+
+ if test "x$ac_gmime" = "xauto"; then
+ PKG_CHECK_MODULES(GMIME, gmime-2.4, ac_gmime="no")
+ fi
+ fi
+
+ AC_MSG_RESULT([$ac_gmime])
+])
diff --git a/src/agents/store/Bongo.rules b/src/agents/store/Bongo.rules
index 726cc1d..8cbc6f8 100644
--- a/src/agents/store/Bongo.rules
+++ b/src/agents/store/Bongo.rules
@@ -4,6 +4,7 @@ sbin_PROGRAMS += bongostore
bongostore_CPPFLAGS = $(AM_CPPFLAGS) \
-Wall -Werror \
-I$(top_srcdir)/src/agents/store \
+ $(GMIME_CFLAGS) \
$(SQLITE_CFLAGS)
# this is a hack because certain automakes don't seem to output .o
@@ -33,8 +34,6 @@ bongostore_SOURCES = \
src/agents/store/mail.c \
src/agents/store/mail.h \
src/agents/store/maildir.c \
- src/agents/store/mail-parser.c \
- src/agents/store/mail-parser.h \
src/agents/store/main.cpp \
src/agents/store/messages.h \
src/agents/store/mime.c \
@@ -65,7 +64,8 @@ bongostore_LDADD = \
libbongoutil.la \
libbongoxpl.la \
$(top_builddir)/import/log4c/log4c/libbongolog4c.la \
- $(SQLITE_LIBS) \
+ $(SQLITE_LIBS) \
+ $(GMIME_LIBS) \
$(PTHREAD_LIBS) \
$(KSTAT_LIBS) \
$(ALL_LIBS)
diff --git a/src/agents/store/mail-parser.c b/src/agents/store/mail-parser.c
deleted file mode 100644
index f2aeca8..0000000
--- a/src/agents/store/mail-parser.c
+++ /dev/null
@@ -1,1195 +0,0 @@
-/****************************************************************************
- *
- * Copyright (c) 2005, 2006 Novell, Inc. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public License
- * as published by the Free Software Foundation.
- *
- * 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, contact Novell, Inc.
- *
- * To contact Novell about this file by physical or electronic mail, you
- * may find current contact information at www.novell.com.
- *
- ****************************************************************************/
-
-/* Routines for parsing and manipulating mail messages */
-
-#include
-#include "stored.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "mail-parser.h"
-#include "mime.h"
-
-typedef struct {
- MailHeaderName name;
- union {
- struct {
- char *email;
- char *name;
- };
- char *content_type;
- uint64_t date;
- };
-} MailHeader;
-
-/* FIXME This is a third-assed mail header parser. */
-
-struct MailParser {
- FILE *stream;
- MailParserParticipantsCallback participantscb;
- MailParserUnstructuredCallback unstructuredcb;
- MailParserDateCallback datecb;
- MailParserMessageIdCallback messageidcb;
- MailParserOffsetCallback offsetcb;
- void *cbarg;
-
- char line[2048];
- char headerName[128];
- char *p;
-};
-
-
-#define NEXTLINE(_retval) \
- if (!fgets(parser->line, 2048, parser->stream)) { \
- return _retval; \
- } \
- parser->p = parser->line;
-
-
-
-
-/* requires: parser->p points to the first char following
- the comment's opening '('
-
- returns: -1 on EOF/error, o/w 0
-
- modifies: parser->p will be advanced to the next char
- following the closing ')'
- */
-
-static int
-ParseComment(MailParser *parser)
-{
- int c;
-
- while (1) {
- switch (*parser->p++) {
- case 0:
- NEXTLINE(-1);
- continue;
- case '(':
- if (ParseComment(parser)) {
- return -1;
- }
- continue;
- case ')':
- return 0;
- case '\\': /* quoted pair */
- c = *parser->p++;
- if (0 == c) {
- NEXTLINE(-1);
- }
- continue;
- default:
- continue;
- }
- }
-}
-
-
-/* returns: 0 - EOF or run-on comment
- '\n' - non-folded CRLF
- char - next non-whitespace char
-
- modifies: parser->line may be refreshed to the next line
- parser->p will be advanced to the next non-whitespace character
- */
-
-static char
-NextNonCFWS(MailParser *parser)
-{
- int c;
-#if 0
- if (parser->p == parser->line) {
- if ('\r' == *parser->p || '\n' == *parser->p) {
- /* we are at the end of the headers */
- return *parser->p;
- }
- }
-#endif
- while (1) {
- switch (*parser->p++) {
- case 0:
- NEXTLINE(0);
- continue;
- case ' ':
- case '\t':
- continue;
- case '\r':
- c = *parser->p++;
- if (0 == c) {
- NEXTLINE(0);
- c = *parser->p++;
- }
- if ('\n' == c) {
- case '\n':
- NEXTLINE(0);
- if (' ' != *parser->p && '\t' != *parser->p) {
- return '\n'; /* CRLF */
- }
- continue;
- } else {
- /* again, I think this is illegal, treat as space */
- continue;
- }
- case '(':
- if (ParseComment(parser)) {
- return 0;
- }
- continue;
- default:
- return *--parser->p;
- }
- }
-}
-
-
-/* requires: points to a string of up to 80 characters to be examined to
- see if they are encoded text. specifies the capacity of
- * points to the first character following the last string
- successfully decoded by this function, or NULL if the last string
- was not encoded.
-
- modifies: if contains rfc2047-encoded text, then it will be decoded
- in place into its UTF-8 representation, and * will be
- set to the first character following the decoded string.
-
- returns: the length of the decoded token minus (buf - *prevtext)
- */
-
-static int
-DecodeWord(char *buf, int len, char **prevtext)
-{
- char token[81]; /* max encoded word length is 75 characters, we accept up to 80 */
- char charset[81];
- const char *encoding = NULL;
- char *pbuf = *prevtext ? *prevtext : buf;
-
- BongoStream *stream = NULL;
-
- char *t; /* input ptr to */
- char *d; /* output ptr to buf */
- int tlen;
- int elen;
-
- tlen = strlen(buf);
- assert(tlen < 80);
-
- strncpy(token, buf, 80);
- *prevtext = NULL;
-
- /* encoded-word = "=?" charset "?" encoding "?" encoded-text "?=" */
-
- if ('=' != token[0] || '?' != token[1]) {
- return tlen;
- }
-
- /* find charset: */
-
- t = token + 2;
- d = charset;
-
- while (*t) {
- if (!*t) {
- return tlen;
- } else if ('?' == *t) {
- *d = 0;
- ++t;
- break;
- } else {
- *d++ = *t;
- }
- t++;
- }
-
- /* find encoding: */
-
- if ('B' == toupper(*t)) {
- encoding = "BASE64";
- } else if ('Q' == toupper(*t)) {
- encoding = "Q";
- } else {
- return tlen;
- }
-
- if ('?' != *++t) {
- return tlen;
- }
-
- /* verify encoded-text: */
-
- d = ++t;
- elen = 0;
- while (1) {
- if (0 == *d) {
- return tlen;
- } else if (' ' == *d) {
- return tlen;
- } else if ('?' == *d) {
- if ('=' != d[1] || 0 != d[2]) {
- return tlen;
- }
- break;
- }
- ++elen;
- ++d;
- }
-
- /* attempt to decode */
-
- stream = BongoStreamCreate(NULL, 0, FALSE);
- if (!stream) {
- return tlen;
- }
-
- if (BongoStreamAddCodec(stream, encoding, FALSE)) {
- Log(LOG_ERROR, "Mail parser doesn't understand encoding %s", encoding);
- goto done;
- }
-
- if (BongoStreamAddCodec(stream, charset, FALSE)) {
- Log(LOG_ERROR, "Mail parser doesn't understand charset %s", charset);
- goto done;
- }
-
- if (BongoStreamAddCodec(stream, "UTF-8", TRUE)) {
- Log(LOG_ERROR, "Mail parser couldn't convert to UTF-8");
- goto done;
- }
-
- BongoStreamPut(stream, t, elen);
- tlen = BongoStreamGet(stream, pbuf, len);
- *prevtext = pbuf + tlen;
- tlen -= (buf - pbuf);
-
-done:
- if (stream) {
- BongoStreamFree(stream);
- }
-
- return tlen;
-}
-
-
-static int
-ParseUnstructured(MailParser *parser, char *buf, int len)
-{
- int i = 0;
- int c;
- char *prevtext = NULL;
- int tokstart = 0;
-
- while (1) {
- c = *parser->p++;
-
- switch (c) {
- case 0:
- if (!fgets(parser->line, 2048, parser->stream)) {
- goto done;
- }
- parser->p = parser->line;
- continue;
- case '\r':
- c = *parser->p++;
- /* fall through */
- case '\n':
- if (i < len) {
- if (i > tokstart && '=' == buf[tokstart] && i - tokstart < 80) {
- buf[i] = 0;
- i = tokstart +
- DecodeWord(buf + tokstart, len - tokstart, &prevtext);
- }
- }
-
- if ('\n' == c) {
- if (!fgets(parser->line, 2048, parser->stream)) {
- goto done;
- }
- parser->p = parser->line;
-
- if (' ' != *parser->p && '\t' != *parser->p) {
- goto done;
- }
- if (i < len) {
- buf[i++] = *parser->p++;
- }
- tokstart = i;
- } else {
- /* again, I think this is illegal, treat as space */
- if (i < len) {
- buf[i++] = ' ';
- tokstart = i;
- if (i < len) {
- buf[i++] = c;
- }
- }
- }
- break;
- case ' ':
- case '\t':
- if (i < len) {
- if (i > tokstart && '=' == buf[tokstart] && i - tokstart < 80) {
- buf[i] = 0;
- i = tokstart +
- DecodeWord(buf + tokstart, len - tokstart, &prevtext);
- }
- buf[i++] = c;
- }
- tokstart = i;
- break;
-
- default:
- if (i < len) {
- buf[i++] = c;
- }
- }
- }
-
-done:
- buf[i < len ? i++ : len - 1] = 0;
-
- return i;
-}
-
-
-static int
-ParseQuotedString(MailParser *parser, char *buf, int len)
-{
- /*
- qtext = NO-WS-CTL / ; Non white space controls
-
- %d33 / ; The rest of the US-ASCII
- %d35-91 / ; characters not including "\"
- %d93-126 ; or the quote character
-
- qcontent = qtext / quoted-pair
-
- quoted-string = [CFWS]
- DQUOTE *([FWS] qcontent) [FWS] DQUOTE
- [CFWS]
- */
-
- int c;
- int i = 0;
-
- if ('"' != *parser->p++) {
- return -1;
- }
-
- while (1) {
- c = *parser->p++;
-
- switch (c) {
- case 0:
- NEXTLINE(-1);
- continue;
- case '\r':
- c = *parser->p++;
- if ('\n' == c) {
- case '\n':
- NEXTLINE(-1);
- if (' ' != *parser->p && '\t' != *parser->p) {
- return -1;
- }
- c = NextNonCFWS(parser);
- if ('\n' == c || 0 == c) {
- return -1;
- }
- if (i < len) {
- buf[i++] = ' ';
- }
- } else {
- if (i < len) {
- buf[i++] = ' ';
- if (i < len) {
- buf[i++] = c;
- }
- }
- }
- break;
- case '"':
- goto done;
- case '\\':
- c = *parser->p++;
- if (0 == c) {
- NEXTLINE(-1);
- c = *parser->p++;
- }
- /* fall through: */
- default:
- if (i < len) {
- buf[i++] = c;
- }
- }
- }
-
-done:
- buf[i < len ? i++ : len - 1] = 0;
-
- return i;
-}
-
-
-/* returns: approximate number of tokens in phrase, -1 on error */
-
-static int
-ParsePhrase(MailParser *parser, char *buf, int len)
-{
- int c;
- int i = 0;
- int tokens = 0;
- int n;
- int tokstart;
- char *prevtext = NULL;
-
- --len;
-
-loop:
- tokstart = i;
-
- c = NextNonCFWS(parser);
- if ('\n' == c || 0 == c) {
- goto done;
- } else {
- tokens++;
- while (1) {
- c = *parser->p;
- switch (c) {
- case 0:
- if (!fgets(parser->line, 2048, parser->stream)) {
- goto done;
- }
- parser->p = parser->line;
- continue;
- case '"':
- if (-1 == (n = ParseQuotedString(parser, buf + i, len - i))) {
- return -1;
- }
- i += n;
- continue;
- case '<':
- case '>':
- case '@':
- case ':':
- case ';':
- case ',':
- goto done;
- case ' ':
- case '\t':
- case '\r':
- if (i < len) {
- if ('=' == buf[tokstart] && i - tokstart < 80) {
- buf[i] = 0;
- i = tokstart +
- DecodeWord(buf + tokstart, len - tokstart, &prevtext);
- }
- buf[i++] = ' ';
- }
- goto loop;
- case '\n':
- if (i < len) {
- buf[i++] = ' ';
- }
- goto done;
- case '(':
- parser->p++;
- if (ParseComment(parser)) {
- goto done;
- }
- continue;
- default:
- if (i < len) {
- buf[i++] = c;
- }
- }
- ++parser->p;
- }
- }
-done:
- if (i && ' ' == buf[i-1]) {
- buf[i-1] = 0;
- } else {
- buf[i] = 0;
- }
- return tokens;
-}
-
-
-static int
-ParseDotAtom(MailParser *parser, char *buf, int len)
-{
- /* bad */
- if (0 == ParsePhrase(parser, buf, len)) {
- return -1;
- }
- return strlen(buf);
-}
-
-static int
-ParseLocalPart(MailParser *parser, char *buf, int len)
-{
- return ParseDotAtom(parser, buf, len);
-}
-
-
-static int
-ParseDomain(MailParser *parser, char *buf, int len)
-{
- return ParseDotAtom(parser, buf, len);
-}
-
-static int
-ParseAngleAddr(MailParser *parser, char *buf, int len)
-{
- int cnt;
- int pcode;
-
- ++parser->p;
-
- cnt = pcode = ParseLocalPart(parser, buf, len - 1);
- if (-1 == pcode) {
- return 0;
- }
- if ('@' != NextNonCFWS(parser)) {
- return 0;
- }
- ++parser->p;
- buf[cnt++] = '@';
- cnt += pcode = ParseDomain(parser, buf + cnt, len - cnt);
- if (-1 == pcode) {
- return 0;
- }
-
- if ('>' != NextNonCFWS(parser)) {
- return 0;
- }
- ++parser->p;
-
- return cnt;
-}
-
-
-static int ParseAddress(MailParser *parser, MailHeaderName header);
-static int ParseAddressList(MailParser *parser, MailHeaderName header);
-
-
-static int
-ParseMailbox(MailParser *parser, MailHeaderName header)
-{
- /* FIXME */
- return ParseAddress(parser, header);
-}
-
-
-static int
-ParseMailboxList(MailParser *parser, MailHeaderName header)
-{
- /* 1*([mailbox] [CFWS] "," [CFWS]) [mailbox] */
-
- /* FIXME */
- return ParseAddressList(parser, header);
-}
-
-
-/* returns: 1 - address found
- 0 - no address found
- -1 - error
-*/
-
-
-static int
-ParseAddress(MailParser *parser,
- MailHeaderName header)
-{
- /* mailbox / group */
-
- char c;
- MailAddress addr;
- char phrase[1024];
-
- c = NextNonCFWS(parser);
-
- if ('\n' == c || ';' == c || 0 == c) {
- return 0;
- } else if (',' == c) {
- /* forgive empty addresses in addresslists. */
- return 0;
- } else if ('<' == c) {
- if (-1 == ParseAngleAddr(parser, phrase, sizeof(phrase))) {
- /* Sometimes the address has the illegal form "BLAH <>" */
- phrase[0] = 0;
- }
- addr.displayName = NULL;
- addr.address = phrase;
- if (parser->participantscb) {
- parser->participantscb(parser->cbarg, header, &addr);
- }
-
- return 1;
- } else {
-
- if (0 == ParsePhrase(parser, phrase, sizeof(phrase))) {
- return -1;
- }
-
- c = NextNonCFWS(parser);
- if (':' == c) {
- /* we have a group */
- ++parser->p;
-
- if (-1 == ParseMailboxList(parser, header)) {
- return -1;
- }
- c = NextNonCFWS(parser);
- if (';' == c) {
- ++parser->p;
- return 0;
- }
- return -1;
- } else if ('@' == c) {
- int len;
- addr.displayName = NULL;
- len = strlen(phrase);
- phrase[len++] = '@';
- ++parser->p;
- if (-1 == ParseDomain(parser, phrase + len, sizeof(phrase) - len)) {
- return -1;
- }
- addr.address = phrase;
- if (parser->participantscb) {
- parser->participantscb(parser->cbarg, header, &addr);
- }
-
- return 1;
- } else if ('<' == c) {
- char tmp[1024];
- memcpy(tmp, phrase, sizeof(tmp));
- addr.displayName = tmp;
- if (-1 == ParseAngleAddr(parser, phrase, sizeof(phrase))) {
- return -1;
- }
- addr.address = phrase;
- if (parser->participantscb) {
- parser->participantscb(parser->cbarg, header, &addr);
- }
-
- return 1;
- } else {
- return -1;
- }
- }
-}
-
-
-static int
-ParseAddressList(MailParser *parser,
- MailHeaderName header)
-{
- /* (address *("," address)) */
- int pcode;
-
- pcode = ParseAddress(parser, header);
- if (-1 == pcode) {
- return -1;
- }
- while (1) {
- char c = NextNonCFWS(parser);
- if (',' == c) {
- ++parser->p;
- return ParseAddressList(parser, header);
- } else {
- return 0;
- }
- }
-}
-
-static int
-ParseUnstructuredHeader(MailParser *parser, MailHeaderName header)
-{
- char buffer[2048];
- int c;
-
- c = NextNonCFWS(parser);
-
- if (0 == c || '\n' == c) {
- return 0;
- }
- if (-1 == ParseUnstructured(parser, buffer, sizeof(buffer))) {
- return -1;
- }
-
- if (parser->unstructuredcb) {
- parser->unstructuredcb(parser->cbarg, header, parser->headerName, buffer);
- }
- return 1;
-}
-
-static int
-ParseDateHeader(MailParser *parser, MailHeaderName header)
-{
- char buffer[2048];
- int c;
- uint64_t date;
-
- c = NextNonCFWS(parser);
-
- if (':' == c) {
- /* forgive Groupwise spam filter messages */
- ++parser->p;
- c = NextNonCFWS(parser);
- }
-
-
- if (0 == c || '\n' == c) {
- return 0;
- }
-
- if (-1 == ParseUnstructured(parser, buffer, sizeof(buffer))) {
- return -1;
- }
-
- date = MsgParseRFC822DateTime(buffer);
-
- if (0 == date) {
- return 0;
- }
-
- if (parser->datecb) {
- parser->datecb(parser->cbarg, header, date);
- }
-
- return 1;
-}
-
-static int
-ParseIdLeft(MailParser *parser, char *buf, int len)
-{
- return ParseDotAtom(parser, buf, len);
-}
-
-static int
-ParseIdRight(MailParser *parser, char *buf, int len)
-{
- return ParseDotAtom(parser, buf, len);
-}
-
-static int
-ParseAngleMessageId(MailParser *parser, char *buf, int len)
-{
- int cnt;
- int pcode;
-
- ++parser->p;
-
- cnt = pcode = ParseIdLeft(parser, buf, len - 1);
- if (-1 == pcode) {
- return -1;
- }
- if ('@' != NextNonCFWS(parser)) {
- return -1;
- }
- ++parser->p;
- buf[cnt++] = '@';
- cnt += pcode = ParseIdRight(parser, buf + cnt, len - cnt);
- if (-1 == pcode) {
- return -1;
- }
-
- if ('>' != NextNonCFWS(parser)) {
- return -1;
- }
- ++parser->p;
-
- return cnt;
-}
-
-static int
-ParseMessageId(MailParser *parser,
- MailHeaderName header)
-{
- /* mailbox / group */
-
- char c;
- char phrase[1024];
-
- c = NextNonCFWS(parser);
-
- if ('\n' == c || ';' == c || 0 == c) {
- return 0;
- } else if ('<' == c) {
- int length;
-
- phrase[0] = '<';
- length = ParseAngleMessageId(parser, phrase + 1, sizeof(phrase) - 1);
- if (length == -1 || (length - 1) >= (int)sizeof(phrase)) {
- return -1;
- }
- phrase[length + 1] = '>';
- phrase[length + 2] = '\0';
-
- if (parser->messageidcb) {
- parser->messageidcb(parser->cbarg, header, phrase);
- }
-
- return 1;
- }
-
- return 0;
-}
-
-static int
-ParseInReplyTo(MailParser *parser,
- MailHeaderName header)
-{
- /* Grab the first and only the first msg id */
- int ret = 0;
- char c;
-
- c = NextNonCFWS(parser);
-
- while (ret >= 0 && !('\n' == c || 0 == c)) {
- if ('<' == c && ret != 1) {
- ret = ParseMessageId(parser, header);
- } else {
- parser->p++;
- c = NextNonCFWS(parser);
- }
- }
-
- return ret;
-}
-
-static int
-ParseMessageIdList(MailParser *parser,
- MailHeaderName header)
-{
- /* (msg-id *(msg-id)) */
- int pcode;
-
- pcode = ParseMessageId(parser, header);
-
- while (pcode == 1) {
- pcode = ParseMessageId(parser, header);
- }
-
- return pcode;
-}
-
-static int
-ParseHeader(MailParser *parser,
- MailHeaderName header)
-{
- int ret;
-
- switch (header) {
- case MAIL_FROM:
- return ParseMailboxList(parser, header);
- case MAIL_SENDER:
- case MAIL_X_AUTH_OK:
- case MAIL_X_BEEN_THERE:
- case MAIL_X_LOOP:
- ret = ParseMailbox(parser, header);
- /* FIXME: this is awkward, and sometimes shoots us into the message body */
- NextNonCFWS(parser);
- return ret;
- case MAIL_REPLY_TO:
- case MAIL_TO:
- case MAIL_CC:
- case MAIL_BCC:
- return ParseAddressList(parser, header);
- case MAIL_DATE:
- return ParseDateHeader(parser, header);
- case MAIL_SPAM_FLAG:
- case MAIL_SUBJECT:
- case MAIL_LIST_ID:
- case MAIL_OPTIONAL:
- return ParseUnstructuredHeader(parser, header);
- case MAIL_MESSAGE_ID:
- ret = ParseMessageId(parser, header);
- NextNonCFWS(parser);
- return ret;
- case MAIL_IN_REPLY_TO:
- return ParseInReplyTo(parser, header);
- case MAIL_REFERENCES:
- return ParseMessageIdList(parser, header);
- case MAIL_OFFSET_BODY_START:
- return 1;
- }
-
- return -1;
-}
-
-int
-MailParseHeaders(MailParser *parser)
-{
- char *q;
-
- MailHeaderName header;
-
- if (!fgets(parser->line, 2048, parser->stream)) {
- return -1;
- }
- parser->p = parser->line;
-
- while (1) {
- if (!*parser->p) {
- /* early EOF, this is an error */
- break;
- } else if ('\r' == *parser->p && '\n' == *(parser->p + 1)) {
- break;
- } else if ('\n' == *parser->p) {
- break;
- }
-
- q = strchr(parser->p, ':');
- if (!q) {
- /* we are probably at the beginning of the message body */
- break;
- }
-
- *q = 0;
-
- header = MAIL_OPTIONAL;
-
- switch (parser->p[0]) {
- case 'B': case 'b':
- if (!XplStrCaseCmp(parser->p, "Bcc")) {
- header = MAIL_BCC;
- }
- break;
-
- case 'C': case 'c':
- if (!XplStrCaseCmp(parser->p, "Cc")) {
- header = MAIL_CC;
- }
- break;
-
- case 'D': case 'd':
- if (!XplStrCaseCmp(parser->p, "Date")) {
- header = MAIL_DATE;
- }
- break;
-
- case 'F': case 'f':
- if (!XplStrCaseCmp(parser->p, "From")) {
- header = MAIL_FROM;
- }
- break;
-
- case 'L': case 'l':
- if (!XplStrCaseCmp(parser->p, "List-Id")) {
- header = MAIL_LIST_ID;
- }
- break;
-
- case 'M': case 'm':
- if (!XplStrCaseCmp(parser->p, "Message-Id")) {
- header = MAIL_MESSAGE_ID;
- }
- break;
-
- case 'I': case 'i':
- if (!XplStrCaseCmp(parser->p, "In-Reply-To")) {
- header = MAIL_IN_REPLY_TO;
- }
- break;
-
- case 'R': case 'r':
- if (!XplStrCaseCmp(parser->p, "Reply-to")) {
- header = MAIL_REPLY_TO;
- } else if (!XplStrCaseCmp(parser->p, "References")) {
- header = MAIL_REFERENCES;
- }
- break;
-
- case 'S': case 's':
- if (!XplStrCaseCmp(parser->p, "Sender")) {
- header = MAIL_SENDER;
- } else if (!XplStrCaseCmp(parser->p, "Subject")) {
- BongoStrNCpy(parser->headerName, parser->p, sizeof(parser->headerName));
- header = MAIL_SUBJECT;
- }
- break;
-
- case 'T': case 't':
- if (!XplStrCaseCmp(parser->p, "To")) {
- header = MAIL_TO;
- }
- break;
-
- case 'X': case 'x':
- if (parser->p[1] != '-') {
- break;
- }
-
- /* there are a lot of X- headers so switch on these as well */
- switch (parser->p[2]) {
- case 'A': case 'a':
- if (!XplStrCaseCmp(parser->p, "X-Auth-OK")) {
- header = MAIL_X_AUTH_OK;
- }
- break;
-
- case 'B': case 'b':
- if (!XplStrCaseCmp(parser->p, "X-BeenThere")) {
- header = MAIL_X_BEEN_THERE;
- }
- break;
-
- case 'H': case 'h':
- if (!XplStrCaseCmp(parser->p, "X-Bongo-Ipecac")) {
- return -1;
- }
- break;
-
- case 'L': case 'l':
- if (!XplStrCaseCmp(parser->p, "X-Loop")) {
- header = MAIL_X_LOOP;
- }
- break;
-
- case 'S': case 's':
- if (!XplStrCaseCmp(parser->p, "X-Spam-Flag")) {
- BongoStrNCpy(parser->headerName, parser->p, sizeof(parser->headerName));
- header = MAIL_SPAM_FLAG;
- }
- break;
- }
- break;
- }
-
- if (header == MAIL_OPTIONAL) {
- BongoStrNCpy(parser->headerName, parser->p, sizeof(parser->headerName));
- }
-
- *q = ':';
- parser->p = q + 1;
-
- if (-1 == ParseHeader(parser, header)) {
- Log(LOG_ERROR, "Parsing headers, didn't understand this: %d: %s", header, q + 1);
- if (parser->p != parser->line) {
- /* nevertheless, we've got to soldier on */
- NEXTLINE(0);
- }
- }
- }
-
- if (parser->offsetcb) {
- parser->offsetcb(parser->cbarg,
- MAIL_OFFSET_BODY_START, ftell(parser->stream));
- }
- return 0;
-}
-
-int
-MailParseBody(MailParser *parser)
-{
- return 0;
-}
-
-#if 0
-static void
-Participants(void *arg,
- MailHeaderName header,
- MailAddress *addr)
-{
-
- fprintf (stderr, "[%s] (%s) <%s>\r\n",
- MAIL_FROM == header ? "From" :
- MAIL_SENDER == header ? "Sender" :
- MAIL_REPLY_TO == header ? "Reply-to" :
- MAIL_TO == header ? "To" :
- MAIL_CC == header ? "Cc" :
- MAIL_BCC == header ? "Bcc" :
- "Unknown",
- addr->displayName,
- addr->address);
-
-}
-#endif
-
-MailParser *
-MailParserInit(FILE *stream, void *cbData)
-{
- MailParser *p = MemMalloc0(sizeof(MailParser));
-
- p->stream = stream;
- p->cbarg = cbData;
-
- return p;
-
-}
-
-void
-MailParserDestroy(MailParser *p)
-{
- MemFree(p);
-}
-
-void
-MailParserSetParticipantsCallback(MailParser *p,
- MailParserParticipantsCallback cbfunc)
-{
- p->participantscb = cbfunc;
-}
-
-void
-MailParserSetUnstructuredCallback(MailParser *p,
- MailParserUnstructuredCallback cbfunc)
-{
- p->unstructuredcb = cbfunc;
-}
-
-void
-MailParserSetDateCallback(MailParser *p,
- MailParserDateCallback cbfunc)
-{
- p->datecb = cbfunc;
-}
-
-void
-MailParserSetOffsetCallback(MailParser *p,
- MailParserOffsetCallback cbfunc)
-{
- p->offsetcb = cbfunc;
-}
-
-void
-MailParserSetMessageIdCallback(MailParser *p,
- MailParserMessageIdCallback cbfunc)
-{
- p->messageidcb = cbfunc;
-}
-
-
-
diff --git a/src/agents/store/mail-parser.h b/src/agents/store/mail-parser.h
deleted file mode 100644
index cc1c6ff..0000000
--- a/src/agents/store/mail-parser.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/****************************************************************************
- *
- * Copyright (c) 2005-6 Novell, Inc. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public License
- * as published by the Free Software Foundation.
- *
- * 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, contact Novell, Inc.
- *
- * To contact Novell about this file by physical or electronic mail, you
- * may find current contact information at www.novell.com.
- *
- ****************************************************************************/
-
-#ifndef MAIL_PARSER_H
-#define MAIL_PARSER_H
-
-#include "stored.h"
-
-XPL_BEGIN_C_LINKAGE
-
-#define XPL_MAX_RFC822_LINE 1000
-
-typedef enum {
- MAIL_OPTIONAL = 0,
-
- MAIL_FROM = 1 << 1,
- MAIL_SENDER = 1 << 2,
- MAIL_REPLY_TO = 1 << 3,
- MAIL_TO = 1 << 4,
- MAIL_CC = 1 << 5,
- MAIL_BCC = 1 << 6,
-
- MAIL_DATE = 1 << 7,
- MAIL_SUBJECT = 1 << 8,
- MAIL_SPAM_FLAG = 1 << 9,
-
- MAIL_MESSAGE_ID = 1 << 10,
- MAIL_IN_REPLY_TO = 1 << 11,
- MAIL_REFERENCES = 1 << 12,
- MAIL_OFFSET_BODY_START = 1 << 13,
- MAIL_X_AUTH_OK = 1 << 14,
-
- MAIL_X_BEEN_THERE = 1 << 15,
- MAIL_X_LOOP = 1 << 16,
- MAIL_LIST_ID = 1 << 17,
-} MailHeaderName;
-
-typedef struct MailAddress {
- char *displayName;
- char *address;
-/* struct MailAddress *next; */
-} MailAddress;
-
-typedef struct MailParser MailParser;
-
-typedef void (*MailParserParticipantsCallback)(void *cbdata, MailHeaderName name, MailAddress *address);
-typedef void (*MailParserUnstructuredCallback)(void *cbdata, MailHeaderName name, const char *nameRaw, const char *data);
-typedef void (*MailParserMessageIdCallback)(void *cbdata, MailHeaderName name, const char *data);
-typedef void (*MailParserDateCallback)(void *cbdata, MailHeaderName name, uint64_t date);
-typedef void (*MailParserOffsetCallback)(void *cbdata, MailHeaderName name, uint64_t offset);
-
-MailParser *MailParserInit(FILE *stream, void *cbData);
-void MailParserDestroy(MailParser *p);
-
-void MailParserSetParticipantsCallback(MailParser *p,
- MailParserParticipantsCallback cbfunc);
-
-void MailParserSetUnstructuredCallback(MailParser *p,
- MailParserUnstructuredCallback cbfunc);
-void MailParserSetDateCallback(MailParser *p,
- MailParserDateCallback cbfunc);
-void MailParserSetOffsetCallback(MailParser *p,
- MailParserOffsetCallback cbfunc);
-void MailParserSetMessageIdCallback(MailParser *p,
- MailParserMessageIdCallback cbfunc);
-
-int MailParseHeaders(MailParser *parser);
-int MailParseBody(MailParser *parser);
-
-XPL_END_C_LINKAGE
-
-#endif
diff --git a/src/agents/store/mail.c b/src/agents/store/mail.c
index 2152bb3..9f55eaf 100644
--- a/src/agents/store/mail.c
+++ b/src/agents/store/mail.c
@@ -1,237 +1,19 @@
-/****************************************************************************
- *
- * Copyright (c) 2005-6 Novell, Inc. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public License
- * as published by the Free Software Foundation.
- *
- * 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, contact Novell, Inc.
- *
- * To contact Novell about this file by physical or electronic mail, you
- * may find current contact information at www.novell.com.
- *
- ****************************************************************************/
-
-/* Routines for parsing and manipulating mail messages */
+// parse incoming mail messages using GMime
#include
#include "stored.h"
+#include
+
+#include
+#include
+
#include
#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
#include "mail.h"
-// #include "conversations.h"
-#include "mail-parser.h"
#include "messages.h"
-typedef struct {
- StoreObject *document;
-
- BongoStringBuilder from;
- BongoStringBuilder sender;
- BongoStringBuilder to;
- BongoStringBuilder cc;
- BongoStringBuilder bcc;
- BongoStringBuilder xAuthOK;
- BongoStringBuilder references;
-
- char *subject;
- char *messageId;
- char *parentMessageId;
- char *inReplyTo;
- char *listId;
-
- BOOL haveListId;
-
- uint64_t headerStartOffset;
- uint64_t headerSize;
-} IncomingParseData;
-
-static void
-IncomingParticipantCb(void *datap, MailHeaderName name, MailAddress *address)
-{
- IncomingParseData *data = datap;
- const char *type = NULL;
- char buf[1024];
- snprintf(buf, 1024, "%s%s<%s>\n",
- address->displayName ? address->displayName : "",
- address->displayName ? " " : "",
- address->address);
-
- switch(name) {
- case MAIL_FROM:
- BongoStringBuilderAppend(&data->from, buf);
- type = "from";
- break;
- case MAIL_SENDER:
- BongoStringBuilderAppend(&data->sender, buf);
- type = "sender";
-
- /* For listservs that don't set a good listid, we use the
- * sender. Save the sender as the listid if no listid
- * has been saved. */
- if (!data->listId) {
- snprintf(buf, 1024, "<%s>", address->address);
- data->listId = MemStrdup(buf);
- }
-
- break;
- case MAIL_TO:
- BongoStringBuilderAppend(&data->to, buf);
- type = "to";
- break;
- case MAIL_CC:
- BongoStringBuilderAppend(&data->cc, buf);
- type = "cc";
- break;
- case MAIL_BCC:
- BongoStringBuilderAppend(&data->bcc, buf);
- type = "bcc";
- break;
- case MAIL_X_AUTH_OK:
- BongoStringBuilderAppend(&data->xAuthOK, buf);
- type = "xAuthOK";
- break;
- case MAIL_X_BEEN_THERE:
- case MAIL_X_LOOP :
- snprintf(buf, 1024, "<%s>", address->address);
-
- data->haveListId = TRUE;
-
- if (data->listId) {
- MemFree(data->listId);
- }
- data->listId = MemStrdup(buf);
- break;
- default :
- /* Ignore */
- return;
- }
-
- Log(LOG_DEBUG, "IPCB got %s %s <%s>\n", type, address->displayName, address->address);
-}
-
-static void
-IncomingUnstructuredCb(void *datap, MailHeaderName name, const char *headerName, const char *buffer)
-{
- IncomingParseData *data = datap;
-
- switch(name) {
- case MAIL_SUBJECT:
- data->subject = MemStrdup(buffer);
- break;
- case MAIL_SPAM_FLAG :
- if (tolower(buffer[0]) == 'y') {
- data->document->type |= STORE_MSG_FLAG_JUNK;
- }
- break;
- case MAIL_LIST_ID:
- /* If no proper listid is given, the sender will be saved
- * in the listid */
- data->haveListId = TRUE;
- break;
- default:
- if (!XplStrCaseCmp(headerName, "X-Listprocessor-Version")) {
- /* If no proper listid is given, the sender will be saved
- * in the listid */
- data->haveListId = TRUE;
- }
- break;
- }
-}
-
-static void
-IncomingDateCb(void *datap, MailHeaderName name, uint64_t date)
-{
- // IncomingParseData *data = datap;
-
- switch(name) {
- case MAIL_DATE:
- // FIXME
- // data->info->data.mail.sent = date;
- break;
- default :
- /* Ignore */
- return;
- }
-}
-
-static void
-IncomingOffsetCb(void *datap, MailHeaderName name, uint64_t offset)
-{
- IncomingParseData *data = datap;
-
- switch(name) {
- case MAIL_OFFSET_BODY_START:
- data->headerSize = offset - data->headerStartOffset;
- break;
- default :
- /* Ignore */
- return;
- }
-}
-
-static void
-IncomingMessageIdCb(void *datap, MailHeaderName name, const char *messageId)
-{
- IncomingParseData *data = datap;
-
- switch(name) {
- case MAIL_MESSAGE_ID:
- if (!data->messageId) {
- data->messageId = MemStrdup(messageId);
- }
- break;
- case MAIL_IN_REPLY_TO :
- if (!data->inReplyTo) {
- data->inReplyTo = MemStrdup(messageId);
-
- MemFree(data->parentMessageId);
- data->parentMessageId = MemStrdup(messageId);
- }
-
- break;
- case MAIL_REFERENCES :
- if (data->references.len > 0) {
- BongoStringBuilderAppend(&data->references, ", ");
- }
-
- BongoStringBuilderAppend(&data->references, messageId);
-
- if (!data->inReplyTo) {
- /* If In-Reply-To isn't set, use the last References:
- * messageId as the parent */
- MemFree(data->parentMessageId);
- data->parentMessageId = MemStrdup(messageId);
- }
-
- break;
- default :
- /* Ignore */
- return;
- }
-}
-
-
-/* returns: NULL on success
- errormsg o/w
-*/
-
static void
SetDocProp(StoreClient *client, StoreObject *doc, char *pname, char *value)
{
@@ -246,66 +28,90 @@ SetDocProp(StoreClient *client, StoreObject *doc, char *pname, char *value)
}
}
+struct wanted_header {
+ const char const *header;
+ const char const *propname;
+};
+
+// properties we want to look for and set automatically
+static struct wanted_header header_list[] = {
+ { "From", "bongo.from" },
+ { "To", "bongo.to" },
+ { "CC", "bongo.cc" },
+ { "Sender", "bongo.sender" },
+ { "In-Reply-To", "bongo.inreplyto" },
+ { "References", "bongo.references" },
+ { "X-Auth-OK", "bongo.xauthok" },
+ { "List-Id", "bongo.listid" },
+ { "Subject", "bongo.subject" },
+ { NULL, NULL }
+};
+
const char *
StoreProcessIncomingMail(StoreClient *client,
StoreObject *document,
const char *path)
{
- const char *result = NULL;
- MailParser *p;
- IncomingParseData data = {0, };
- char prop[XPL_MAX_PATH + 1];
+ GMimeMessage *message;
+ GMimeParser *parser;
+ GMimeStream *stream;
StoreObject conversation;
StoreConversationData conversation_data;
-
- FILE *fh;
-
- fh = fopen(path, "rb");
- if (!fh) return MSG4224CANTREADMBOX;
-
- data.headerStartOffset = 0;
- data.document = document;
-
- p = MailParserInit(fh, &data);
- MailParserSetParticipantsCallback(p, IncomingParticipantCb);
- MailParserSetUnstructuredCallback(p, IncomingUnstructuredCb);
- MailParserSetDateCallback(p, IncomingDateCb);
- MailParserSetOffsetCallback(p, IncomingOffsetCb);
- MailParserSetMessageIdCallback(p, IncomingMessageIdCb);
-
- if (MailParseHeaders(p) == -1) {
- result = MSG4226BADEMAIL;
- goto finish;
- }
-
- SetDocProp(client, document, "bongo.from", data.from.value);
- SetDocProp(client, document, "bongo.to", data.to.value);
- SetDocProp(client, document, "bongo.sender", data.sender.value);
- SetDocProp(client, document, "bongo.cc", data.cc.value);
- SetDocProp(client, document, "bongo.inreplyto", data.inReplyTo);
- SetDocProp(client, document, "bongo.references", data.references.value);
- SetDocProp(client, document, "bongo.xauthok", data.xAuthOK.value);
- SetDocProp(client, document, "bongo.listid", data.listId);
-
- if (data.messageId) {
- SetDocProp(client, document, "nmap.mail.messageid", data.messageId);
- } else {
- snprintf(prop, XPL_MAX_PATH, "%u." GUID_FMT "@%s", document->time_created, document->guid,
- StoreAgent.agent.officialName);
- SetDocProp(client, document, "nmap.mail.messageid", prop);
- }
-
- SetDocProp(client, document, "nmap.mail.parentmessageid", data.parentMessageId);
- SetDocProp(client, document, "nmap.mail.subject", data.subject);
-
- if (data.headerSize > 0) {
- snprintf(prop, XPL_MAX_PATH, FMT_UINT64_DEC, data.headerSize);
- SetDocProp(client, document, "nmap.mail.headersize", prop);
- }
+ struct wanted_header *headers = header_list;
+ char prop[XPL_MAX_PATH+1];
+ char *result = NULL;
+ char *header_str = NULL;
+ int fd;
memset(&conversation_data, 0, sizeof(StoreConversationData));
memset(&conversation, 0, sizeof(conversation));
- conversation_data.subject = MemStrdup(data.subject);
+
+ // open up the mail
+ fd = open(path, O_RDONLY);
+ if (fd == -1) return MSG4224CANTREADMBOX;
+
+ stream = g_mime_stream_fs_new(fd);
+ parser = g_mime_parser_new_with_stream(stream);
+ g_mime_parser_set_scan_from (parser, FALSE);
+ g_object_unref(stream);
+ message = g_mime_parser_construct_message(parser);
+ g_object_unref(parser);
+
+ if (message == NULL) {
+ // message didn't parse. What should we do?
+ goto finish;
+ }
+
+ while (headers->header != NULL) {
+ const char *value = g_mime_object_get_header(GMIME_OBJECT(message), headers->header);
+
+ if (value != NULL)
+ SetDocProp(client, document, (char *)headers->propname, (char *)value);
+
+ headers++;
+ }
+
+ // treat message ID specially because we want to invent one if it
+ // doesn't already exist.
+ if (message->message_id) {
+ SetDocProp(client, document, "nmap.mail.messageid", message->message_id);
+ } else {
+ snprintf(prop, XPL_MAX_PATH, "%u." GUID_FMT "@%s", document->time_created, document->guid,
+ StoreAgent.agent.officialName);
+ prop[XPL_MAX_PATH] = '\0';
+ SetDocProp(client, document, "nmap.mail.messageid", prop);
+ }
+
+ header_str = g_mime_object_get_headers(GMIME_OBJECT(message));
+
+ if (header_str != NULL) {
+ snprintf(prop, XPL_MAX_PATH, FMT_UINT64_DEC, strlen(header_str));
+ SetDocProp(client, document, "nmap.mail.headersize", prop);
+ g_free(header_str);
+ }
+
+ // now, see if we can find a matching conversation
+ conversation_data.subject = MemStrdup(message->subject);
conversation_data.date = 0; // FIXME: should be 7 days ago or similar
if (StoreObjectFindConversation(client, &conversation_data, &conversation) != 0) {
@@ -349,26 +155,11 @@ StoreProcessIncomingMail(StoreClient *client,
goto finish;
}
- // fix up conversation meta-data?
- // TODO
-
finish:
- if (conversation_data.subject) MemFree(conversation_data.subject);
- MemFree(data.from.value);
- MemFree(data.to.value);
- MemFree(data.cc.value);
- MemFree(data.sender.value);
- MemFree(data.messageId);
- MemFree(data.inReplyTo);
- MemFree(data.references.value);
- MemFree(data.parentMessageId);
- MemFree(data.xAuthOK.value);
- MemFree(data.listId);
-
- MailParserDestroy(p);
-
- fclose(fh);
-
+ if (conversation_data.subject != NULL) MemFree(conversation_data.subject);
+ g_object_unref(message);
+ close(fd);
+
return result;
}
diff --git a/src/agents/store/stored.c b/src/agents/store/stored.c
index 4602285..3a09ca5 100644
--- a/src/agents/store/stored.c
+++ b/src/agents/store/stored.c
@@ -23,6 +23,7 @@
#include "stored.h"
#include "messages.h"
+#include
#include
#include
#include
@@ -232,6 +233,9 @@ _XplServiceMain(int argc, char *argv[])
Log(LOG_FATAL, "Couldn't initialize calendaring library");
return -1;
}
+
+ // initialize the mail parsing code
+ g_mime_init(0);
// create lock pool
StoreInitializeFairLocks();
@@ -295,5 +299,7 @@ _XplServiceMain(int argc, char *argv[])
XplUnloadApp(XplGetThreadID());
MsgClearRecoveryFlag("store");
+ g_mime_shutdown();
+
return 0;
}