comparison src/mail/ngx_mail_pop3_handler.c @ 1476:67578e966dcc

split pop3, imap, and smtp handlers
author Igor Sysoev <igor@sysoev.ru>
date Thu, 13 Sep 2007 20:13:18 +0000
parents src/mail/ngx_mail_handler.c@32450a2bbdf4
children 59e1caf2be94
comparison
equal deleted inserted replaced
1475:732fe8258857 1476:67578e966dcc
1
2 /*
3 * Copyright (C) Igor Sysoev
4 */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_event.h>
10 #include <ngx_mail.h>
11
12
13 static ngx_int_t ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c);
14 static ngx_int_t ngx_mail_pop3_pass(ngx_mail_session_t *s, ngx_connection_t *c);
15 static ngx_int_t ngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c,
16 ngx_int_t stls);
17 static ngx_int_t ngx_mail_pop3_stls(ngx_mail_session_t *s, ngx_connection_t *c);
18 static ngx_int_t ngx_mail_pop3_apop(ngx_mail_session_t *s, ngx_connection_t *c);
19 static ngx_int_t ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c);
20
21
22 static u_char pop3_greeting[] = "+OK POP3 ready" CRLF;
23 static u_char pop3_ok[] = "+OK" CRLF;
24 static u_char pop3_next[] = "+ " CRLF;
25 static u_char pop3_username[] = "+ VXNlcm5hbWU6" CRLF;
26 static u_char pop3_password[] = "+ UGFzc3dvcmQ6" CRLF;
27 static u_char pop3_invalid_command[] = "-ERR invalid command" CRLF;
28
29
30 void
31 ngx_mail_pop3_init_session(ngx_mail_session_t *s, ngx_connection_t *c)
32 {
33 u_char *p;
34 ngx_mail_core_srv_conf_t *cscf;
35
36 cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
37
38 if (cscf->pop3_auth_methods
39 & (NGX_MAIL_AUTH_APOP_ENABLED|NGX_MAIL_AUTH_CRAM_MD5_ENABLED))
40 {
41 if (ngx_mail_salt(s, c, cscf) != NGX_OK) {
42 ngx_mail_session_internal_server_error(s);
43 return;
44 }
45
46 s->out.data = ngx_palloc(c->pool, sizeof(pop3_greeting) + s->salt.len);
47 if (s->out.data == NULL) {
48 ngx_mail_session_internal_server_error(s);
49 return;
50 }
51
52 p = ngx_cpymem(s->out.data, pop3_greeting, sizeof(pop3_greeting) - 3);
53 *p++ = ' ';
54 p = ngx_cpymem(p, s->salt.data, s->salt.len);
55
56 s->out.len = p - s->out.data;
57
58 } else {
59 s->out.len = sizeof(pop3_greeting) - 1;
60 s->out.data = pop3_greeting;
61 }
62
63 c->read->handler = ngx_mail_pop3_init_protocol;
64
65 ngx_mail_send(c->write);
66 }
67
68
69 void
70 ngx_mail_pop3_init_protocol(ngx_event_t *rev)
71 {
72 ngx_connection_t *c;
73 ngx_mail_session_t *s;
74
75 c = rev->data;
76
77 c->log->action = "in auth state";
78
79 if (rev->timedout) {
80 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
81 c->timedout = 1;
82 ngx_mail_close_connection(c);
83 return;
84 }
85
86 s = c->data;
87
88 if (s->buffer == NULL) {
89 if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t))
90 == NGX_ERROR)
91 {
92 ngx_mail_session_internal_server_error(s);
93 return;
94 }
95
96 s->buffer = ngx_create_temp_buf(c->pool, 128);
97 if (s->buffer == NULL) {
98 ngx_mail_session_internal_server_error(s);
99 return;
100 }
101 }
102
103 s->mail_state = ngx_pop3_start;
104 c->read->handler = ngx_pop3_auth_state;
105
106 ngx_pop3_auth_state(rev);
107 }
108
109
110 void
111 ngx_pop3_auth_state(ngx_event_t *rev)
112 {
113 ngx_int_t rc;
114 ngx_connection_t *c;
115 ngx_mail_session_t *s;
116
117 c = rev->data;
118 s = c->data;
119
120 ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 auth state");
121
122 if (rev->timedout) {
123 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
124 c->timedout = 1;
125 ngx_mail_close_connection(c);
126 return;
127 }
128
129 if (s->out.len) {
130 ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 send handler busy");
131 s->blocked = 1;
132 return;
133 }
134
135 s->blocked = 0;
136
137 rc = ngx_mail_read_command(s);
138
139 if (rc == NGX_AGAIN || rc == NGX_ERROR) {
140 return;
141 }
142
143 s->out.len = sizeof(pop3_ok) - 1;
144 s->out.data = pop3_ok;
145
146 if (rc == NGX_OK) {
147 switch (s->mail_state) {
148
149 case ngx_pop3_start:
150
151 switch (s->command) {
152
153 case NGX_POP3_USER:
154 rc = ngx_mail_pop3_user(s, c);
155 break;
156
157 case NGX_POP3_CAPA:
158 rc = ngx_mail_pop3_capa(s, c, 1);
159 break;
160
161 case NGX_POP3_APOP:
162 rc = ngx_mail_pop3_apop(s, c);
163 break;
164
165 case NGX_POP3_AUTH:
166 rc = ngx_mail_pop3_auth(s, c);
167 break;
168
169 case NGX_POP3_QUIT:
170 s->quit = 1;
171 break;
172
173 case NGX_POP3_NOOP:
174 break;
175
176 case NGX_POP3_STLS:
177 rc = ngx_mail_pop3_stls(s, c);
178 break;
179
180 default:
181 rc = NGX_MAIL_PARSE_INVALID_COMMAND;
182 s->mail_state = ngx_pop3_start;
183 break;
184 }
185
186 break;
187
188 case ngx_pop3_user:
189
190 switch (s->command) {
191
192 case NGX_POP3_PASS:
193 rc = ngx_mail_pop3_pass(s, c);
194 break;
195
196 case NGX_POP3_CAPA:
197 rc = ngx_mail_pop3_capa(s, c, 0);
198 break;
199
200 case NGX_POP3_QUIT:
201 s->quit = 1;
202 break;
203
204 case NGX_POP3_NOOP:
205 break;
206
207 default:
208 rc = NGX_MAIL_PARSE_INVALID_COMMAND;
209 s->mail_state = ngx_pop3_start;
210 break;
211 }
212
213 break;
214
215 /* suppress warinings */
216 case ngx_pop3_passwd:
217 break;
218
219 case ngx_pop3_auth_login_username:
220 rc = ngx_mail_auth_login_username(s, c);
221
222 s->out.len = sizeof(pop3_password) - 1;
223 s->out.data = pop3_password;
224 s->mail_state = ngx_pop3_auth_login_password;
225 break;
226
227 case ngx_pop3_auth_login_password:
228 rc = ngx_mail_auth_login_password(s, c);
229 break;
230
231 case ngx_pop3_auth_plain:
232 rc = ngx_mail_auth_plain(s, c, 0);
233 break;
234
235 case ngx_pop3_auth_cram_md5:
236 rc = ngx_mail_auth_cram_md5(s, c);
237 break;
238 }
239 }
240
241 switch (rc) {
242
243 case NGX_DONE:
244 ngx_mail_auth(s);
245 return;
246
247 case NGX_ERROR:
248 ngx_mail_session_internal_server_error(s);
249 return;
250
251 case NGX_MAIL_PARSE_INVALID_COMMAND:
252 s->mail_state = ngx_pop3_start;
253 s->state = 0;
254
255 s->out.len = sizeof(pop3_invalid_command) - 1;
256 s->out.data = pop3_invalid_command;
257
258 /* fall through */
259
260 case NGX_OK:
261
262 s->args.nelts = 0;
263 s->buffer->pos = s->buffer->start;
264 s->buffer->last = s->buffer->start;
265
266 if (s->state) {
267 s->arg_start = s->buffer->start;
268 }
269
270 ngx_mail_send(c->write);
271 }
272 }
273
274 static ngx_int_t
275 ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c)
276 {
277 ngx_str_t *arg;
278 #if (NGX_MAIL_SSL)
279 ngx_mail_ssl_conf_t *sslcf;
280
281 if (c->ssl == NULL) {
282 sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
283
284 if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
285 return NGX_MAIL_PARSE_INVALID_COMMAND;
286 }
287 }
288
289 #endif
290
291 if (s->args.nelts != 1) {
292 return NGX_MAIL_PARSE_INVALID_COMMAND;
293 }
294
295 arg = s->args.elts;
296 s->login.len = arg[0].len;
297 s->login.data = ngx_palloc(c->pool, s->login.len);
298 if (s->login.data == NULL) {
299 return NGX_ERROR;
300 }
301
302 ngx_memcpy(s->login.data, arg[0].data, s->login.len);
303
304 ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
305 "pop3 login: \"%V\"", &s->login);
306
307 s->mail_state = ngx_pop3_user;
308
309 return NGX_OK;
310 }
311
312
313 static ngx_int_t
314 ngx_mail_pop3_pass(ngx_mail_session_t *s, ngx_connection_t *c)
315 {
316 ngx_str_t *arg;
317
318 if (s->args.nelts != 1) {
319 return NGX_MAIL_PARSE_INVALID_COMMAND;
320 }
321
322 arg = s->args.elts;
323 s->passwd.len = arg[0].len;
324 s->passwd.data = ngx_palloc(c->pool, s->passwd.len);
325 if (s->passwd.data == NULL) {
326 return NGX_ERROR;
327 }
328
329 ngx_memcpy(s->passwd.data, arg[0].data, s->passwd.len);
330
331 #if (NGX_DEBUG_MAIL_PASSWD)
332 ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
333 "pop3 passwd: \"%V\"", &s->passwd);
334 #endif
335
336 return NGX_DONE;
337 }
338
339
340 static ngx_int_t
341 ngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c, ngx_int_t stls)
342 {
343 ngx_mail_core_srv_conf_t *cscf;
344 #if (NGX_MAIL_SSL)
345 ngx_mail_ssl_conf_t *sslcf;
346 #endif
347
348 cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
349
350 #if (NGX_MAIL_SSL)
351
352 if (stls && c->ssl == NULL) {
353 sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
354
355 if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {
356 s->out = cscf->pop3_starttls_capability;
357 return NGX_OK;
358 }
359
360 if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
361 s->out = cscf->pop3_starttls_only_capability;
362 return NGX_OK;
363 }
364 }
365
366 #endif
367
368 s->out = cscf->pop3_capability;
369 return NGX_OK;
370 }
371
372
373 static ngx_int_t
374 ngx_mail_pop3_stls(ngx_mail_session_t *s, ngx_connection_t *c)
375 {
376 #if (NGX_MAIL_SSL)
377 ngx_mail_ssl_conf_t *sslcf;
378
379 if (c->ssl == NULL) {
380 sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
381 if (sslcf->starttls) {
382 c->read->handler = ngx_mail_starttls_handler;
383 return NGX_OK;
384 }
385 }
386
387 #endif
388
389 return NGX_MAIL_PARSE_INVALID_COMMAND;
390 }
391
392
393 static ngx_int_t
394 ngx_mail_pop3_apop(ngx_mail_session_t *s, ngx_connection_t *c)
395 {
396 ngx_str_t *arg;
397 ngx_mail_core_srv_conf_t *cscf;
398 #if (NGX_MAIL_SSL)
399 ngx_mail_ssl_conf_t *sslcf;
400
401 if (c->ssl == NULL) {
402 sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
403
404 if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
405 return NGX_MAIL_PARSE_INVALID_COMMAND;
406 }
407 }
408
409 #endif
410
411 if (s->args.nelts != 2) {
412 return NGX_MAIL_PARSE_INVALID_COMMAND;
413 }
414
415 cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
416
417 if (!(cscf->pop3_auth_methods & NGX_MAIL_AUTH_APOP_ENABLED)) {
418 return NGX_MAIL_PARSE_INVALID_COMMAND;
419 }
420
421 arg = s->args.elts;
422
423 s->login.len = arg[0].len;
424 s->login.data = ngx_palloc(c->pool, s->login.len);
425 if (s->login.data == NULL) {
426 return NGX_ERROR;
427 }
428
429 ngx_memcpy(s->login.data, arg[0].data, s->login.len);
430
431 s->passwd.len = arg[1].len;
432 s->passwd.data = ngx_palloc(c->pool, s->passwd.len);
433 if (s->passwd.data == NULL) {
434 return NGX_ERROR;
435 }
436
437 ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len);
438
439 ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
440 "pop3 apop: \"%V\" \"%V\"", &s->login, &s->passwd);
441
442 s->auth_method = NGX_MAIL_AUTH_APOP;
443
444 return NGX_DONE;
445 }
446
447
448 static ngx_int_t
449 ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c)
450 {
451 size_t n;
452 u_char *p;
453 ngx_str_t *arg, salt;
454 ngx_mail_core_srv_conf_t *cscf;
455 #if (NGX_MAIL_SSL)
456 ngx_mail_ssl_conf_t *sslcf;
457
458 if (c->ssl == NULL) {
459 sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
460
461 if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
462 return NGX_MAIL_PARSE_INVALID_COMMAND;
463 }
464 }
465
466 #endif
467
468 cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
469
470 if (s->args.nelts == 0) {
471 s->out = cscf->pop3_auth_capability;
472 s->state = 0;
473
474 return NGX_OK;
475 }
476
477 arg = s->args.elts;
478
479 if (arg[0].len == 5) {
480
481 if (ngx_strncasecmp(arg[0].data, (u_char *) "LOGIN", 5) == 0) {
482
483 if (s->args.nelts != 1) {
484 return NGX_MAIL_PARSE_INVALID_COMMAND;
485 }
486
487 s->out.len = sizeof(pop3_username) - 1;
488 s->out.data = pop3_username;
489 s->mail_state = ngx_pop3_auth_login_username;
490
491 return NGX_OK;
492
493 } else if (ngx_strncasecmp(arg[0].data, (u_char *) "PLAIN", 5) == 0) {
494
495 if (s->args.nelts == 1) {
496
497 s->out.len = sizeof(pop3_next) - 1;
498 s->out.data = pop3_next;
499 s->mail_state = ngx_pop3_auth_plain;
500
501 return NGX_OK;
502 }
503
504 if (s->args.nelts == 2) {
505
506 /*
507 * workaround for Eudora for Mac: it sends
508 * AUTH PLAIN [base64 encoded]
509 */
510
511 return ngx_mail_auth_plain(s, c, 1);
512 }
513
514 return NGX_MAIL_PARSE_INVALID_COMMAND;
515 }
516
517 } else if (arg[0].len == 8
518 && ngx_strncasecmp(arg[0].data, (u_char *) "CRAM-MD5", 8) == 0)
519 {
520 if (s->args.nelts != 1) {
521 return NGX_MAIL_PARSE_INVALID_COMMAND;
522 }
523
524 if (!(cscf->pop3_auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) {
525 return NGX_MAIL_PARSE_INVALID_COMMAND;
526 }
527
528 p = ngx_palloc(c->pool,
529 sizeof("+ " CRLF) - 1
530 + ngx_base64_encoded_length(s->salt.len));
531 if (p == NULL) {
532 return NGX_ERROR;
533 }
534
535 p[0] = '+'; p[1]= ' ';
536 salt.data = &p[2];
537 s->salt.len -= 2;
538
539 ngx_encode_base64(&salt, &s->salt);
540
541 s->salt.len += 2;
542 n = 2 + salt.len;
543 p[n++] = CR; p[n++] = LF;
544
545 s->out.len = n;
546 s->out.data = p;
547 s->mail_state = ngx_pop3_auth_cram_md5;
548
549 return NGX_OK;
550 }
551
552 return NGX_MAIL_PARSE_INVALID_COMMAND;
553 }