diff options
author | Lennart Poettering <lennart@poettering.net> | 2003-10-22 22:39:09 +0000 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2003-10-22 22:39:09 +0000 |
commit | 6b459521ce990c3e626190f66ece300daae70dac (patch) | |
tree | c4300270e776acc711d16b5ab5e7775edea590f7 /src/imap.c |
create trunk directory and move everything into it
git-svn-id: file:///home/lennart/svn/public/libnewmail/trunk@29 2d4e79f2-dfba-0310-a9f4-9628e67fcdf4
Diffstat (limited to 'src/imap.c')
-rw-r--r-- | src/imap.c | 370 |
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; +} + + |