Commit 4f1e5ada authored by Kim Vandry's avatar Kim Vandry

Added code for IMAP feature

parent b47025b2
......@@ -50,7 +50,7 @@ docdir=$(DESTDIR)@docdir@
OBJ = mairix.o db.o rfc822.o tok.o hash.o dirscan.o writer.o \
reader.o search.o stats.o dates.o datescan.o mbox.o md5.o \
fromcheck.o glob.o dumper.o expandstr.o dotlock.o \
nvp.o nvpscan.o
nvp.o nvpscan.o imap.o imapinterface.o
all : mairix
......@@ -73,6 +73,9 @@ dates.o : datescan.h
mbox.o : fromcheck.h
nvp.o : nvpscan.h
imap.o : imap.c imap.h
imapinterface.o : imapinterface.c imapinterface.h imap.h
version.h:
./mkversion
......
......@@ -181,6 +181,30 @@ test_for_flex () {
}
#}}}
#{{{ test_for_openssl
test_for_openssl () {
cat > docheck.c <<EOF;
#include <openssl/ssl.h>
int main () {
SSL_load_error_strings();
SSL_library_init();
return 0;
}
EOF
echo "Test program is" 1>&5
cat docheck.c 1>&5
${MYCC} ${MYCPPFLAGS} ${MYCFLAGS} ${MYLDFLAGS} -o docheck docheck.c -lssl -lcrypto 1>&5 2>&1
if [ $? -eq 0 ]
then
result=0
else
result=1
fi
rm -f docheck.c docheck
echo $result
}
#}}}
#{{{ usage
usage () {
cat <<EOF;
......@@ -343,6 +367,15 @@ else
exit 1;
fi
printf "Checking for OpenSSL : "
if [ `test_for_openssl` -eq 0 ]; then
printf "Yes\n";
DEFS="${DEFS} -DUSE_OPENSSL"
LIBS="${LIBS} -lssl -lcrypto"
else
printf "No (disabled IMAP STARTTLS support)\n";
fi
#{{{ Determine version number of the program.
if [ -f version.txt ]; then
revision=`cat version.txt`
......
This diff is collapsed.
#ifndef __IMAPLL_H__
#define __IMAPLL_H__
#ifdef USE_OPENSSL
#include <openssl/ssl.h>
#endif
#include <poll.h>
enum imap_ll_tltype {
TLTYPE_UNTAGGED = 1,
TLTYPE_TAGGED = 2,
TLTYPE_LIST = 3,
TLTYPE_SQLIST = 4,
TLTYPE_ATOM = 5,
TLTYPE_STRING = 6,
TLTYPE_CONTINUATION = 7,
/* the following only for imap_ll_build */
TLTYPE_END = 100,
TLTYPE_POP = 101,
TLTYPE_SUB = 102
};
struct imap_ll_tokenlist {
enum imap_ll_tltype type;
char *leaf;
size_t leaflen;
struct imap_ll_tokenlist *parent;
struct imap_ll_tokenlist *next;
/* children */
struct imap_ll_tokenlist *first;
struct imap_ll_tokenlist *last;
};
struct imap_ll *imap_ll_connect(const char *host, const char *port);
struct imap_ll *imap_ll_pipe_connect(const char *command);
void imap_ll_timeout(struct imap_ll *, int seconds);
struct imap_ll_tokenlist *imap_ll_waitline(struct imap_ll *);
void imap_ll_freeline(struct imap_ll_tokenlist *);
struct imap_ll_tokenlist *imap_ll_build(enum imap_ll_tltype maintype, ...);
void imap_ll_append(struct imap_ll_tokenlist *, struct imap_ll_tokenlist *);
void imap_ll_pprint(struct imap_ll_tokenlist *, int indent, FILE *);
struct imap_ll_tokenlist *imap_ll_command(struct imap_ll *, struct imap_ll_tokenlist *, int timeout);
const char *imap_ll_status(struct imap_ll_tokenlist *);
#ifdef USE_OPENSSL
enum imap_ll_starttls_result {
IMAP_LL_STARTTLS_FAILED_PROCEED, /* STARTTLS failed but session still OK */
IMAP_LL_STARTTLS_FAILED, /* session must be closed */
IMAP_LL_STARTTLS_FAILED_CERT, /* certificate problem (session must be closed) */
IMAP_LL_STARTTLS_SUCCESS /* certificate problem (session must be closed) */
};
enum imap_ll_starttls_result imap_ll_starttls(struct imap_ll *, SSL_CTX *, const char *servername);
#endif
void imap_ll_logout(struct imap_ll *);
enum imap_login_result {
imap_login_ok = 0,
imap_login_denied = 1,
imap_login_error = 2
};
enum imap_login_result
imap_login(
struct imap_ll *,
const char *username, size_t username_len,
const char *password, size_t password_len
#ifdef USE_OPENSSL
, SSL_CTX *, const char *servername
#endif
);
/* returns the uidvalidity of the opened folder if successful */
const char *imap_select(
struct imap_ll *,
const char *foldername, size_t foldername_len,
int need_write,
long *exists_p /* optional, return number of messages which exist */
);
#endif
#include <stdio.h>
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include "imapinterface.h"
#include "imap.h"
#include "mairix.h"
struct imap_ll *
imap_start(const char *pipe, const char *server, const char *username, const char *password)
{
#ifdef USE_OPENSSL
SSL_CTX *sslctx;
#endif
struct imap_ll *imapc;
#ifdef USE_OPENSSL
SSL_load_error_strings();
SSL_library_init();
sslctx = SSL_CTX_new(SSLv23_client_method());
if (!sslctx) {
fprintf(stderr, "SSL_CTX_new failed\n");
ERR_print_errors_fp(stderr);
return NULL;
}
SSL_CTX_load_verify_locations(sslctx, NULL, "/etc/ssl/certs");
SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER, NULL);
#endif
imapc = pipe ? imap_ll_pipe_connect(pipe) : imap_ll_connect(server, "143");
if (!imapc) return NULL;
switch (imap_login(
imapc, username, -1, password, -1
#ifdef USE_OPENSSL
, sslctx, server
#endif
)) {
case imap_login_error:
case imap_login_denied:
return NULL;
case imap_login_ok:
break;
}
return imapc;
}
static void
add_imap_message_to_list(const char *folder, const char *uidvalidity, const char *uid, struct msgpath_array *arr)
{
char *pseudopath;
pseudopath = Malloc(strlen(folder) + strlen(uidvalidity) + strlen(uid) + 3);
sprintf(pseudopath, "%s:%s:%s", uidvalidity, uid, folder);
if (arr->n == arr->max) {
arr->max += 1024;
arr->paths = grow_array(struct msgpath, arr->max, arr->paths);
}
arr->paths[arr->n].type = MTY_IMAP;
arr->paths[arr->n].src.mpf.path = pseudopath;
++arr->n;
}
static void
scan_folder(struct imap_ll *imapc, const char *folder, struct msgpath_array *msgs)
{
struct imap_ll_tokenlist *cmd, *result, *l, *l2, *l3;
const char *uidvalidity;
char *uid;
int deleted;
long exists;
uidvalidity = imap_select(imapc, folder, -1, 0, &exists);
if (!uidvalidity) return;
if (exists < 1) return;
cmd = imap_ll_build(
TLTYPE_TAGGED,
TLTYPE_ATOM, "UID", (size_t)-1,
TLTYPE_ATOM, "FETCH", (size_t)-1,
TLTYPE_ATOM, "1:*", (size_t)-1,
TLTYPE_ATOM, "FLAGS", (size_t)-1,
TLTYPE_END
);
result = imap_ll_command(imapc, cmd, 40);
imap_ll_freeline(cmd);
if (!result) {
fprintf(stderr, "unable to FETCH in folder \"%s\": no response from IMAP server\n", folder);
return;
}
if (0 != strcmp(imap_ll_status(result), "OK")) {
fprintf(stderr, "unable to FETCH in folder \"%s\":\n", folder);
imap_ll_pprint(result, 0, stderr);
imap_ll_freeline(result);
return;
}
for (l = result->first; l != result->last; l = l->next) {
l2 = l->first;
if (!l2) continue;
if (l2->type != TLTYPE_ATOM) continue; /* sequence */
l2 = l2->next;
if (l2->type != TLTYPE_ATOM) continue;
if (0 != strcmp(l2->leaf, "FETCH")) continue;
l2 = l2->next;
if (l2->type != TLTYPE_LIST) continue;
uid = NULL;
deleted = 0;
for (l2 = l2->first; l2 && (l2->next); l2 = l2->next) {
if (l2->type != TLTYPE_ATOM) continue;
if (0 == strcmp(l2->leaf, "UID")) {
l2 = l2->next;
if (l2->type != TLTYPE_ATOM) continue;
uid = l2->leaf;
} else if (0 == strcmp(l2->leaf, "FLAGS")) {
l2 = l2->next;
if (l2->type != TLTYPE_LIST) continue;
for (l3 = l2->first; l3; l3 = l3->next) {
if (l3->type != TLTYPE_ATOM) continue;
if (0 == strcmp(l3->leaf, "\\Deleted")) {
deleted = 1;
}
}
}
}
if (deleted) continue;
if (!uid) continue;
add_imap_message_to_list(folder, uidvalidity, uid, msgs);
}
imap_ll_freeline(result);
}
void
build_imap_message_list(
const char *folders, struct msgpath_array *msgs,
struct globber_array *omit_globs, struct imap_ll *imapc
)
{
char **folderlist;
int n_folders;
int i;
split_on_colons(folders, &n_folders, &folderlist);
for (i = 0; i < n_folders; i++) {
if (!is_globber_array_match(omit_globs, folderlist[i])) {
scan_folder(imapc, folderlist[i], msgs);
}
}
}
int
imap_fetch_message_raw(
const char *pseudopath, struct imap_ll *imapc,
void (*callback)(const char *, size_t, void *), void *arg
)
{
struct imap_ll_tokenlist *cmd, *result, *l, *l2;
const char *uidvalidity, *uid, *folder, *p;
size_t uidvalidity_len;
size_t uid_len, raw_len, got_uid_len;
const char *actual_uidvalidity, *got_uid, *got_raw;
/* first part is the uidvalidity */
uidvalidity = pseudopath;
p = strchr(pseudopath, ':');
if (!p) return 0;
uidvalidity_len = p-pseudopath;
uid = p+1;
/* second part is the uid */
p = strchr(uid, ':');
if (!p) return 0;
uid_len = p-uid;
folder = p+1;
actual_uidvalidity = imap_select(imapc, folder, -1, 0, NULL);
if (!actual_uidvalidity) return 0;
if (
(strlen(actual_uidvalidity) != uidvalidity_len) ||
(0 != memcmp(uidvalidity, actual_uidvalidity, uidvalidity_len))
) {
fprintf(stderr, "IMAP message \"%s\" cannot be loaded because UIDVALIDITY changed\n", pseudopath);
return 0;
}
cmd = imap_ll_build(
TLTYPE_TAGGED,
TLTYPE_ATOM, "UID", (size_t)-1,
TLTYPE_ATOM, "FETCH", (size_t)-1,
TLTYPE_ATOM, uid, uid_len,
TLTYPE_ATOM, "BODY.PEEK[]", (size_t)-1,
TLTYPE_END
);
result = imap_ll_command(imapc, cmd, 40);
imap_ll_freeline(cmd);
if (!result) {
fprintf(stderr, "unable to FETCH message \"%s\": no response from IMAP server\n", pseudopath);
return 0;
}
if (0 != strcmp(imap_ll_status(result), "OK")) {
fprintf(stderr, "unable to FETCH message \"%s\":\n", pseudopath);
imap_ll_pprint(result, 0, stderr);
imap_ll_freeline(result);
return 0;
}
for (l = result->first; l != result->last; l = l->next) {
l2 = l->first;
if (!l2) continue;
if (l2->type != TLTYPE_ATOM) continue; /* sequence */
l2 = l2->next;
if (l2->type != TLTYPE_ATOM) continue;
if (0 != strcmp(l2->leaf, "FETCH")) continue;
l2 = l2->next;
if (l2->type != TLTYPE_LIST) continue;
got_uid = got_raw = NULL;
for (l2 = l2->first; l2 && (l2->next); l2 = l2->next) {
if (l2->type != TLTYPE_ATOM) continue;
if (0 == strcmp(l2->leaf, "UID")) {
l2 = l2->next;
if (l2->type != TLTYPE_ATOM) continue;
got_uid = l2->leaf;
got_uid_len = l2->leaflen;
} else if (0 == strcmp(l2->leaf, "BODY[]")) {
l2 = l2->next;
if (l2->type != TLTYPE_STRING) continue;
got_raw = l2->leaf;
raw_len = l2->leaflen;
}
}
if ((!got_uid) || (!got_raw)) continue;
if (got_uid_len != uid_len) continue;
if (0 != memcmp(got_uid, uid, got_uid_len)) continue;
callback(got_raw, raw_len, arg);
imap_ll_freeline(result);
return 1;
}
fprintf(stderr, "IMAP server did not return message \"%s\"\n", pseudopath);
imap_ll_freeline(result);
return 0;
}
struct make_rfc822_from_imap_s {
const char *pseudopath;
struct rfc822 *r;
};
static void
callback822(const char *data, size_t len, void *arg)
{
struct make_rfc822_from_imap_s *s = (struct make_rfc822_from_imap_s *)arg;
struct msg_src src; /* assuming this is for error message presentation only! */
src.type = MS_FILE;
src.filename = (char *)(s->pseudopath); /* XXX breaking "const" */
s->r = data_to_rfc822(&src, (char *)data /* XXX breaking "const" */, len, NULL);
}
struct rfc822 *
make_rfc822_from_imap(const char *pseudopath, struct imap_ll *imapc)
{
struct make_rfc822_from_imap_s s;
s.pseudopath = pseudopath;
imap_fetch_message_raw(pseudopath, imapc, callback822, &s);
return s.r;
}
#ifndef __IMAPMSG_H__
#define __IMAPMSG_H__
#include "mairix.h"
struct imap_ll;
/* set pipe OR server, not both */
struct imap_ll *
imap_start(const char *pipe, const char *server, const char *username, const char *password);
void
build_imap_message_list(const char *folders, struct msgpath_array *msgs, struct globber_array *omit_globs, struct imap_ll *);
/* returns 1 on success, 0 otherwise */
int imap_fetch_message_raw(
const char *pseudopath, struct imap_ll *imapc,
/* on success, calls this callback */
void (*callback)(const char *, size_t, void *), void *arg
/* after the callback returns, the pointer to the message
data is no longer valid. */
);
struct rfc822 *
make_rfc822_from_imap(const char *pseudopath, struct imap_ll *);
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment