Mercurial > hg > nginx
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 } |