summaryrefslogtreecommitdiffstats
path: root/src/imap.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/imap.c')
-rw-r--r--src/imap.c370
1 files changed, 370 insertions, 0 deletions
diff --git a/src/imap.c b/src/imap.c
new file mode 100644
index 0000000..ac492c2
--- /dev/null
+++ b/src/imap.c
@@ -0,0 +1,370 @@
+/* $Id$ */
+
+/***
+ This file is part of libnewmail
+
+ libnewmail is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ libnewmail 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 libnewmail; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA
+***/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <wait.h>
+
+#include "module.h"
+#include "newmail.h"
+#include "util.h"
+#include "md5.h"
+#include "sockwrap.h"
+
+struct forkresp {
+ struct nm_status status;
+ int ret;
+ int nm_errno;
+ int system_errno;
+ char nm_explanation[128];
+};
+
+struct data {
+ char *username;
+ char *password;
+
+ char *hostname;
+ int port;
+ int tls;
+
+ char *folder;
+
+ oop_source *oop;
+ int fdpipe;
+ struct forkresp forkresp;
+ int nread;
+
+ int debug;
+
+ nm_query_cb_t cb;
+ void *user;
+
+ pid_t child;
+};
+
+static int _imap_process(struct nm_spool *s, enum nm_query query, struct nm_status *status) {
+ struct sockwrap *sw = NULL;
+ struct data *data = (struct data*) s->data;
+ enum { HELLO, LOGIN, STATUS, LOGOUT } state = HELLO;
+ char *tag = "* ";
+ int r = -1;
+ static char star[256] = "";
+
+ status->cur = status->new = -1;
+
+ if (!(sw = sockwrap(data->hostname, data->port, data->tls)))
+ goto finish;
+
+ for (;;) {
+ int finished = 0;
+
+ static char response[256];
+ static char request[256];
+ char *p;
+
+ if (sockwrap_readln(sw, response, sizeof(response)) < 0) {
+ nm_error(NM_ERROR_SERVFAIL, NULL);
+ goto finish;
+ }
+
+ nm_chomp(response);
+
+ if (data->debug)
+ fprintf(stderr, "RECV: %s\n", response);
+
+ if (strncmp(response, tag, strlen(tag))) {
+
+ if (!strncmp(response, "* ", 2))
+ strcpy(star, response);
+
+ continue;
+ }
+
+ p = response + strlen(tag);
+
+ if (strncmp(p, "OK ", 3)) {
+ char *e;
+ if ((e = strchr(p, ' ')))
+ e++;
+ else
+ e = p;
+
+ nm_error(NM_ERROR_SERVFAIL|NM_ERROR_EXPLANATION, e);
+ goto finish;
+ }
+
+ switch (state) {
+ case HELLO:
+ state = LOGIN;
+ break;
+
+ case LOGIN:
+ state = STATUS;
+ star[0] = 0;
+ break;
+
+ case STATUS:
+
+ if (sscanf(star, "%*s %*s %*s %*s %d %*s %d", &status->cur, &status->new) != 2) {
+ status->cur = status->new = -1;
+ nm_error(NM_ERROR_SERVFAIL, NULL);
+ goto finish;
+ }
+
+ state = LOGOUT;
+ break;
+
+ case LOGOUT:
+ finished = 1;
+ break;
+ }
+
+ if (finished)
+ break;
+
+
+ switch (state) {
+ case LOGIN:
+ snprintf(request, sizeof(request), "%sLOGIN \"%s\" \"%s\"\n", tag = "A ", data->username, data->password);
+ break;
+
+ case STATUS:
+ snprintf(request, sizeof(request), "%sSTATUS \"%s\" (MESSAGES UNSEEN)\n", tag = "B ", data->folder);
+ break;
+
+ case LOGOUT:
+ snprintf(request, sizeof(request), "%sLOGOUT\n", tag = "C ");
+ break;
+
+ case HELLO: // Completely useless, however, this will remove GCC's warning
+ break;
+ }
+
+ if (data->debug)
+ fprintf(stderr, "SEND: %s", request);
+
+ if (sockwrap_writeln(sw, request) < 0) {
+ nm_error(NM_ERROR_SERVFAIL, NULL);
+ goto finish;
+ }
+ }
+
+
+ r = 0;
+
+finish:
+
+ if (sw)
+ sockwrap_close(sw);
+
+ return r;
+}
+
+static void* _callback(oop_source *source, int fd, oop_event event, void *user) {
+ struct data *data = (struct data*) ((struct nm_spool*) user)->data;
+ ssize_t r;
+
+ if ((r = read(data->fdpipe, ((char*) &data->forkresp) + data->nread, sizeof(struct forkresp)-data->nread)) < 0) {
+ nm_error(NM_ERROR_INTERNAL|NM_ERROR_SYSTEM, NULL);
+ goto fail;
+ }
+
+ data->nread += r;
+
+ if (data->nread >= sizeof(struct forkresp)) {
+
+ if (data->forkresp.nm_errno == NM_ERROR_SUCCESS) {
+ data->cb((struct nm_spool*) user, &data->forkresp.status, data->user);
+ goto finish;
+ } else {
+ nm_error(nm_errno, data->forkresp.nm_explanation[0] ? data->forkresp.nm_explanation : NULL);
+ errno = data->forkresp.system_errno;
+ goto fail;
+ }
+ }
+
+ return OOP_CONTINUE;
+
+fail:
+ data->cb((struct nm_spool*) user, NULL, data->user);
+
+finish:
+
+ if (data->oop)
+ data->oop->cancel_fd(data->oop, data->fdpipe, OOP_READ);
+
+ close(data->fdpipe);
+ data->fdpipe = -1;
+
+ waitpid(data->child, 0, 0);
+ data->child = (pid_t) -1;
+
+ return OOP_CONTINUE;
+}
+
+static int _check_submit(struct nm_spool *s, enum nm_query query, oop_source* oop, nm_query_cb_t cb, void *user) {
+ struct data *data = (struct data*) s->data;
+ int pf[2] = { -1, -1};
+ pid_t pid;
+
+ if (data->fdpipe >= 0) {
+ nm_error(NM_ERROR_ALREADY, NULL);
+ goto fail;
+ }
+
+ if (pipe(pf) < 0) {
+ nm_error(NM_ERROR_FORK|NM_ERROR_SYSTEM, NULL);
+ goto fail;
+ }
+
+ if ((pid = fork()) < 0) {
+ nm_error(NM_ERROR_FORK|NM_ERROR_SYSTEM, NULL);
+ goto fail;
+ } else if (!pid) {
+ struct forkresp forkresp = { { -1, -1 }, -1, 0, 0, "" };
+ FILE *f;
+
+ signal(SIGPIPE, SIG_IGN);
+ close(pf[0]);
+
+ nm_error(NM_ERROR_SUCCESS, NULL);
+
+ if ((forkresp.ret = _imap_process(s, query, &forkresp.status)) < 0) {
+ forkresp.system_errno = errno;
+ forkresp.nm_errno = nm_errno;
+ if (nm_explanation[0])
+ snprintf(forkresp.nm_explanation, sizeof(forkresp.nm_explanation), "%s", nm_explanation);
+ else
+ forkresp.nm_explanation[0] = 0;
+ }
+
+ f = fdopen(pf[1], "w");
+ fwrite(&forkresp, sizeof(forkresp), 1, f);
+ fclose(f);
+ close(pf[1]);
+
+ exit(0);
+ } else {
+ close(pf[1]);
+ data->fdpipe = pf[0];
+ data->nread = 0;
+
+ data->cb = cb;
+ data->user = user;
+ data->oop = oop;
+
+ data->child = pid;
+
+ oop->on_fd(oop, data->fdpipe, OOP_READ, _callback, s);
+ return 0;
+ }
+
+fail:
+
+ if (pf[0] >= 0)
+ close(pf[0]);
+ if (pf[1] >= 1)
+ close(pf[1]);
+
+ return -1;
+}
+
+static int _check(struct nm_spool *s, enum nm_query query, struct nm_status *status) {
+ struct data *data = (struct data*) s->data;
+
+ if (!s || !status) {
+ nm_error(NM_ERROR_INVPAR, NULL);
+ return -1;
+ }
+
+ if (data->fdpipe >= 0) {
+ nm_error(NM_ERROR_ALREADY, NULL);
+ return -1;
+ }
+
+ return _imap_process(s, query, status);
+}
+
+static int _configure(struct nm_spool *s) {
+ return -1;
+}
+
+static int _info(struct nm_spool *s, struct nm_info *i) {
+ struct data *data = (struct data*) s->data;
+ if (data->port == 143)
+ snprintf(i->text, sizeof(i->text), "IMAP mailbox %s@%s/%s", data->username, data->hostname, data->folder);
+ else
+ snprintf(i->text, sizeof(i->text), "IMAP mailbox %s@%s:%i/%s", data->username, data->hostname, data->port, data->folder);
+
+ i->flags = NM_FLAG_ASYNCHRONOUS;
+
+ return 0;
+}
+
+static void _done(struct nm_spool *s) {
+ struct data *data = (struct data*) s->data;
+
+ if (data) {
+ if (data->fdpipe >= 0) {
+ if (data->oop)
+ data->oop->cancel_fd(data->oop, data->fdpipe, OOP_READ);
+ close(data->fdpipe);
+ }
+
+ nm_free(data->hostname);
+ nm_free(data->username);
+ nm_free(data->password);
+ nm_free(data);
+ s->data = NULL;
+ }
+}
+
+int nm_init(struct nm_spool *s) {
+ struct data *data;
+ s->query = _check;
+ s->query_submit = _check_submit;
+ s->configure = _configure;
+ s->info = _info;
+ s->done = _done;
+
+ data = nm_malloc(sizeof(struct data));
+ memset(data, 0, sizeof(struct data));
+
+ data->hostname = nm_strdup(nm_specials(nm_config_get(s->config, "Hostname", "localhost")));
+ data->username = nm_strdup(nm_specials(nm_config_get(s->config, "Username", "%u")));
+ data->password = nm_strdup(nm_config_get(s->config, "Password", "secret"));
+ data->folder = nm_strdup(nm_specials(nm_config_get(s->config, "Folder", "INBOX")));
+
+ data->tls = nm_config_get_bool(s->config, "UseSSL", 0) || nm_config_get_bool(s->config, "UseTLS", 0);
+ data->port = nm_config_get_int(s->config, "Port", data->tls ? 993 : 143);
+ data->debug = nm_config_get_bool(s->config, "Debug", 0);
+ data->fdpipe = -1;
+ data->child = (pid_t) -1;
+ s->data = (void*) data;
+
+ return 0;
+}
+
+