Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nginx/nginx.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src/imap
diff options
context:
space:
mode:
authorIgor Sysoev <igor@sysoev.ru>2005-07-08 18:34:20 +0400
committerIgor Sysoev <igor@sysoev.ru>2005-07-08 18:34:20 +0400
commit5192b3651f2f44fb5769828a2a4060989c7e9c5f (patch)
treed1ef9dfd855e836c6f05b496be88dc835537d03f /src/imap
parent549c6c644976dc694765d77110ebd2504ff7ce2b (diff)
nginx-0.1.38-RELEASE importrelease-0.1.38
*) Feature: the "limit_rate" directive is supported in in proxy and FastCGI mode. *) Feature: the "X-Accel-Limit-Rate" response header line is supported in proxy and FastCGI mode. *) Feature: the "break" directive. *) Feature: the "log_not_found" directive. *) Bugfix: the response status code was not changed when request was redirected by the ""X-Accel-Redirect" header line. *) Bugfix: the variables set by the "set" directive could not be used in SSI. *) Bugfix: the segmentation fault may occurred if the SSI page has more than one remote subrequest. *) Bugfix: nginx treated the backend response as invalid if the status line in the header was transferred in two packets; the bug had appeared in 0.1.29. *) Feature: the "ssi_types" directive. *) Feature: the "autoindex_exact_size" directive. *) Bugfix: the ngx_http_autoindex_module did not support the long file names in UTF-8. *) Feature: the IMAP/POP3 proxy.
Diffstat (limited to 'src/imap')
-rw-r--r--src/imap/ngx_imap.h63
-rw-r--r--src/imap/ngx_imap_auth_http_module.c648
-rw-r--r--src/imap/ngx_imap_core_module.c159
-rw-r--r--src/imap/ngx_imap_handler.c289
-rw-r--r--src/imap/ngx_imap_parse.c447
-rw-r--r--src/imap/ngx_imap_proxy_module.c296
6 files changed, 1697 insertions, 205 deletions
diff --git a/src/imap/ngx_imap.h b/src/imap/ngx_imap.h
index 5ba390055..8a506db64 100644
--- a/src/imap/ngx_imap.h
+++ b/src/imap/ngx_imap.h
@@ -36,6 +36,12 @@ typedef struct {
ngx_uint_t protocol;
+ ngx_buf_t *pop3_capability;
+ ngx_buf_t *imap_capability;
+
+ ngx_array_t pop3_capabilities;
+ ngx_array_t imap_capabilities;
+
/* server ctx */
ngx_imap_conf_ctx_t *ctx;
} ngx_imap_core_srv_conf_t;
@@ -51,11 +57,20 @@ typedef struct {
typedef enum {
- ngx_pop3_start = 0,
- ngx_pop3_user
+ ngx_imap_start = 0,
+ ngx_imap_login,
+ ngx_imap_user,
+ ngx_imap_passwd,
} ngx_imap_state_e;
+typedef enum {
+ ngx_pop3_start = 0,
+ ngx_pop3_user,
+ ngx_pop3_passwd
+} ngx_po3_state_e;
+
+
typedef struct {
ngx_peer_connection_t upstream;
ngx_buf_t *buffer;
@@ -68,6 +83,7 @@ typedef struct {
ngx_connection_t *connection;
ngx_buf_t *buffer;
+ ngx_str_t out;
void **ctx;
void **main_conf;
@@ -75,39 +91,55 @@ typedef struct {
ngx_imap_proxy_ctx_t *proxy;
- ngx_imap_state_e imap_state;
+ ngx_uint_t imap_state;
unsigned protocol:1;
+ unsigned quoted:1;
ngx_str_t login;
ngx_str_t passwd;
+ ngx_str_t tag;
+
ngx_uint_t command;
ngx_array_t args;
+ ngx_uint_t login_attempt;
+
/* used to parse IMAP/POP3 command */
ngx_uint_t state;
+ u_char *cmd_start;
u_char *arg_start;
u_char *arg_end;
+ ngx_uint_t literal_len;
} ngx_imap_session_t;
#define NGX_POP3_USER 1
#define NGX_POP3_PASS 2
-#define NGX_POP3_APOP 3
-#define NGX_POP3_STAT 4
-#define NGX_POP3_LIST 5
-#define NGX_POP3_RETR 6
-#define NGX_POP3_DELE 7
-#define NGX_POP3_NOOP 8
-#define NGX_POP3_RSET 9
-#define NGX_POP3_TOP 10
-#define NGX_POP3_UIDL 11
-#define NGX_POP3_QUIT 12
+#define NGX_POP3_CAPA 3
+#define NGX_POP3_QUIT 4
+#define NGX_POP3_NOOP 5
+#define NGX_POP3_APOP 6
+#define NGX_POP3_STAT 7
+#define NGX_POP3_LIST 8
+#define NGX_POP3_RETR 9
+#define NGX_POP3_DELE 10
+#define NGX_POP3_RSET 11
+#define NGX_POP3_TOP 12
+#define NGX_POP3_UIDL 13
+
+
+#define NGX_IMAP_LOGIN 1
+#define NGX_IMAP_LOGOUT 2
+#define NGX_IMAP_CAPABILITY 3
+#define NGX_IMAP_NOOP 4
+
+#define NGX_IMAP_NEXT 5
-#define NGX_IMAP_PARSE_INVALID_COMMAND 10
+#define NGX_IMAP_PARSE_INVALID_COMMAND 20
#define NGX_IMAP_PROXY_INVALID 10
@@ -135,9 +167,12 @@ typedef struct {
void ngx_imap_init_connection(ngx_connection_t *c);
+void ngx_imap_auth_state(ngx_event_t *rev);
+void ngx_pop3_auth_state(ngx_event_t *rev);
void ngx_imap_close_connection(ngx_connection_t *c);
void ngx_imap_session_internal_server_error(ngx_imap_session_t *s);
+ngx_int_t ngx_imap_parse_command(ngx_imap_session_t *s);
ngx_int_t ngx_pop3_parse_command(ngx_imap_session_t *s);
diff --git a/src/imap/ngx_imap_auth_http_module.c b/src/imap/ngx_imap_auth_http_module.c
index fa984b855..e66593a4a 100644
--- a/src/imap/ngx_imap_auth_http_module.c
+++ b/src/imap/ngx_imap_auth_http_module.c
@@ -12,24 +12,54 @@
typedef struct {
- ngx_peers_t *peers;
+ ngx_peers_t *peers;
- ngx_msec_t timeout;
+ ngx_msec_t timeout;
- ngx_str_t host_header;
- ngx_str_t uri;
+ ngx_str_t host_header;
+ ngx_str_t uri;
} ngx_imap_auth_http_conf_t;
-typedef struct {
- ngx_buf_t *request;
- ngx_buf_t *response;
- ngx_peer_connection_t peer;
-} ngx_imap_auth_http_ctx_t;
+typedef struct ngx_imap_auth_http_ctx_s ngx_imap_auth_http_ctx_t;
+
+typedef void (*ngx_imap_auth_http_handler_pt)(ngx_imap_session_t *s,
+ ngx_imap_auth_http_ctx_t *ctx);
+
+struct ngx_imap_auth_http_ctx_s {
+ ngx_buf_t *request;
+ ngx_buf_t *response;
+ ngx_peer_connection_t peer;
+
+ ngx_imap_auth_http_handler_pt handler;
+
+ ngx_uint_t state;
+ ngx_uint_t hash; /* no needed ? */
+
+ u_char *header_name_start;
+ u_char *header_name_end;
+ u_char *header_start;
+ u_char *header_end;
+
+ ngx_str_t addr;
+ ngx_str_t port;
+ ngx_str_t err;
+
+ ngx_msec_t sleep;
+
+ ngx_peers_t *peers;
+};
static void ngx_imap_auth_http_write_handler(ngx_event_t *wev);
static void ngx_imap_auth_http_read_handler(ngx_event_t *rev);
+static void ngx_imap_auth_http_ignore_status_line(ngx_imap_session_t *s,
+ ngx_imap_auth_http_ctx_t *ctx);
+static void ngx_imap_auth_http_process_headers(ngx_imap_session_t *s,
+ ngx_imap_auth_http_ctx_t *ctx);
+static void ngx_imap_auth_sleep_handler(ngx_event_t *rev);
+static ngx_int_t ngx_imap_auth_http_parse_header_line(ngx_imap_session_t *s,
+ ngx_imap_auth_http_ctx_t *ctx);
static void ngx_imap_auth_http_block_read(ngx_event_t *rev);
static void ngx_imap_auth_http_dummy_handler(ngx_event_t *ev);
static ngx_buf_t *ngx_imap_auth_http_create_request(ngx_imap_session_t *s,
@@ -124,6 +154,8 @@ ngx_imap_auth_http_init(ngx_imap_session_t *s)
ctx->peer.connection->read->handler = ngx_imap_auth_http_read_handler;
ctx->peer.connection->write->handler = ngx_imap_auth_http_write_handler;
+ ctx->handler = ngx_imap_auth_http_ignore_status_line;
+
if (rc == NGX_OK) {
ngx_imap_auth_http_write_handler(ctx->peer.connection->write);
return;
@@ -194,7 +226,6 @@ static void
ngx_imap_auth_http_read_handler(ngx_event_t *rev)
{
ssize_t n, size;
- ngx_peers_t *peers;
ngx_connection_t *c;
ngx_imap_session_t *s;
ngx_imap_auth_http_ctx_t *ctx;
@@ -224,24 +255,604 @@ ngx_imap_auth_http_read_handler(ngx_event_t *rev)
}
}
- size = ctx->response->last - ctx->response->pos;
+ size = ctx->response->end - ctx->response->last;
n = ngx_recv(c, ctx->response->pos, size);
- if (n == NGX_ERROR || n == 0) {
+ if (n > 0) {
+ ctx->response->last += n;
+
+ ctx->handler(s, ctx);
+ return;
+ }
+
+ if (n == NGX_AGAIN) {
+ return;
+ }
+
+ ngx_close_connection(ctx->peer.connection);
+ ngx_imap_session_internal_server_error(s);
+}
+
+
+static void
+ngx_imap_auth_http_ignore_status_line(ngx_imap_session_t *s,
+ ngx_imap_auth_http_ctx_t *ctx)
+{
+ u_char *p, ch;
+ enum {
+ sw_start = 0,
+ sw_H,
+ sw_HT,
+ sw_HTT,
+ sw_HTTP,
+ sw_skip,
+ sw_almost_done
+ } state;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, s->connection->log, 0,
+ "imap auth http process status line");
+
+ state = ctx->state;
+
+ for (p = ctx->response->pos; p < ctx->response->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* "HTTP/" */
+ case sw_start:
+ if (ch == 'H') {
+ state = sw_H;
+ break;
+ }
+ goto next;
+
+ case sw_H:
+ if (ch == 'T') {
+ state = sw_HT;
+ break;
+ }
+ goto next;
+
+ case sw_HT:
+ if (ch == 'T') {
+ state = sw_HTT;
+ break;
+ }
+ goto next;
+
+ case sw_HTT:
+ if (ch == 'P') {
+ state = sw_HTTP;
+ break;
+ }
+ goto next;
+
+ case sw_HTTP:
+ if (ch == '/') {
+ state = sw_skip;
+ break;
+ }
+ goto next;
+
+ /* any text until end of line */
+ case sw_skip:
+ switch (ch) {
+ case CR:
+ state = sw_almost_done;
+
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+
+ /* end of status line */
+ case sw_almost_done:
+ if (ch == LF) {
+ goto done;
+ }
+
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "auth http server sent invalid response");
+ ngx_close_connection(ctx->peer.connection);
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+ }
+
+ ctx->response->pos = p;
+ ctx->state = state;
+
+ return;
+
+next:
+
+ p = ctx->response->start - 1;
+
+done:
+
+ ctx->response->pos = p + 1;
+ ctx->state = 0;
+ ctx->handler = ngx_imap_auth_http_process_headers;
+ ctx->handler(s, ctx);
+}
+
+
+static void
+ngx_imap_auth_http_process_headers(ngx_imap_session_t *s,
+ ngx_imap_auth_http_ctx_t *ctx)
+{
+ u_char *p;
+ size_t len, size;
+ ngx_int_t rc, port, n;
+ struct sockaddr_in *sin;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, s->connection->log, 0,
+ "imap auth http process headers");
+
+ for ( ;; ) {
+ rc = ngx_imap_auth_http_parse_header_line(s, ctx);
+
+ if (rc == NGX_OK) {
+
+#if (NGX_DEBUG)
+ {
+ ngx_str_t key, value;
+
+ key.len = ctx->header_name_end - ctx->header_name_start;
+ key.data = ctx->header_name_start;
+ value.len = ctx->header_end - ctx->header_start;
+ value.data = ctx->header_start;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_IMAP, s->connection->log, 0,
+ "auth http header: \"%V: %V\"",
+ &key, &value);
+ }
+#endif
+
+ len = ctx->header_name_end - ctx->header_name_start;
+
+ if (len == sizeof("Auth-Status") - 1
+ && ngx_strncasecmp(ctx->header_name_start, "Auth-Status",
+ sizeof("Auth-Status") - 1) == 0)
+ {
+ len = ctx->header_end - ctx->header_start;
+
+ if (len == 2
+ && ctx->header_start[0] == 'O'
+ && ctx->header_start[1] == 'K')
+ {
+ continue;
+ }
+
+ if (s->protocol == NGX_IMAP_POP3_PROTOCOL) {
+ size = sizeof("-ERR") - 1 + len + sizeof(CRLF) - 1;
+
+ } else {
+ size = s->tag.len + sizeof("NO") - 1 + len
+ + sizeof(CRLF) - 1;
+ }
+
+ p = ngx_pcalloc(s->connection->pool, size);
+ if (p == NULL) {
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ ctx->err.data = p;
+
+ if (s->protocol == NGX_IMAP_POP3_PROTOCOL) {
+ *p++ = '-'; *p++ = 'E'; *p++ = 'R'; *p++ = 'R';
+
+ } else {
+ p = ngx_cpymem(p, s->tag.data, s->tag.len);
+ *p++ = 'N'; *p++ = 'O';
+ }
+
+ *p++ = ' ';
+ p = ngx_cpymem(p, ctx->header_start, len);
+ *p++ = CR; *p++ = LF;
+
+ ctx->err.len = p - ctx->err.data;
+
+ continue;
+ }
+
+ if (len == sizeof("Auth-Server") - 1
+ && ngx_strncasecmp(ctx->header_name_start, "Auth-Server",
+ sizeof("Auth-Server") - 1) == 0)
+ {
+ ctx->addr.len = ctx->header_end - ctx->header_start;
+ ctx->addr.data = ctx->header_start;
+
+ continue;
+ }
+
+ if (len == sizeof("Auth-Port") - 1
+ && ngx_strncasecmp(ctx->header_name_start, "Auth-Port",
+ sizeof("Auth-Port") - 1) == 0)
+ {
+ ctx->port.len = ctx->header_end - ctx->header_start;
+ ctx->port.data = ctx->header_start;
+
+ continue;
+ }
+
+ if (len == sizeof("Auth-User") - 1
+ && ngx_strncasecmp(ctx->header_name_start, "Auth-User",
+ sizeof("Auth-User") - 1) == 0)
+ {
+ s->login.len = ctx->header_end - ctx->header_start;
+ s->login.data = ctx->header_start;
+
+ continue;
+ }
+
+ if (len == sizeof("Auth-Wait") - 1
+ && ngx_strncasecmp(ctx->header_name_start, "Auth-Wait",
+ sizeof("Auth-Wait") - 1) == 0)
+ {
+ n = ngx_atoi(ctx->header_start,
+ ctx->header_end - ctx->header_start);
+
+ if (n != NGX_ERROR) {
+ ctx->sleep = n;
+ }
+
+ continue;
+ }
+
+ /* ignore other headers */
+
+ continue;
+ }
+
+ if (rc == NGX_DONE) {
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, s->connection->log, 0,
+ "auth http header done");
+
+ ngx_close_connection(ctx->peer.connection);
+
+ if (ctx->err.len) {
+ (void) ngx_send(s->connection, ctx->err.data, ctx->err.len);
+
+ if (ctx->sleep == 0) {
+ ngx_imap_close_connection(s->connection);
+ return;
+ }
+
+ ngx_add_timer(s->connection->read, ctx->sleep * 1000);
+
+ s->connection->read->handler = ngx_imap_auth_sleep_handler;
+
+ return;
+ }
+
+ if (ctx->addr.len == 0 || ctx->port.len == 0) {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "auth http server did not send server or port");
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ ctx->peers = ngx_pcalloc(s->connection->pool, sizeof(ngx_peers_t));
+ if (ctx->peers == NULL) {
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ sin = ngx_pcalloc(s->connection->pool, sizeof(struct sockaddr_in));
+ if (sin == NULL) {
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ sin->sin_family = AF_INET;
+
+ port = ngx_atoi(ctx->port.data, ctx->port.len);
+ if (port == NGX_ERROR || port < 1 || port > 65536) {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "auth http server sent invalid server "
+ "port:\"%V\"", &ctx->port);
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ sin->sin_port = htons((in_port_t) port);
+
+ ctx->addr.data[ctx->addr.len] = '\0';
+ sin->sin_addr.s_addr = inet_addr((char *) ctx->addr.data);
+ if (sin->sin_addr.s_addr == INADDR_NONE) {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "auth http server sent invalid server "
+ "address:\"%V\"", &ctx->addr);
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ ctx->peers->number = 1;
+
+ ctx->peers->peer[0].sockaddr = (struct sockaddr *) sin;
+ ctx->peers->peer[0].socklen = sizeof(struct sockaddr_in);
+
+ len = ctx->addr.len + 1 + ctx->port.len;
+
+ ctx->peers->peer[0].name.len = len;
+
+ ctx->peers->peer[0].name.data = ngx_palloc(s->connection->pool,
+ len);
+ if (ctx->peers->peer[0].name.data == NULL) {
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ len = ctx->addr.len;
+
+ ngx_memcpy(ctx->peers->peer[0].name.data, ctx->addr.data, len);
+
+ ctx->peers->peer[0].name.data[len++] = ':';
+
+ ngx_memcpy(ctx->peers->peer[0].name.data + len,
+ ctx->port.data, ctx->port.len);
+
+ ctx->peers->peer[0].uri_separator = "";
+
+ ngx_imap_proxy_init(s, ctx->peers);
+
+ return;
+ }
+
+ if (rc == NGX_AGAIN ) {
+ return;
+ }
+
+ /* rc == NGX_ERROR */
+
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "auth http server sent invalid header in response");
ngx_close_connection(ctx->peer.connection);
ngx_imap_session_internal_server_error(s);
+
+ return;
+ }
+}
+
+
+static void
+ngx_imap_auth_sleep_handler(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_imap_session_t *s;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap auth sleep handler");
+
+ c = rev->data;
+ s = c->data;
+
+ if (rev->timedout) {
+
+ rev->timedout = 0;
+
+ if (s->protocol == NGX_IMAP_POP3_PROTOCOL) {
+ s->imap_state = ngx_pop3_start;
+ s->connection->read->handler = ngx_pop3_auth_state;
+
+ } else {
+ s->imap_state = ngx_imap_start;
+ s->connection->read->handler = ngx_imap_auth_state;
+ }
+
+ if (rev->ready) {
+ s->connection->read->handler(rev);
+ return;
+ }
+
+ if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+ ngx_imap_close_connection(s->connection);
+ }
+
return;
}
+ if (rev->active) {
+ if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+ ngx_imap_close_connection(s->connection);
+ }
+ }
+}
+static ngx_int_t
+ngx_imap_auth_http_parse_header_line(ngx_imap_session_t *s,
+ ngx_imap_auth_http_ctx_t *ctx)
+{
+ u_char c, ch, *p;
+ ngx_uint_t hash;
+ enum {
+ sw_start = 0,
+ sw_name,
+ sw_space_before_value,
+ sw_value,
+ sw_space_after_value,
+ sw_almost_done,
+ sw_header_almost_done
+ } state;
+
+ state = ctx->state;
+ hash = ctx->hash;
+
+ for (p = ctx->response->pos; p < ctx->response->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* first char */
+ case sw_start:
+
+ switch (ch) {
+ case CR:
+ ctx->header_end = p;
+ state = sw_header_almost_done;
+ break;
+ case LF:
+ ctx->header_end = p;
+ goto header_done;
+ default:
+ state = sw_name;
+ ctx->header_name_start = p;
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ hash = c;
+ break;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ hash = ch;
+ break;
+ }
+
+ return NGX_ERROR;
+ }
+ break;
+
+ /* header name */
+ case sw_name:
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ hash += c;
+ break;
+ }
+ if (ch == ':') {
+ ctx->header_name_end = p;
+ state = sw_space_before_value;
+ break;
+ }
+ if (ch == '-') {
+ hash += ch;
+ break;
+ }
- peers = NULL;
+ if (ch >= '0' && ch <= '9') {
+ hash += ch;
+ break;
+ }
+
+ if (ch == CR) {
+ ctx->header_name_end = p;
+ ctx->header_start = p;
+ ctx->header_end = p;
+ state = sw_almost_done;
+ break;
+ }
+
+ if (ch == LF) {
+ ctx->header_name_end = p;
+ ctx->header_start = p;
+ ctx->header_end = p;
+ goto done;
+ }
+
+ return NGX_ERROR;
+
+ /* space* before header value */
+ case sw_space_before_value:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ ctx->header_start = p;
+ ctx->header_end = p;
+ state = sw_almost_done;
+ break;
+ case LF:
+ ctx->header_start = p;
+ ctx->header_end = p;
+ goto done;
+ default:
+ ctx->header_start = p;
+ state = sw_value;
+ break;
+ }
+ break;
+
+ /* header value */
+ case sw_value:
+ switch (ch) {
+ case ' ':
+ ctx->header_end = p;
+ state = sw_space_after_value;
+ break;
+ case CR:
+ ctx->header_end = p;
+ state = sw_almost_done;
+ break;
+ case LF:
+ ctx->header_end = p;
+ goto done;
+ }
+ break;
+
+ /* space* before end of header line */
+ case sw_space_after_value:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ default:
+ state = sw_value;
+ break;
+ }
+ break;
+
+ /* end of header line */
+ case sw_almost_done:
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ return NGX_ERROR;
+ }
- ngx_imap_proxy_init(s, peers);
+ /* end of header */
+ case sw_header_almost_done:
+ switch (ch) {
+ case LF:
+ goto header_done;
+ default:
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ ctx->response->pos = p;
+ ctx->state = state;
+ ctx->hash = hash;
+
+ return NGX_AGAIN;
+
+done:
+
+ ctx->response->pos = p + 1;
+ ctx->state = sw_start;
+ ctx->hash = hash;
+
+ return NGX_OK;
+
+header_done:
+
+ ctx->response->pos = p + 1;
+ ctx->state = sw_start;
+
+ return NGX_DONE;
}
@@ -288,6 +899,8 @@ ngx_imap_auth_http_create_request(ngx_imap_session_t *s,
+ sizeof("Auth-User: ") - 1 + s->login.len + sizeof(CRLF) - 1
+ sizeof("Auth-Pass: ") - 1 + s->passwd.len + sizeof(CRLF) - 1
+ sizeof("Auth-Protocol: imap" CRLF) - 1
+ + sizeof("Auth-Login-Attempt: ") - 1 + NGX_INT_T_LEN
+ + sizeof(CRLF) - 1
+ sizeof("Client-IP: ") - 1 + s->connection->addr_text.len
+ sizeof(CRLF) - 1
+ sizeof(CRLF) - 1;
@@ -324,6 +937,9 @@ ngx_imap_auth_http_create_request(ngx_imap_session_t *s,
sizeof("imap") - 1);
*b->last++ = CR; *b->last++ = LF;
+ b->last = ngx_sprintf(b->last, "Auth-Login-Attempt: %ui" CRLF,
+ s->login_attempt);
+
b->last = ngx_cpymem(b->last, "Client-IP: ", sizeof("Client-IP: ") - 1);
b->last = ngx_cpymem(b->last, s->connection->addr_text.data,
s->connection->addr_text.len);
@@ -338,7 +954,7 @@ ngx_imap_auth_http_create_request(ngx_imap_session_t *s,
l.len = b->last - b->pos;
l.data = b->pos;
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, s->connection->log, 0,
+ ngx_log_debug1(NGX_LOG_DEBUG_IMAP, s->connection->log, 0,
"imap auth http header:\n\"%V\"", &l);
}
#endif
@@ -440,7 +1056,7 @@ ngx_imap_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
}
for (i = 0; i < ahcf->peers->number; i++) {
- ahcf->peers->peer[i].uri_separator = ":";
+ ahcf->peers->peer[i].uri_separator = "";
}
ahcf->host_header = inet_upstream.host_header;
diff --git a/src/imap/ngx_imap_core_module.c b/src/imap/ngx_imap_core_module.c
index b4bde5282..e5d7a2050 100644
--- a/src/imap/ngx_imap_core_module.c
+++ b/src/imap/ngx_imap_core_module.c
@@ -18,6 +18,8 @@ static char *ngx_imap_core_server(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_imap_core_listen(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
+static char *ngx_imap_core_capability(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
static ngx_conf_enum_t ngx_imap_core_procotol[] = {
@@ -27,6 +29,22 @@ static ngx_conf_enum_t ngx_imap_core_procotol[] = {
};
+static ngx_str_t ngx_pop3_default_capabilities[] = {
+ ngx_string("TOP"),
+ ngx_string("USER"),
+ ngx_string("UIDL"),
+ ngx_null_string
+};
+
+
+static ngx_str_t ngx_imap_default_capabilities[] = {
+ ngx_string("IMAP4"),
+ ngx_string("IMAP4rev1"),
+ ngx_string("UIDPLUS"),
+ ngx_null_string
+};
+
+
static ngx_command_t ngx_imap_core_commands[] = {
{ ngx_string("server"),
@@ -71,6 +89,20 @@ static ngx_command_t ngx_imap_core_commands[] = {
offsetof(ngx_imap_core_srv_conf_t, timeout),
NULL },
+ { ngx_string("pop3_capabilities"),
+ NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_1MORE,
+ ngx_imap_core_capability,
+ NGX_IMAP_SRV_CONF_OFFSET,
+ offsetof(ngx_imap_core_srv_conf_t, pop3_capabilities),
+ NULL },
+
+ { ngx_string("imap_capabilities"),
+ NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_1MORE,
+ ngx_imap_core_capability,
+ NGX_IMAP_SRV_CONF_OFFSET,
+ offsetof(ngx_imap_core_srv_conf_t, imap_capabilities),
+ NULL },
+
ngx_null_command
};
@@ -121,7 +153,7 @@ ngx_imap_core_create_srv_conf(ngx_conf_t *cf)
cscf = ngx_pcalloc(cf->pool, sizeof(ngx_imap_core_srv_conf_t));
if (cscf == NULL) {
- return NGX_CONF_ERROR;
+ return NULL;
}
cscf->imap_client_buffer_size = NGX_CONF_UNSET_SIZE;
@@ -129,6 +161,18 @@ ngx_imap_core_create_srv_conf(ngx_conf_t *cf)
cscf->timeout = NGX_CONF_UNSET_MSEC;
cscf->protocol = NGX_CONF_UNSET_UINT;
+ if (ngx_array_init(&cscf->pop3_capabilities, cf->pool, 4, sizeof(ngx_str_t))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ if (ngx_array_init(&cscf->imap_capabilities, cf->pool, 4, sizeof(ngx_str_t))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
return cscf;
}
@@ -139,6 +183,11 @@ ngx_imap_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_imap_core_srv_conf_t *prev = parent;
ngx_imap_core_srv_conf_t *conf = child;
+ size_t size;
+ ngx_buf_t *b;
+ ngx_str_t *c, *d;
+ ngx_uint_t i;
+
ngx_conf_merge_size_value(conf->imap_client_buffer_size,
prev->imap_client_buffer_size,
(size_t) ngx_pagesize);
@@ -148,6 +197,88 @@ ngx_imap_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_unsigned_value(conf->protocol, prev->protocol,
NGX_IMAP_IMAP_PROTOCOL);
+
+ if (conf->pop3_capabilities.nelts == 0) {
+ conf->pop3_capabilities = prev->pop3_capabilities;
+ }
+
+ if (conf->pop3_capabilities.nelts == 0) {
+
+ for (d = ngx_pop3_default_capabilities; d->len; d++) {
+ c = ngx_array_push(&conf->pop3_capabilities);
+ if (c == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *c = *d;
+ }
+ }
+
+ size = sizeof("+OK Capability list follows" CRLF) - 1
+ + sizeof("." CRLF) - 1;
+
+ c = conf->pop3_capabilities.elts;
+ for (i = 0; i < conf->pop3_capabilities.nelts; i++) {
+ size += c[i].len + sizeof(CRLF) - 1;
+ }
+
+ b = ngx_create_temp_buf(cf->pool, size);
+ if (b == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ b->last = ngx_cpymem(b->last, "+OK Capability list follows" CRLF,
+ sizeof("+OK Capability list follows" CRLF) - 1);
+
+ for (i = 0; i < conf->pop3_capabilities.nelts; i++) {
+ b->last = ngx_cpymem(b->last, c[i].data, c[i].len);
+ *b->last++ = CR; *b->last++ = LF;
+ }
+
+ *b->last++ = '.'; *b->last++ = CR; *b->last++ = LF;
+
+ conf->pop3_capability = b;
+
+
+ if (conf->imap_capabilities.nelts == 0) {
+ conf->imap_capabilities = prev->imap_capabilities;
+ }
+
+ if (conf->imap_capabilities.nelts == 0) {
+
+ for (d = ngx_imap_default_capabilities; d->len; d++) {
+ c = ngx_array_push(&conf->imap_capabilities);
+ if (c == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *c = *d;
+ }
+ }
+
+ size = sizeof("* CAPABILITY") - 1 + sizeof(CRLF) - 1;
+
+ c = conf->imap_capabilities.elts;
+ for (i = 0; i < conf->imap_capabilities.nelts; i++) {
+ size += 1 + c[i].len;
+ }
+
+ b = ngx_create_temp_buf(cf->pool, size);
+ if (b == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ b->last = ngx_cpymem(b->last, "* CAPABILITY", sizeof("* CAPABILITY") - 1);
+
+ for (i = 0; i < conf->imap_capabilities.nelts; i++) {
+ *b->last++ = ' ';
+ b->last = ngx_cpymem(b->last, c[i].data, c[i].len);
+ }
+
+ *b->last++ = CR; *b->last++ = LF;
+
+ conf->imap_capability = b;
+
return NGX_CONF_OK;
}
@@ -296,3 +427,29 @@ ngx_imap_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
return NGX_CONF_OK;
}
+
+
+static char *
+ngx_imap_core_capability(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *p = conf;
+
+ ngx_str_t *c, *value;
+ ngx_uint_t i;
+ ngx_array_t *a;
+
+ a = (ngx_array_t *) (p + cmd->offset);
+
+ value = cf->args->elts;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+ c = ngx_array_push(a);
+ if (c == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *c = value[i];
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/src/imap/ngx_imap_handler.c b/src/imap/ngx_imap_handler.c
index 680bf20d0..6464d4a85 100644
--- a/src/imap/ngx_imap_handler.c
+++ b/src/imap/ngx_imap_handler.c
@@ -8,20 +8,15 @@
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_imap.h>
-#include <nginx.h>
static void ngx_imap_init_session(ngx_event_t *rev);
-
-static void ngx_pop3_auth_state(ngx_event_t *rev);
-static ngx_int_t ngx_pop3_read_command(ngx_imap_session_t *s);
-
-static void ngx_imap_auth_state(ngx_event_t *rev);
+static ngx_int_t ngx_imap_read_command(ngx_imap_session_t *s);
static ngx_str_t greetings[] = {
- ngx_string("+OK " NGINX_VER " ready" CRLF),
- ngx_string("* OK " NGINX_VER " ready" CRLF)
+ ngx_string("+OK POP3 ready" CRLF),
+ ngx_string("* OK IMAP ready" CRLF)
};
static ngx_str_t internal_server_errors[] = {
@@ -32,6 +27,11 @@ static ngx_str_t internal_server_errors[] = {
static u_char pop3_ok[] = "+OK" CRLF;
static u_char pop3_invalid_command[] = "-ERR invalid command" CRLF;
+static u_char imap_ok[] = "OK" CRLF;
+static u_char imap_next[] = "+ OK" CRLF;
+static u_char imap_bye[] = "* BYE" CRLF;
+static u_char imap_invalid_command[] = "BAD invalid command" CRLF;
+
void
ngx_imap_init_connection(ngx_connection_t *c)
@@ -87,7 +87,7 @@ ngx_imap_init_session(ngx_event_t *rev)
s = ngx_pcalloc(c->pool, sizeof(ngx_imap_session_t));
if (s == NULL) {
- ngx_imap_close_connection(c);
+ ngx_imap_session_internal_server_error(s);
return;
}
@@ -96,7 +96,7 @@ ngx_imap_init_session(ngx_event_t *rev)
s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_imap_max_module);
if (s->ctx == NULL) {
- ngx_imap_close_connection(c);
+ ngx_imap_session_internal_server_error(s);
return;
}
@@ -105,7 +105,7 @@ ngx_imap_init_session(ngx_event_t *rev)
s->srv_conf = ctx->srv_conf;
if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) == NGX_ERROR) {
- ngx_imap_close_connection(c);
+ ngx_imap_session_internal_server_error(s);
return;
}
@@ -115,16 +115,18 @@ ngx_imap_init_session(ngx_event_t *rev)
if (cscf->protocol == NGX_IMAP_POP3_PROTOCOL) {
size = 128;
+ s->imap_state = ngx_pop3_start;
c->read->handler = ngx_pop3_auth_state;
} else {
size = cscf->imap_client_buffer_size;
+ s->imap_state = ngx_imap_start;
c->read->handler = ngx_imap_auth_state;
}
s->buffer = ngx_create_temp_buf(c->pool, size);
if (s->buffer == NULL) {
- ngx_imap_close_connection(c);
+ ngx_imap_session_internal_server_error(s);
return;
}
@@ -132,27 +134,194 @@ ngx_imap_init_session(ngx_event_t *rev)
}
-static void
+void
ngx_imap_auth_state(ngx_event_t *rev)
{
- ngx_connection_t *c;
+ u_char *text, *last, *out, *p;
+ ssize_t size, text_len, last_len;
+ ngx_str_t *arg;
+ ngx_int_t rc;
+ ngx_uint_t quit, tag;
+ ngx_connection_t *c;
+ ngx_imap_session_t *s;
+ ngx_imap_core_srv_conf_t *cscf;
c = rev->data;
+ s = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, c->log, 0, "imap auth state");
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ ngx_imap_close_connection(c);
+ return;
+ }
+
+ rc = ngx_imap_read_command(s);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0, "imap auth: %i", rc);
+
+ if (rc == NGX_AGAIN || rc == NGX_ERROR) {
+ return;
+ }
+
+ quit = 0;
+ tag = 1;
+
+ text = NULL;
+ text_len = 0;
+
+ last = imap_ok;
+ last_len = sizeof(imap_ok) - 1;
+
+ if (rc == NGX_OK) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0, "imap auth command: %i",
+ s->command);
+
+ switch (s->command) {
+
+ case NGX_IMAP_LOGIN:
+ if (s->args.nelts == 2) {
+
+ arg = s->args.elts;
+
+ s->login.len = arg[0].len;
+ s->login.data = ngx_palloc(c->pool, s->login.len);
+ if (s->login.data == NULL) {
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ ngx_memcpy(s->login.data, arg[0].data, s->login.len);
+
+ s->passwd.len = arg[1].len;
+ s->passwd.data = ngx_palloc(c->pool, s->passwd.len);
+ if (s->passwd.data == NULL) {
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_IMAP, c->log, 0,
+ "imap login:\"%V\" passwd:\"%V\"",
+ &s->login, &s->passwd);
+
+ s->args.nelts = 0;
+ s->buffer->pos = s->buffer->start;
+ s->buffer->last = s->buffer->start;
+
+ if (rev->timer_set) {
+ ngx_del_timer(rev);
+ }
+
+ s->login_attempt++;
+
+ ngx_imap_auth_http_init(s);
+
+ return;
+
+ } else {
+ rc = NGX_IMAP_PARSE_INVALID_COMMAND;
+ }
+
+ break;
+
+ case NGX_IMAP_CAPABILITY:
+ cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
+ text = cscf->imap_capability->pos;
+ text_len = cscf->imap_capability->last - cscf->imap_capability->pos;
+ break;
+
+ case NGX_IMAP_LOGOUT:
+ text = imap_bye;
+ text_len = sizeof(imap_bye) - 1;
+ quit = 1;
+ break;
+
+ case NGX_IMAP_NOOP:
+ break;
+
+ default:
+ rc = NGX_IMAP_PARSE_INVALID_COMMAND;
+ break;
+ }
- ngx_imap_close_connection(c);
+ } else if (rc == NGX_IMAP_NEXT) {
+ last = imap_next;
+ last_len = sizeof(imap_next) - 1;
+ tag = 0;
+ }
+
+ if (rc == NGX_IMAP_PARSE_INVALID_COMMAND) {
+ last = imap_invalid_command;
+ last_len = sizeof(imap_invalid_command) - 1;
+ }
+
+ if (tag) {
+ if (s->out.len < text_len + s->tag.len + last_len) {
+
+ s->out.len = text_len + s->tag.len + last_len;
+ s->out.data = ngx_palloc(c->pool, s->out.len);
+ if (s->out.data == NULL) {
+ ngx_imap_close_connection(c);
+ return;
+ }
+ }
+
+ out = s->out.data;
+ p = out;
+
+ if (text) {
+ p = ngx_cpymem(p, text, text_len);
+ }
+ p = ngx_cpymem(p, s->tag.data, s->tag.len);
+ ngx_memcpy(p, last, last_len);
+
+ size = text_len + s->tag.len + last_len;
+
+ } else {
+ out = last;
+ size = last_len;
+ }
+
+ if (ngx_send(c, out, size) < size) {
+ /*
+ * we treat the incomplete sending as NGX_ERROR
+ * because it is very strange here
+ */
+ ngx_imap_close_connection(c);
+ return;
+ }
+
+ if (rc == NGX_IMAP_NEXT) {
+ return;
+ }
+
+ if (quit) {
+ ngx_imap_close_connection(c);
+ return;
+ }
+
+ s->args.nelts = 0;
+ s->buffer->pos = s->buffer->start;
+ s->buffer->last = s->buffer->start;
+ s->tag.len = 0;
}
-static void
+void
ngx_pop3_auth_state(ngx_event_t *rev)
{
- u_char *text;
- ssize_t size;
- ngx_int_t rc;
- ngx_uint_t quit;
- ngx_str_t *arg;
- ngx_connection_t *c;
- ngx_imap_session_t *s;
+ u_char *text;
+ ssize_t size;
+ ngx_int_t rc;
+ ngx_uint_t quit;
+ ngx_str_t *arg;
+ ngx_connection_t *c;
+ ngx_imap_session_t *s;
+ ngx_imap_core_srv_conf_t *cscf;
c = rev->data;
s = c->data;
@@ -165,7 +334,7 @@ ngx_pop3_auth_state(ngx_event_t *rev)
return;
}
- rc = ngx_pop3_read_command(s);
+ rc = ngx_imap_read_command(s);
if (rc == NGX_AGAIN || rc == NGX_ERROR) {
return;
@@ -188,16 +357,16 @@ ngx_pop3_auth_state(ngx_event_t *rev)
arg = s->args.elts;
s->login.len = arg[0].len;
- s->login.data = ngx_palloc(c->pool, s->login.len + 1);
+ s->login.data = ngx_palloc(c->pool, s->login.len);
if (s->login.data == NULL) {
- ngx_imap_close_connection(c);
+ ngx_imap_session_internal_server_error(s);
return;
}
- ngx_cpystrn(s->login.data, arg[0].data, s->login.len + 1);
+ ngx_memcpy(s->login.data, arg[0].data, s->login.len);
ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0,
- "pop3 login: \"%s\"", s->login.data);
+ "pop3 login: \"%V\"", &s->login);
} else {
rc = NGX_IMAP_PARSE_INVALID_COMMAND;
@@ -205,10 +374,19 @@ ngx_pop3_auth_state(ngx_event_t *rev)
break;
+ case NGX_POP3_CAPA:
+ cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
+ text = cscf->pop3_capability->pos;
+ size = cscf->pop3_capability->last - cscf->pop3_capability->pos;
+ break;
+
case NGX_POP3_QUIT:
quit = 1;
break;
+ case NGX_POP3_NOOP:
+ break;
+
default:
s->imap_state = ngx_pop3_start;
rc = NGX_IMAP_PARSE_INVALID_COMMAND;
@@ -227,20 +405,25 @@ ngx_pop3_auth_state(ngx_event_t *rev)
arg = s->args.elts;
s->passwd.len = arg[0].len;
- s->passwd.data = ngx_palloc(c->pool, s->passwd.len + 1);
+ s->passwd.data = ngx_palloc(c->pool, s->passwd.len);
if (s->passwd.data == NULL) {
- ngx_imap_close_connection(c);
+ ngx_imap_session_internal_server_error(s);
return;
}
- ngx_cpystrn(s->passwd.data, arg[0].data, s->passwd.len + 1);
+ ngx_memcpy(s->passwd.data, arg[0].data, s->passwd.len);
ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0,
- "pop3 passwd: \"%s\"", s->passwd.data);
+ "pop3 passwd: \"%V\"", &s->passwd);
+ s->args.nelts = 0;
s->buffer->pos = s->buffer->start;
s->buffer->last = s->buffer->start;
+ if (rev->timer_set) {
+ ngx_del_timer(rev);
+ }
+
ngx_imap_auth_http_init(s);
return;
@@ -251,10 +434,19 @@ ngx_pop3_auth_state(ngx_event_t *rev)
break;
+ case NGX_POP3_CAPA:
+ cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
+ text = cscf->pop3_capability->pos;
+ size = cscf->pop3_capability->last - cscf->pop3_capability->pos;
+ break;
+
case NGX_POP3_QUIT:
quit = 1;
break;
+ case NGX_POP3_NOOP:
+ break;
+
default:
s->imap_state = ngx_pop3_start;
rc = NGX_IMAP_PARSE_INVALID_COMMAND;
@@ -262,6 +454,10 @@ ngx_pop3_auth_state(ngx_event_t *rev)
}
break;
+
+ /* suppress warinings */
+ case ngx_pop3_passwd:
+ break;
}
}
@@ -291,7 +487,7 @@ ngx_pop3_auth_state(ngx_event_t *rev)
static ngx_int_t
-ngx_pop3_read_command(ngx_imap_session_t *s)
+ngx_imap_read_command(ngx_imap_session_t *s)
{
ssize_t n;
ngx_int_t rc;
@@ -310,16 +506,23 @@ ngx_pop3_read_command(ngx_imap_session_t *s)
if (n == NGX_AGAIN) {
if (ngx_handle_read_event(s->connection->read, 0) == NGX_ERROR) {
- ngx_imap_close_connection(s->connection);
+ ngx_imap_session_internal_server_error(s);
return NGX_ERROR;
}
return NGX_AGAIN;
}
- rc = ngx_pop3_parse_command(s);
+ if (s->protocol == NGX_IMAP_POP3_PROTOCOL) {
+ rc = ngx_pop3_parse_command(s);
+ } else {
+ rc = ngx_imap_parse_command(s);
+ }
- if (rc == NGX_AGAIN || rc == NGX_IMAP_PARSE_INVALID_COMMAND) {
+ if (rc == NGX_AGAIN
+ || rc == NGX_IMAP_NEXT
+ || rc == NGX_IMAP_PARSE_INVALID_COMMAND)
+ {
return rc;
}
@@ -332,20 +535,6 @@ ngx_pop3_read_command(ngx_imap_session_t *s)
}
-#if 0
-
-void
-ngx_imap_close_session(ngx_imap_session_t *s)
-{
- ngx_log_debug0(NGX_LOG_DEBUG_IMAP, s->connection->log, 0,
- "close imap session");
-
- ngx_imap_close_connection(s->connection);
-}
-
-#endif
-
-
void
ngx_imap_session_internal_server_error(ngx_imap_session_t *s)
{
diff --git a/src/imap/ngx_imap_parse.c b/src/imap/ngx_imap_parse.c
index 6c1c3e95a..e3923e892 100644
--- a/src/imap/ngx_imap_parse.c
+++ b/src/imap/ngx_imap_parse.c
@@ -10,64 +10,383 @@
#include <ngx_imap.h>
-ngx_int_t ngx_pop3_parse_command(ngx_imap_session_t *s)
+ngx_int_t ngx_imap_parse_command(ngx_imap_session_t *s)
{
u_char ch, *p, *c;
ngx_str_t *arg;
enum {
sw_start = 0,
+ sw_spaces_before_command,
+ sw_command,
sw_spaces_before_argument,
sw_argument,
- sw_almost_done,
- sw_done
+ sw_literal,
+ sw_start_literal_argument,
+ sw_literal_argument,
+ sw_end_literal_argument,
+ sw_almost_done
} state;
state = s->state;
- p = s->buffer->pos;
- while (p < s->buffer->last && state < sw_done) {
- ch = *p++;
+ for (p = s->buffer->pos; p < s->buffer->last; p++) {
+ ch = *p;
switch (state) {
- /* POP3 command */
+ /* IMAP tag */
+ case sw_start:
+ switch (ch) {
+ case ' ':
+ s->tag.len = p - s->buffer->start + 1;
+ s->tag.data = s->buffer->start;
+ state = sw_spaces_before_command;
+ break;
+ case CR:
+ s->state = sw_start;
+ return NGX_IMAP_PARSE_INVALID_COMMAND;
+ case LF:
+ s->state = sw_start;
+ return NGX_IMAP_PARSE_INVALID_COMMAND;
+ }
+ break;
+
+ case sw_spaces_before_command:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ s->state = sw_start;
+ return NGX_IMAP_PARSE_INVALID_COMMAND;
+ case LF:
+ s->state = sw_start;
+ return NGX_IMAP_PARSE_INVALID_COMMAND;
+ default:
+ s->cmd_start = p;
+ state = sw_command;
+ break;
+ }
+ break;
+
+ case sw_command:
+ if (ch == ' ' || ch == CR || ch == LF) {
+
+ c = s->cmd_start;
+
+ switch (p - c) {
+
+ case 4:
+ if ((c[0] == 'N' || c[0] == 'n')
+ && (c[1] == 'O'|| c[1] == 'o')
+ && (c[2] == 'O'|| c[2] == 'o')
+ && (c[3] == 'P'|| c[3] == 'p'))
+ {
+ s->command = NGX_IMAP_NOOP;
+
+ } else {
+ goto invalid;
+ }
+ break;
+
+ case 5:
+ if ((c[0] == 'L'|| c[0] == 'l')
+ && (c[1] == 'O'|| c[1] == 'o')
+ && (c[2] == 'G'|| c[2] == 'g')
+ && (c[3] == 'I'|| c[3] == 'i')
+ && (c[4] == 'N'|| c[4] == 'n'))
+ {
+ s->command = NGX_IMAP_LOGIN;
+
+ } else {
+ goto invalid;
+ }
+ break;
+
+ case 6:
+ if ((c[0] == 'L'|| c[0] == 'l')
+ && (c[1] == 'O'|| c[1] == 'o')
+ && (c[2] == 'G'|| c[2] == 'g')
+ && (c[3] == 'O'|| c[3] == 'o')
+ && (c[4] == 'U'|| c[4] == 'u')
+ && (c[5] == 'T'|| c[5] == 't'))
+ {
+ s->command = NGX_IMAP_LOGOUT;
+
+ } else {
+ goto invalid;
+ }
+ break;
+
+ case 10:
+ if ((c[0] == 'C'|| c[0] == 'c')
+ && (c[1] == 'A'|| c[1] == 'a')
+ && (c[2] == 'P'|| c[2] == 'p')
+ && (c[3] == 'A'|| c[3] == 'a')
+ && (c[4] == 'B'|| c[4] == 'b')
+ && (c[5] == 'I'|| c[5] == 'i')
+ && (c[6] == 'L'|| c[6] == 'l')
+ && (c[7] == 'I'|| c[7] == 'i')
+ && (c[8] == 'T'|| c[8] == 't')
+ && (c[9] == 'Y'|| c[9] == 'y'))
+ {
+ s->command = NGX_IMAP_CAPABILITY;
+
+ } else {
+ goto invalid;
+ }
+ break;
+
+ default:
+ goto invalid;
+ }
+
+ switch (ch) {
+ case ' ':
+ state = sw_spaces_before_argument;
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+ }
+
+ if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
+ goto invalid;
+ }
+
+ break;
+
+ case sw_spaces_before_argument:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ state = sw_almost_done;
+ s->arg_end = p;
+ break;
+ case LF:
+ s->arg_end = p;
+ goto done;
+ case '"':
+ if (s->args.nelts <= 2) {
+ s->quoted = 1;
+ s->arg_start = p + 1;
+ state = sw_argument;
+ break;
+ }
+ goto invalid;
+ case '{':
+ if (s->args.nelts <= 2) {
+ state = sw_literal;
+ break;
+ }
+ goto invalid;
+ default:
+ if (s->args.nelts <= 2) {
+ s->arg_start = p;
+ state = sw_argument;
+ break;
+ }
+ goto invalid;
+ }
+ break;
+
+ case sw_argument:
+ switch (ch) {
+ case '"':
+ if (!s->quoted) {
+ break;
+ }
+ s->quoted = 0;
+ /* fall through */
+ case ' ':
+ case CR:
+ case LF:
+ arg = ngx_array_push(&s->args);
+ if (arg == NULL) {
+ return NGX_ERROR;
+ }
+ arg->len = p - s->arg_start;
+ arg->data = s->arg_start;
+ s->arg_start = NULL;
+
+ switch (ch) {
+ case '"':
+ case ' ':
+ state = sw_spaces_before_argument;
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+ }
+ break;
+
+ case sw_literal:
+ if (ch >= '0' && ch <= '9') {
+ s->literal_len = s->literal_len * 10 + (ch - '0');
+ break;
+ }
+ if (ch == '}') {
+ state = sw_start_literal_argument;
+ break;
+ }
+ goto invalid;
+
+ case sw_start_literal_argument:
+ switch (ch) {
+ case CR:
+ break;
+ case LF:
+ s->buffer->pos = p + 1;
+ s->arg_start = p + 1;
+ s->state = sw_literal_argument;
+ return NGX_IMAP_NEXT;
+ }
+ goto invalid;
+
+ case sw_literal_argument:
+ if (--s->literal_len) {
+ break;
+ }
+
+ arg = ngx_array_push(&s->args);
+ if (arg == NULL) {
+ return NGX_ERROR;
+ }
+ arg->len = p + 1 - s->arg_start;
+ arg->data = s->arg_start;
+ s->arg_start = NULL;
+ state = sw_end_literal_argument;
+
+ break;
+
+ case sw_end_literal_argument:
+ switch (ch) {
+ case '{':
+ if (s->args.nelts <= 2) {
+ state = sw_literal;
+ break;
+ }
+ goto invalid;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ default:
+ goto invalid;
+ }
+ break;
+ case sw_almost_done:
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ goto invalid;
+ }
+ }
+ }
+
+ s->buffer->pos = p;
+ s->state = state;
+
+ return NGX_AGAIN;
+
+done:
+
+ s->buffer->pos = p + 1;
+
+ if (s->arg_start) {
+ arg = ngx_array_push(&s->args);
+ if (arg == NULL) {
+ return NGX_ERROR;
+ }
+ arg->len = s->arg_end - s->arg_start;
+ arg->data = s->arg_start;
+ s->arg_start = NULL;
+ s->cmd_start = NULL;
+ s->quoted = 0;
+ s->literal_len = 0;
+ }
+
+ s->state = sw_start;
+
+ return NGX_OK;
+
+invalid:
+
+ s->state = sw_start;
+ s->quoted = 0;
+ s->literal_len = 0;
+
+ return NGX_IMAP_PARSE_INVALID_COMMAND;
+}
+
+
+ngx_int_t ngx_pop3_parse_command(ngx_imap_session_t *s)
+{
+ u_char ch, *p, *c, c0, c1, c2, c3;
+ ngx_str_t *arg;
+ enum {
+ sw_start = 0,
+ sw_spaces_before_argument,
+ sw_argument,
+ sw_almost_done
+ } state;
+
+ state = s->state;
+
+ for (p = s->buffer->pos; p < s->buffer->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* POP3 command */
case sw_start:
if (ch == ' ' || ch == CR || ch == LF) {
c = s->buffer->start;
- if (p - 1 - c == 4) {
+ if (p - c == 4) {
+
+ c0 = ngx_toupper(c[0]);
+ c1 = ngx_toupper(c[1]);
+ c2 = ngx_toupper(c[2]);
+ c3 = ngx_toupper(c[3]);
- if (c[0] == 'U' && c[1] == 'S'
- && c[2] == 'E' && c[3] == 'R')
+ if (c0 == 'U' && c1 == 'S' && c2 == 'E' && c3 == 'R')
{
s->command = NGX_POP3_USER;
- } else if (c[0] == 'P' && c[1] == 'A'
- && c[2] == 'S' && c[3] == 'S')
+ } else if (c0 == 'P' && c1 == 'A' && c2 == 'S' && c3 == 'S')
{
s->command = NGX_POP3_PASS;
- } else if (c[0] == 'Q' && c[1] == 'U'
- && c[2] == 'I' && c[3] == 'T')
+ } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T')
{
s->command = NGX_POP3_QUIT;
-#if 0
- } else if (c[0] == 'N' && c[1] == 'O'
- && c[2] == 'O' && c[3] == 'P')
+ } else if (c0 == 'C' && c1 == 'A' && c2 == 'P' && c3 == 'A')
+ {
+ s->command = NGX_POP3_CAPA;
+
+ } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')
{
s->command = NGX_POP3_NOOP;
-#endif
} else {
- s->state = sw_start;
- return NGX_IMAP_PARSE_INVALID_COMMAND;
+ goto invalid;
}
} else {
- s->state = sw_start;
- return NGX_IMAP_PARSE_INVALID_COMMAND;
+ goto invalid;
}
switch (ch) {
@@ -78,45 +397,38 @@ ngx_int_t ngx_pop3_parse_command(ngx_imap_session_t *s)
state = sw_almost_done;
break;
case LF:
- state = sw_done;
- break;
+ goto done;
}
break;
}
- if (ch < 'A' || ch > 'Z') {
- s->state = sw_start;
- return NGX_IMAP_PARSE_INVALID_COMMAND;
+ if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
+ goto invalid;
}
break;
- /* the spaces before the argument */
case sw_spaces_before_argument:
switch (ch) {
case ' ':
break;
case CR:
state = sw_almost_done;
- s->arg_end = p - 1;
+ s->arg_end = p;
break;
case LF:
- state = sw_done;
- s->arg_end = p - 1;
- break;
+ s->arg_end = p;
+ goto done;
default:
- if (s->args.nelts > 2) {
- s->state = sw_start;
- return NGX_IMAP_PARSE_INVALID_COMMAND;
+ if (s->args.nelts <= 2) {
+ state = sw_argument;
+ s->arg_start = p;
+ break;
}
-
- state = sw_argument;
- s->arg_start = p - 1;
- break;
+ goto invalid;
}
break;
- /* the argument */
case sw_argument:
switch (ch) {
case ' ':
@@ -126,7 +438,7 @@ ngx_int_t ngx_pop3_parse_command(ngx_imap_session_t *s)
if (arg == NULL) {
return NGX_ERROR;
}
- arg->len = p - 1 - s->arg_start;
+ arg->len = p - s->arg_start;
arg->data = s->arg_start;
s->arg_start = NULL;
@@ -138,8 +450,7 @@ ngx_int_t ngx_pop3_parse_command(ngx_imap_session_t *s)
state = sw_almost_done;
break;
case LF:
- state = sw_done;
- break;
+ goto done;
}
break;
@@ -148,42 +459,42 @@ ngx_int_t ngx_pop3_parse_command(ngx_imap_session_t *s)
}
break;
- /* end of request line */
case sw_almost_done:
switch (ch) {
case LF:
- state = sw_done;
- break;
+ goto done;
default:
- s->state = sw_start;
- return NGX_IMAP_PARSE_INVALID_COMMAND;
+ goto invalid;
}
- break;
-
- /* suppress warning */
- case sw_done:
- break;
}
}
s->buffer->pos = p;
+ s->state = state;
- if (state == sw_done) {
- if (s->arg_start) {
- arg = ngx_array_push(&s->args);
- if (arg == NULL) {
- return NGX_ERROR;
- }
- arg->len = s->arg_end - s->arg_start;
- arg->data = s->arg_start;
- s->arg_start = NULL;
- }
+ return NGX_AGAIN;
+
+done:
- s->state = sw_start;
- return NGX_OK;
+ s->buffer->pos = p + 1;
- } else {
- s->state = state;
- return NGX_AGAIN;
+ if (s->arg_start) {
+ arg = ngx_array_push(&s->args);
+ if (arg == NULL) {
+ return NGX_ERROR;
+ }
+ arg->len = s->arg_end - s->arg_start;
+ arg->data = s->arg_start;
+ s->arg_start = NULL;
}
+
+ s->state = sw_start;
+
+ return NGX_OK;
+
+invalid:
+
+ s->state = sw_start;
+
+ return NGX_IMAP_PARSE_INVALID_COMMAND;
}
diff --git a/src/imap/ngx_imap_proxy_module.c b/src/imap/ngx_imap_proxy_module.c
index b3bedae58..5177e289f 100644
--- a/src/imap/ngx_imap_proxy_module.c
+++ b/src/imap/ngx_imap_proxy_module.c
@@ -17,16 +17,23 @@ typedef struct {
static void ngx_imap_proxy_block_read(ngx_event_t *rev);
-static void ngx_imap_proxy_auth_handler(ngx_event_t *rev);
+static void ngx_imap_proxy_imap_handler(ngx_event_t *rev);
+static void ngx_imap_proxy_pop3_handler(ngx_event_t *rev);
static void ngx_imap_proxy_dummy_handler(ngx_event_t *ev);
-static ngx_int_t ngx_imap_proxy_read_response(ngx_imap_session_t *s);
+static ngx_int_t ngx_imap_proxy_read_response(ngx_imap_session_t *s,
+ ngx_uint_t what);
static void ngx_imap_proxy_handler(ngx_event_t *ev);
+static void ngx_imap_proxy_internal_server_error(ngx_imap_session_t *s);
static void ngx_imap_proxy_close_session(ngx_imap_session_t *s);
static void *ngx_imap_proxy_create_conf(ngx_conf_t *cf);
static char *ngx_imap_proxy_merge_conf(ngx_conf_t *cf, void *parent,
void *child);
+#define NGX_IMAP_WAIT_OK 0
+#define NGX_IMAP_WAIT_NEXT 1
+
+
static ngx_command_t ngx_imap_proxy_commands[] = {
{ ngx_string("proxy"),
NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_FLAG,
@@ -61,12 +68,13 @@ ngx_module_t ngx_imap_proxy_module = {
void
ngx_imap_proxy_init(ngx_imap_session_t *s, ngx_peers_t *peers)
{
- ngx_int_t rc;
- ngx_imap_proxy_ctx_t *p;
+ ngx_int_t rc;
+ ngx_imap_proxy_ctx_t *p;
+ ngx_imap_core_srv_conf_t *cscf;
p = ngx_pcalloc(s->connection->pool, sizeof(ngx_imap_proxy_ctx_t));
if (p == NULL) {
- ngx_imap_close_connection(s->connection);
+ ngx_imap_session_internal_server_error(s);
return;
}
@@ -79,16 +87,27 @@ ngx_imap_proxy_init(ngx_imap_session_t *s, ngx_peers_t *peers)
rc = ngx_event_connect_peer(&p->upstream);
if (rc == NGX_ERROR) {
- ngx_imap_proxy_close_session(s);
+ ngx_imap_session_internal_server_error(s);
return;
}
+ cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
+ ngx_add_timer(p->upstream.connection->read, cscf->timeout);
+
p->upstream.connection->data = s;
p->upstream.connection->pool = s->connection->pool;
s->connection->read->handler = ngx_imap_proxy_block_read;
- p->upstream.connection->read->handler = ngx_imap_proxy_auth_handler;
p->upstream.connection->write->handler = ngx_imap_proxy_dummy_handler;
+
+ if (s->protocol == NGX_IMAP_POP3_PROTOCOL) {
+ p->upstream.connection->read->handler = ngx_imap_proxy_pop3_handler;
+ s->imap_state = ngx_pop3_start;
+
+ } else {
+ p->upstream.connection->read->handler = ngx_imap_proxy_imap_handler;
+ s->imap_state = ngx_imap_start;
+ }
}
@@ -110,106 +129,242 @@ ngx_imap_proxy_block_read(ngx_event_t *rev)
static void
-ngx_imap_proxy_auth_handler(ngx_event_t *rev)
+ngx_imap_proxy_imap_handler(ngx_event_t *rev)
{
- u_char *p;
- ngx_int_t rc;
- ngx_str_t line;
- ngx_connection_t *c;
- ngx_imap_session_t *s;
+ u_char *p;
+ ngx_int_t rc;
+ ngx_str_t line;
+ ngx_connection_t *c;
+ ngx_imap_session_t *s;
+ ngx_imap_core_srv_conf_t *cscf;
- ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy auth handler");
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0,
+ "imap proxy imap auth handler");
c = rev->data;
s = c->data;
if (rev->timedout) {
- ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
- ngx_imap_proxy_close_session(s);
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "upstream timed out");
+ ngx_imap_proxy_internal_server_error(s);
return;
}
if (s->proxy->buffer == NULL) {
- s->proxy->buffer = ngx_create_temp_buf(c->pool, /* STUB */ 4096);
+ cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
+
+ s->proxy->buffer = ngx_create_temp_buf(c->pool,
+ cscf->proxy_buffer_size);
if (s->proxy->buffer == NULL) {
- ngx_imap_proxy_close_session(s);
+ ngx_imap_proxy_internal_server_error(s);
return;
}
}
- rc = ngx_imap_proxy_read_response(s);
+ rc = ngx_imap_proxy_read_response(s, s->imap_state == ngx_imap_start ?
+ NGX_IMAP_WAIT_OK : NGX_IMAP_WAIT_NEXT);
if (rc == NGX_AGAIN) {
return;
}
if (rc == NGX_ERROR) {
- /* TODO: ngx_imap_proxy_finalize_session(s, NGX_IMAP_INTERNAL_ERROR) */
- ngx_imap_proxy_close_session(s);
+ ngx_imap_proxy_internal_server_error(s);
return;
}
- if (s->imap_state == ngx_pop3_start) {
+ switch (s->imap_state) {
+
+ case ngx_imap_start:
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0,
+ "imap proxy send login");
+ line.len = s->tag.len + sizeof("LOGIN ") - 1
+ + 1 + NGX_SIZE_T_LEN + 1 + 2;
+ line.data = ngx_palloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_imap_proxy_internal_server_error(s);
+ return;
+ }
+
+ line.len = ngx_sprintf(line.data, "%VLOGIN {%uz}" CRLF,
+ &s->tag, s->login.len)
+ - line.data;
+
+ s->imap_state = ngx_imap_login;
+ break;
+
+ case ngx_imap_login:
ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy send user");
- line.len = sizeof("USER ") + s->login.len - 1 + 2;
+ line.len = s->login.len + 1 + NGX_SIZE_T_LEN + 1 + 2;
line.data = ngx_palloc(c->pool, line.len);
if (line.data == NULL) {
- ngx_imap_proxy_close_session(s);
+ ngx_imap_proxy_internal_server_error(s);
return;
}
- p = ngx_cpymem(line.data, "USER ", sizeof("USER ") - 1);
- p = ngx_cpymem(p, s->login.data, s->login.len);
- *p++ = CR; *p = LF;
+ line.len = ngx_sprintf(line.data, "%V{%uz}" CRLF,
+ &s->login, s->passwd.len)
+ - line.data;
+
+ s->imap_state = ngx_imap_user;
+ break;
+
+ case ngx_imap_user:
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0,
+ "imap proxy send passwd");
- if (ngx_send(c, line.data, line.len) < (ssize_t) line.len) {
- /*
- * we treat the incomplete sending as NGX_ERROR
- * because it is very strange here
- */
- ngx_imap_close_connection(c);
+ line.len = s->passwd.len + 2;
+ line.data = ngx_palloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_imap_proxy_internal_server_error(s);
return;
}
- s->imap_state = ngx_pop3_user;
+ p = ngx_cpymem(line.data, s->passwd.data, s->passwd.len);
+ *p++ = CR; *p = LF;
+
+ s->imap_state = ngx_imap_passwd;
+ break;
- s->proxy->buffer->pos = s->proxy->buffer->start;
- s->proxy->buffer->last = s->proxy->buffer->start;
+ default:
+#if (NGX_SUPPRESS_WARN)
+ line.len = 0;
+ line.data = NULL;
+#endif
+ break;
+ }
+ if (ngx_send(c, line.data, line.len) < (ssize_t) line.len) {
+ /*
+ * we treat the incomplete sending as NGX_ERROR
+ * because it is very strange here
+ */
+ ngx_imap_proxy_internal_server_error(s);
return;
}
- ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy send pass");
+ s->proxy->buffer->pos = s->proxy->buffer->start;
+ s->proxy->buffer->last = s->proxy->buffer->start;
- line.len = sizeof("PASS ") + s->passwd.len - 1 + 2;
- line.data = ngx_palloc(c->pool, line.len);
- if (line.data == NULL) {
- ngx_imap_proxy_close_session(s);
+ if (s->imap_state == ngx_imap_passwd) {
+ s->connection->read->handler = ngx_imap_proxy_handler;
+ s->connection->write->handler = ngx_imap_proxy_handler;
+ rev->handler = ngx_imap_proxy_handler;
+ c->write->handler = ngx_imap_proxy_handler;
+ }
+}
+
+
+static void
+ngx_imap_proxy_pop3_handler(ngx_event_t *rev)
+{
+ u_char *p;
+ ngx_int_t rc;
+ ngx_str_t line;
+ ngx_connection_t *c;
+ ngx_imap_session_t *s;
+ ngx_imap_core_srv_conf_t *cscf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0,
+ "imap proxy pop3 auth handler");
+
+ c = rev->data;
+ s = c->data;
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "upstream timed out");
+ ngx_imap_proxy_internal_server_error(s);
+ return;
+ }
+
+ if (s->proxy->buffer == NULL) {
+ cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
+
+ s->proxy->buffer = ngx_create_temp_buf(c->pool,
+ cscf->proxy_buffer_size);
+ if (s->proxy->buffer == NULL) {
+ ngx_imap_proxy_internal_server_error(s);
+ return;
+ }
+ }
+
+ rc = ngx_imap_proxy_read_response(s, NGX_IMAP_WAIT_OK);
+
+ if (rc == NGX_AGAIN) {
return;
}
- p = ngx_cpymem(line.data, "PASS ", sizeof("PASS ") - 1);
- p = ngx_cpymem(p, s->passwd.data, s->passwd.len);
- *p++ = CR; *p = LF;
+ if (rc == NGX_ERROR) {
+ ngx_imap_proxy_internal_server_error(s);
+ return;
+ }
+
+ switch (s->imap_state) {
+
+ case ngx_pop3_start:
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy send user");
+
+ line.len = sizeof("USER ") - 1 + s->login.len + 2;
+ line.data = ngx_palloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_imap_proxy_internal_server_error(s);
+ return;
+ }
+
+ p = ngx_cpymem(line.data, "USER ", sizeof("USER ") - 1);
+ p = ngx_cpymem(p, s->login.data, s->login.len);
+ *p++ = CR; *p = LF;
+
+ s->imap_state = ngx_pop3_user;
+ break;
+
+ case ngx_pop3_user:
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy send pass");
+
+ line.len = sizeof("PASS ") - 1 + s->passwd.len + 2;
+ line.data = ngx_palloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_imap_proxy_internal_server_error(s);
+ return;
+ }
+
+ p = ngx_cpymem(line.data, "PASS ", sizeof("PASS ") - 1);
+ p = ngx_cpymem(p, s->passwd.data, s->passwd.len);
+ *p++ = CR; *p = LF;
+
+ s->imap_state = ngx_pop3_passwd;
+ break;
+
+ default:
+#if (NGX_SUPPRESS_WARN)
+ line.len = 0;
+ line.data = NULL;
+#endif
+ break;
+ }
if (ngx_send(c, line.data, line.len) < (ssize_t) line.len) {
/*
* we treat the incomplete sending as NGX_ERROR
* because it is very strange here
*/
- ngx_imap_close_connection(c);
+ ngx_imap_proxy_internal_server_error(s);
return;
}
s->proxy->buffer->pos = s->proxy->buffer->start;
s->proxy->buffer->last = s->proxy->buffer->start;
- s->connection->read->handler = ngx_imap_proxy_handler;
- s->connection->write->handler = ngx_imap_proxy_handler;
- rev->handler = ngx_imap_proxy_handler;
- c->write->handler = ngx_imap_proxy_handler;
+ if (s->imap_state == ngx_pop3_passwd) {
+ s->connection->read->handler = ngx_imap_proxy_handler;
+ s->connection->write->handler = ngx_imap_proxy_handler;
+ rev->handler = ngx_imap_proxy_handler;
+ c->write->handler = ngx_imap_proxy_handler;
+ }
}
@@ -221,7 +376,7 @@ ngx_imap_proxy_dummy_handler(ngx_event_t *ev)
static ngx_int_t
-ngx_imap_proxy_read_response(ngx_imap_session_t *s)
+ngx_imap_proxy_read_response(ngx_imap_session_t *s, ngx_uint_t what)
{
u_char *p;
ssize_t n;
@@ -259,17 +414,31 @@ ngx_imap_proxy_read_response(ngx_imap_session_t *s)
p = b->pos;
- if (p[0] == '+' && p[1] == 'O' && p[2] == 'K') {
- return NGX_OK;
- }
+ if (s->protocol == NGX_IMAP_POP3_PROTOCOL) {
+ if (p[0] == '+' && p[1] == 'O' && p[2] == 'K') {
+ return NGX_OK;
+ }
- if (p[0] == '-' && p[1] == 'E' && p[2] == 'R' && p[3] == 'R') {
- return NGX_IMAP_PROXY_ERROR;
+ if (p[0] == '-' && p[1] == 'E' && p[2] == 'R' && p[3] == 'R') {
+ return NGX_IMAP_PROXY_ERROR;
+ }
+
+ } else {
+ if (what == NGX_IMAP_WAIT_OK) {
+ if (p[0] == '*' && p[1] == ' ' && p[2] == 'O' && p[3] == 'K') {
+ return NGX_OK;
+ }
+
+ } else {
+ if (p[0] == '+' && p[1] == ' ' && p[2] == 'O' && p[3] == 'K') {
+ return NGX_OK;
+ }
+ }
}
*(b->last - 2) = '\0';
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
- "upstream sent invalid greeting line: \"%s\"", p);
+ "upstream sent invalid response: \"%s\"", p);
return NGX_IMAP_PROXY_INVALID;
}
@@ -397,6 +566,21 @@ ngx_imap_proxy_handler(ngx_event_t *ev)
static void
+ngx_imap_proxy_internal_server_error(ngx_imap_session_t *s)
+{
+ if (s->proxy->upstream.connection) {
+ ngx_log_debug1(NGX_LOG_DEBUG_IMAP, s->connection->log, 0,
+ "close imap proxy connection: %d",
+ s->proxy->upstream.connection->fd);
+
+ ngx_close_connection(s->proxy->upstream.connection);
+ }
+
+ ngx_imap_session_internal_server_error(s);
+}
+
+
+static void
ngx_imap_proxy_close_session(ngx_imap_session_t *s)
{
if (s->proxy->upstream.connection) {