/* * Add, remove or replace the specified flags of the messages. */ int request_store(const char *server, const char *port, const char *user, const char *mesg, const char *mode, const char *flags) { int t, r; session *s; if (!(s = session_find(server, port, user))) return -1; t = imap_store(s, mesg, mode, flags); if ((r = response_generic(s, t)) == -1) goto fail; if (xstrcasestr(flags, "\\Deleted") && get_option_boolean("expunge")) if (response_generic(s, imap_expunge(s)) == -1) goto fail; return r; fail: close_connection(s); session_destroy(s); return -1; }
/* * Core function to go to idle state. */ static int ifcore_idle(lua_State *lua) { int r; char *event; event = NULL; if (lua_gettop(lua) != 1) luaL_error(lua, "wrong number of arguments"); luaL_checktype(lua, 1, LUA_TLIGHTUSERDATA); if (get_option_boolean("reenter")) while ((r = request_idle((session *)(lua_topointer(lua, 1)), &event)) == STATUS_NONE); else r = request_idle((session *)(lua_topointer(lua, 1)), &event); lua_pop(lua, 1); if (r == -1) return 0; lua_pushboolean(lua, (r == STATUS_OK)); if (!event) return 1; lua_pushstring(lua, event); xfree(event); return 2; }
/* * Get server data and make sure there is a continuation response inside them. */ int response_continuation(session *ssn, int tag) { int r; ssize_t n; buffer_reset(&ibuf); do { buffer_check(&ibuf, ibuf.len + INPUT_BUF); if ((n = receive_response(ssn, ibuf.data + ibuf.len, 0, 1)) == -1) return -1; ibuf.len += n; if (check_bye(ibuf.data)) return STATUS_BYE; } while ((r = check_tag(ibuf.data, ssn, tag)) == STATUS_NONE && !check_continuation(ibuf.data)); if (r == STATUS_NO && (check_trycreate(ibuf.data) || get_option_boolean("create"))) return STATUS_TRYCREATE; if (r == STATUS_NONE) return STATUS_CONTINUE; return r; }
/* * Auxiliary function to create a mailbox. */ int create_mailbox(session *ssn, const char *mbox) { int r; const char *m; m = apply_namespace(mbox, ssn->ns.prefix, ssn->ns.delim); if ((r = response_generic(ssn, imap_create(ssn, m))) == -1) return -1; if (get_option_boolean("subscribe")) if (response_generic(ssn, imap_subscribe(ssn, m)) == -1) return -1; return r; }
/* * Add, remove or replace the specified flags of the messages. */ int request_store(session *ssn, const char *mesg, const char *mode, const char *flags) { int t, r; TRY(t = send_request(ssn, "UID STORE %s %sFLAGS.SILENT (%s)", mesg, (!strncasecmp(mode, "add", 3) ? "+" : !strncasecmp(mode, "remove", 6) ? "-" : ""), flags)); TRY(r = response_generic(ssn, t)); if (xstrcasestr(flags, "\\Deleted") && get_option_boolean("expunge")) { TRY(t = send_request(ssn, "EXPUNGE")); TRY(response_generic(ssn, t)); } return r; }
/* * Append supplied message to the specified mailbox. */ int request_append(session *ssn, const char *mbox, const char *mesg, size_t mesglen, const char *flags, const char *date) { int t, r; const char *m; m = apply_namespace(mbox, ssn->ns.prefix, ssn->ns.delim); TRY(t = send_request(ssn, "APPEND \"%s\"%s%s%s%s%s%s {%d}", m, (flags ? " (" : ""), (flags ? flags : ""), (flags ? ")" : ""), (date ? " \"" : ""), (date ? date : ""), (date ? "\"" : ""), mesglen)); TRY(r = response_continuation(ssn, t)); if (r == STATUS_CONTINUE) { TRY(send_continuation(ssn, mesg, mesglen)); TRY(r = response_generic(ssn, t)); } if (r == STATUS_TRYCREATE) { TRY(t = send_request(ssn, "CREATE \"%s\"", m)); TRY(response_generic(ssn, t)); if (get_option_boolean("subscribe")) { TRY(t = send_request(ssn, "SUBSCRIBE \"%s\"", m)); TRY(response_generic(ssn, t)); } TRY(t = send_request(ssn, "APPEND \"%s\"%s%s%s%s%s%s {%d}", m, (flags ? " (" : ""), (flags ? flags : ""), (flags ? ")" : ""), (date ? " \"" : ""), (date ? date : ""), (date ? "\"" : ""), mesglen)); TRY(r = response_continuation(ssn, t)); if (r == STATUS_CONTINUE) { TRY(send_continuation(ssn, mesg, mesglen)); TRY(r = response_generic(ssn, t)); } } return r; }
/* * Copy the specified messages to another mailbox. */ int request_copy(session *ssn, const char *mesg, const char *mbox) { int t, r; const char *m; m = apply_namespace(mbox, ssn->ns.prefix, ssn->ns.delim); TRY(t = send_request(ssn, "UID COPY %s \"%s\"", mesg, m)); TRY(r = response_generic(ssn, t)); if (r == STATUS_TRYCREATE) { TRY(t = send_request(ssn, "CREATE \"%s\"", m)); TRY(response_generic(ssn, t)); if (get_option_boolean("subscribe")) { TRY(t = send_request(ssn, "SUBSCRIBE \"%s\"", m)); TRY(response_generic(ssn, t)); } TRY(t = send_request(ssn, "UID COPY %s \"%s\"", mesg, m)); TRY(r = response_generic(ssn, t)); } return r; }
/* * Connect to the server, login to the IMAP server, get it's capabilities, get * the namespace of the mailboxes. */ int request_login(const char *server, const char *port, const char *ssl, const char *user, const char *pass) { int r = -1, rg = -1; session *s; if ((s = session_find(server, port, user))) return STATUS_RESPONSE_NONE; s = session_new(); s->server = xstrdup(server); s->port = xstrdup(port); s->username = xstrdup(user); if (ssl && strncasecmp(ssl, "tls1", 4) && strncasecmp(ssl, "ssl3", 4) && strncasecmp(ssl, "ssl2", 4)) ssl = NULL; if (open_connection(s, server, port, ssl) == -1) goto fail; if ((rg = response_greeting(s)) == -1) goto fail; if (opts.debug) if (response_generic(s, imap_noop(s)) == -1) goto fail; if (response_capability(s, imap_capability(s)) == -1) goto fail; #ifndef NO_SSLTLS if (!ssl && s->capabilities & CAPABILITY_STARTTLS && get_option_boolean("starttls")) switch (response_generic(s, imap_starttls(s))) { case STATUS_RESPONSE_OK: if (open_secure_connection(s, server, port, "tls1") == -1) goto fail; if (response_capability(s, imap_capability(s)) == -1) goto fail; break; case -1: goto fail; break; } #endif if (rg != STATUS_RESPONSE_PREAUTH) { #ifndef NO_CRAMMD5 if (s->capabilities & CAPABILITY_CRAMMD5 && get_option_boolean("crammd5")) { if ((r = auth_cram_md5(s, user, pass)) == -1) goto fail; } #endif if (r != STATUS_RESPONSE_OK && (r = response_generic(s, imap_login(s, user, pass))) == -1) goto fail; if (r == STATUS_RESPONSE_NO) { error("username %s or password rejected at %s\n", user, server); goto fail; } } else { r = STATUS_RESPONSE_PREAUTH; } if (response_capability(s, imap_capability(s)) == -1) goto fail; if (s->capabilities & CAPABILITY_NAMESPACE && get_option_boolean("namespace")) { if (response_namespace(s, imap_namespace(s)) == -1) goto fail; } return r; fail: close_connection(s); session_destroy(s); return -1; }
/* * Initialize SSL/TLS connection. */ int open_secure_connection(session *ssn) { int r, e; SSL_CTX *ctx; if (!ssn->sslproto) { ctx = ssl23ctx; } else if (!strcasecmp(ssn->sslproto, "ssl3")) { ctx = ssl3ctx; } else if (!strcasecmp(ssn->sslproto, "tls1")) { ctx = tls1ctx; } else if (!strcasecmp(ssn->sslproto, "tls1.1")) { #if OPENSSL_VERSION_NUMBER >= 0x01000100fL ctx = tls11ctx; #else ctx = tls1ctx; #endif } else if (!strcasecmp(ssn->sslproto, "tls1.2")) { #if OPENSSL_VERSION_NUMBER >= 0x01000100fL ctx = tls12ctx; #else ctx = tls1ctx; #endif } else { ctx = ssl23ctx; } if (!(ssn->sslconn = SSL_new(ctx))) goto fail; SSL_set_fd(ssn->sslconn, ssn->socket); for (;;) { if ((r = SSL_connect(ssn->sslconn)) > 0) break; switch (SSL_get_error(ssn->sslconn, r)) { case SSL_ERROR_ZERO_RETURN: error("initiating SSL connection to %s; the " "connection has been closed cleanly\n", ssn->server); goto fail; case SSL_ERROR_NONE: case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: case SSL_ERROR_WANT_X509_LOOKUP: case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: break; case SSL_ERROR_SYSCALL: e = ERR_get_error(); if (e == 0 && r == 0) error("initiating SSL connection to %s; EOF in " "violation of the protocol\n", ssn->server); else if (e == 0 && r == -1) error("initiating SSL connection to %s; %s\n", ssn->server, strerror(errno)); else error("initiating SSL connection to %s; %s\n", ssn->server, ERR_error_string(e, NULL)); goto fail; case SSL_ERROR_SSL: error("initiating SSL connection to %s; %s\n", ssn->server, ERR_error_string(ERR_get_error(), NULL)); goto fail; default: break; } } if (get_option_boolean("certificates") && get_cert(ssn) == -1) goto fail; return 0; fail: ssn->sslconn = NULL; return -1; }
/* * Connect to the server, login to the IMAP server, get it's capabilities, get * the namespace of the mailboxes. */ int request_login(session **ssnptr, const char *server, const char *port, const char *ssl, const char *user, const char *pass) { int t, r, rg = -1, rl = -1; session *ssn = *ssnptr; if (*ssnptr && (*ssnptr)->socket != -1) return STATUS_PREAUTH; if (!*ssnptr) { ssn = *ssnptr = session_new(); ssn->server = server; ssn->port = port; ssn->username = user; ssn->password = pass; if (strlen(ssl) != 0) ssn->sslproto = ssl; } else { debug("recovering connection: %s://%s@%s:%s/%s\n", ssn->sslproto ?"imaps" : "imap", ssn->username, ssn->server, ssn->port, ssn->selected ? ssn->selected : ""); } if (open_connection(ssn) == -1) goto fail; CHECK(rg = response_greeting(ssn)); if (opts.debug) { CHECK(t = send_request(ssn, "NOOP")); CHECK(response_generic(ssn, t)); } CHECK(t = send_request(ssn, "CAPABILITY")); CHECK(response_capability(ssn, t)); if (!ssn->sslproto && ssn->capabilities & CAPABILITY_STARTTLS && get_option_boolean("starttls")) { CHECK(t = send_request(ssn, "STARTTLS")); CHECK(r = response_generic(ssn, t)); if (r == STATUS_OK) { if (open_secure_connection(ssn) == -1) goto fail; CHECK(t = send_request(ssn, "CAPABILITY")); CHECK(response_capability(ssn, t)); } } if (rg != STATUS_PREAUTH) { if (ssn->capabilities & CAPABILITY_CRAMMD5 && get_option_boolean("crammd5")) { unsigned char *in, *out; CHECK(t = send_request(ssn, "AUTHENTICATE CRAM-MD5")); CHECK(r = response_authenticate(ssn, t, &in)); if (r == STATUS_CONTINUE) { if ((out = auth_cram_md5(ssn->username, ssn->password, in)) == NULL) goto abort; CHECK(send_continuation(ssn, (char *)(out), strlen((char *)(out)))); xfree(out); CHECK(rl = response_generic(ssn, t)); } else goto abort; } if (rl != STATUS_OK) { CHECK(t = send_request(ssn, "LOGIN \"%s\" \"%s\"", ssn->username, ssn->password)); CHECK(rl = response_generic(ssn, t)); } if (rl == STATUS_NO) { error("username %s or password rejected at %s\n", ssn->username, ssn->server); close_connection(ssn); session_destroy(ssn); return STATUS_NO; } } else { rl = STATUS_PREAUTH; } CHECK(t = send_request(ssn, "CAPABILITY")); CHECK(response_capability(ssn, t)); if (ssn->capabilities & CAPABILITY_NAMESPACE && get_option_boolean("namespace")) { CHECK(t = send_request(ssn, "NAMESPACE")); CHECK(response_namespace(ssn, t)); } if (ssn->selected) { CHECK(t = send_request(ssn, "SELECT \"%s\"", apply_namespace(ssn->selected, ssn->ns.prefix, ssn->ns.delim))); CHECK(response_select(ssn, t)); } return rl; abort: close_connection(ssn); fail: session_destroy(ssn); return -1; }
/* * Initialize SSL/TLS connection. */ int open_secure_connection(session *ssn) { int r, e; SSL_CTX *ctx; #if OPENSSL_VERSION_NUMBER >= 0x1000000fL const SSL_METHOD *method; #else SSL_METHOD *method; #endif method = NULL; if (ssn->sslproto && (!strncasecmp(ssn->sslproto, "ssl3", 4) || !strncasecmp(ssn->sslproto, "ssl2", 4))) method = SSLv23_client_method(); else method = TLSv1_client_method(); if (!(ctx = SSL_CTX_new(method))) goto fail; if (!(ssn->sslconn = SSL_new(ctx))) goto fail; SSL_set_fd(ssn->sslconn, ssn->socket); for (;;) { if ((r = SSL_connect(ssn->sslconn)) > 0) break; switch (SSL_get_error(ssn->sslconn, r)) { case SSL_ERROR_ZERO_RETURN: error("initiating SSL connection to %s; the " "connection has been closed cleanly\n", ssn->server); goto fail; case SSL_ERROR_NONE: case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: case SSL_ERROR_WANT_X509_LOOKUP: case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: break; case SSL_ERROR_SYSCALL: e = ERR_get_error(); if (e == 0 && r == 0) error("initiating SSL connection to %s; EOF in " "violation of the protocol\n", ssn->server); else if (e == 0 && r == -1) error("initiating SSL connection to %s; %s\n", ssn->server, strerror(errno)); else error("initiating SSL connection to %s; %s\n", ssn->server, ERR_error_string(e, NULL)); goto fail; case SSL_ERROR_SSL: error("initiating SSL connection to %s; %s\n", ssn->server, ERR_error_string(ERR_get_error(), NULL)); goto fail; default: break; } } if (get_option_boolean("certificates") && get_cert(ssn) == -1) goto fail; SSL_CTX_free(ctx); return 0; fail: ssn->sslconn = NULL; SSL_CTX_free(ctx); return -1; }