upstream nginx-0.7.36
[nginx.git] / nginx / src / mail / ngx_mail_auth_http_module.c
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_event_connect.h>
11 #include <ngx_mail.h>
12
13
14 typedef struct {
15     ngx_peer_addr_t                *peer;
16
17     ngx_msec_t                      timeout;
18
19     ngx_str_t                       host_header;
20     ngx_str_t                       uri;
21     ngx_str_t                       header;
22
23     ngx_array_t                    *headers;
24
25     u_char                         *file;
26     ngx_uint_t                      line;
27 } ngx_mail_auth_http_conf_t;
28
29
30 typedef struct ngx_mail_auth_http_ctx_s  ngx_mail_auth_http_ctx_t;
31
32 typedef void (*ngx_mail_auth_http_handler_pt)(ngx_mail_session_t *s,
33     ngx_mail_auth_http_ctx_t *ctx);
34
35 struct ngx_mail_auth_http_ctx_s {
36     ngx_buf_t                      *request;
37     ngx_buf_t                      *response;
38     ngx_peer_connection_t           peer;
39
40     ngx_mail_auth_http_handler_pt   handler;
41
42     ngx_uint_t                      state;
43
44     u_char                         *header_name_start;
45     u_char                         *header_name_end;
46     u_char                         *header_start;
47     u_char                         *header_end;
48
49     ngx_str_t                       addr;
50     ngx_str_t                       port;
51     ngx_str_t                       err;
52     ngx_str_t                       errmsg;
53     ngx_str_t                       errcode;
54
55     time_t                          sleep;
56
57     ngx_pool_t                     *pool;
58 };
59
60
61 static void ngx_mail_auth_http_write_handler(ngx_event_t *wev);
62 static void ngx_mail_auth_http_read_handler(ngx_event_t *rev);
63 static void ngx_mail_auth_http_ignore_status_line(ngx_mail_session_t *s,
64     ngx_mail_auth_http_ctx_t *ctx);
65 static void ngx_mail_auth_http_process_headers(ngx_mail_session_t *s,
66     ngx_mail_auth_http_ctx_t *ctx);
67 static void ngx_mail_auth_sleep_handler(ngx_event_t *rev);
68 static ngx_int_t ngx_mail_auth_http_parse_header_line(ngx_mail_session_t *s,
69     ngx_mail_auth_http_ctx_t *ctx);
70 static void ngx_mail_auth_http_block_read(ngx_event_t *rev);
71 static void ngx_mail_auth_http_dummy_handler(ngx_event_t *ev);
72 static ngx_buf_t *ngx_mail_auth_http_create_request(ngx_mail_session_t *s,
73     ngx_pool_t *pool, ngx_mail_auth_http_conf_t *ahcf);
74 static ngx_int_t ngx_mail_auth_http_escape(ngx_pool_t *pool, ngx_str_t *text,
75     ngx_str_t *escaped);
76
77 static void *ngx_mail_auth_http_create_conf(ngx_conf_t *cf);
78 static char *ngx_mail_auth_http_merge_conf(ngx_conf_t *cf, void *parent,
79     void *child);
80 static char *ngx_mail_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
81 static char *ngx_mail_auth_http_header(ngx_conf_t *cf, ngx_command_t *cmd,
82     void *conf);
83
84
85 static ngx_command_t  ngx_mail_auth_http_commands[] = {
86
87     { ngx_string("auth_http"),
88       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
89       ngx_mail_auth_http,
90       NGX_MAIL_SRV_CONF_OFFSET,
91       0,
92       NULL },
93
94     { ngx_string("auth_http_timeout"),
95       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
96       ngx_conf_set_msec_slot,
97       NGX_MAIL_SRV_CONF_OFFSET,
98       offsetof(ngx_mail_auth_http_conf_t, timeout),
99       NULL },
100
101     { ngx_string("auth_http_header"),
102       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE2,
103       ngx_mail_auth_http_header,
104       NGX_MAIL_SRV_CONF_OFFSET,
105       0,
106       NULL },
107
108       ngx_null_command
109 };
110
111
112 static ngx_mail_module_t  ngx_mail_auth_http_module_ctx = {
113     NULL,                                  /* protocol */
114
115     NULL,                                  /* create main configuration */
116     NULL,                                  /* init main configuration */
117
118     ngx_mail_auth_http_create_conf,        /* create server configuration */
119     ngx_mail_auth_http_merge_conf          /* merge server configuration */
120 };
121
122
123 ngx_module_t  ngx_mail_auth_http_module = {
124     NGX_MODULE_V1,
125     &ngx_mail_auth_http_module_ctx,        /* module context */
126     ngx_mail_auth_http_commands,           /* module directives */
127     NGX_MAIL_MODULE,                       /* module type */
128     NULL,                                  /* init master */
129     NULL,                                  /* init module */
130     NULL,                                  /* init process */
131     NULL,                                  /* init thread */
132     NULL,                                  /* exit thread */
133     NULL,                                  /* exit process */
134     NULL,                                  /* exit master */
135     NGX_MODULE_V1_PADDING
136 };
137
138
139 static ngx_str_t   ngx_mail_auth_http_method[] = {
140     ngx_string("plain"),
141     ngx_string("plain"),
142     ngx_string("apop"),
143     ngx_string("cram-md5"),
144     ngx_string("none")
145 };
146
147 static ngx_str_t   ngx_mail_smtp_errcode = ngx_string("535 5.7.0");
148
149
150 void
151 ngx_mail_auth_http_init(ngx_mail_session_t *s)
152 {
153     ngx_int_t                   rc;
154     ngx_pool_t                 *pool;
155     ngx_mail_auth_http_ctx_t   *ctx;
156     ngx_mail_auth_http_conf_t  *ahcf;
157
158     s->connection->log->action = "in http auth state";
159
160     pool = ngx_create_pool(2048, s->connection->log);
161     if (pool == NULL) {
162         ngx_mail_session_internal_server_error(s);
163         return;
164     }
165
166     ctx = ngx_pcalloc(pool, sizeof(ngx_mail_auth_http_ctx_t));
167     if (ctx == NULL) {
168         ngx_destroy_pool(pool);
169         ngx_mail_session_internal_server_error(s);
170         return;
171     }
172
173     ctx->pool = pool;
174
175     ahcf = ngx_mail_get_module_srv_conf(s, ngx_mail_auth_http_module);
176
177     ctx->request = ngx_mail_auth_http_create_request(s, pool, ahcf);
178     if (ctx->request == NULL) {
179         ngx_destroy_pool(ctx->pool);
180         ngx_mail_session_internal_server_error(s);
181         return;
182     }
183
184     ngx_mail_set_ctx(s, ctx, ngx_mail_auth_http_module);
185
186     ctx->peer.sockaddr = ahcf->peer->sockaddr;
187     ctx->peer.socklen = ahcf->peer->socklen;
188     ctx->peer.name = &ahcf->peer->name;
189     ctx->peer.get = ngx_event_get_peer;
190     ctx->peer.log = s->connection->log;
191     ctx->peer.log_error = NGX_ERROR_ERR;
192
193     rc = ngx_event_connect_peer(&ctx->peer);
194
195     if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {
196         if (ctx->peer.connection) {
197             ngx_close_connection(ctx->peer.connection);
198         }
199
200         ngx_destroy_pool(ctx->pool);
201         ngx_mail_session_internal_server_error(s);
202         return;
203     }
204
205     ctx->peer.connection->data = s;
206     ctx->peer.connection->pool = s->connection->pool;
207
208     s->connection->read->handler = ngx_mail_auth_http_block_read;
209     ctx->peer.connection->read->handler = ngx_mail_auth_http_read_handler;
210     ctx->peer.connection->write->handler = ngx_mail_auth_http_write_handler;
211
212     ctx->handler = ngx_mail_auth_http_ignore_status_line;
213
214     ngx_add_timer(ctx->peer.connection->read, ahcf->timeout);
215     ngx_add_timer(ctx->peer.connection->write, ahcf->timeout);
216
217     if (rc == NGX_OK) {
218         ngx_mail_auth_http_write_handler(ctx->peer.connection->write);
219         return;
220     }
221 }
222
223
224 static void
225 ngx_mail_auth_http_write_handler(ngx_event_t *wev)
226 {
227     ssize_t                     n, size;
228     ngx_connection_t           *c;
229     ngx_mail_session_t         *s;
230     ngx_mail_auth_http_ctx_t   *ctx;
231     ngx_mail_auth_http_conf_t  *ahcf;
232
233     c = wev->data;
234     s = c->data;
235
236     ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);
237
238     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0,
239                    "mail auth http write handler");
240
241     if (wev->timedout) {
242         ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT,
243                       "auth http server %V timed out", ctx->peer.name);
244         ngx_close_connection(c);
245         ngx_destroy_pool(ctx->pool);
246         ngx_mail_session_internal_server_error(s);
247         return;
248     }
249
250     size = ctx->request->last - ctx->request->pos;
251
252     n = ngx_send(c, ctx->request->pos, size);
253
254     if (n == NGX_ERROR) {
255         ngx_close_connection(c);
256         ngx_destroy_pool(ctx->pool);
257         ngx_mail_session_internal_server_error(s);
258         return;
259     }
260
261     if (n > 0) {
262         ctx->request->pos += n;
263
264         if (n == size) {
265             wev->handler = ngx_mail_auth_http_dummy_handler;
266
267             if (wev->timer_set) {
268                 ngx_del_timer(wev);
269             }
270
271             if (ngx_handle_write_event(wev, 0) != NGX_OK) {
272                 ngx_close_connection(c);
273                 ngx_destroy_pool(ctx->pool);
274                 ngx_mail_session_internal_server_error(s);
275             }
276
277             return;
278         }
279     }
280
281     if (!wev->timer_set) {
282         ahcf = ngx_mail_get_module_srv_conf(s, ngx_mail_auth_http_module);
283         ngx_add_timer(wev, ahcf->timeout);
284     }
285 }
286
287
288 static void
289 ngx_mail_auth_http_read_handler(ngx_event_t *rev)
290 {
291     ssize_t                     n, size;
292     ngx_connection_t          *c;
293     ngx_mail_session_t        *s;
294     ngx_mail_auth_http_ctx_t  *ctx;
295
296     c = rev->data;
297     s = c->data;
298
299     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
300                    "mail auth http read handler");
301
302     ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);
303
304     if (rev->timedout) {
305         ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,
306                       "auth http server %V timed out", ctx->peer.name);
307         ngx_close_connection(c);
308         ngx_destroy_pool(ctx->pool);
309         ngx_mail_session_internal_server_error(s);
310         return;
311     }
312
313     if (ctx->response == NULL) {
314         ctx->response = ngx_create_temp_buf(ctx->pool, 1024);
315         if (ctx->response == NULL) {
316             ngx_close_connection(c);
317             ngx_destroy_pool(ctx->pool);
318             ngx_mail_session_internal_server_error(s);
319             return;
320         }
321     }
322
323     size = ctx->response->end - ctx->response->last;
324
325     n = ngx_recv(c, ctx->response->pos, size);
326
327     if (n > 0) {
328         ctx->response->last += n;
329
330         ctx->handler(s, ctx);
331         return;
332     }
333
334     if (n == NGX_AGAIN) {
335         return;
336     }
337
338     ngx_close_connection(c);
339     ngx_destroy_pool(ctx->pool);
340     ngx_mail_session_internal_server_error(s);
341 }
342
343
344 static void
345 ngx_mail_auth_http_ignore_status_line(ngx_mail_session_t *s,
346     ngx_mail_auth_http_ctx_t *ctx)
347 {
348     u_char  *p, ch;
349     enum  {
350         sw_start = 0,
351         sw_H,
352         sw_HT,
353         sw_HTT,
354         sw_HTTP,
355         sw_skip,
356         sw_almost_done
357     } state;
358
359     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
360                    "mail auth http process status line");
361
362     state = ctx->state;
363
364     for (p = ctx->response->pos; p < ctx->response->last; p++) {
365         ch = *p;
366
367         switch (state) {
368
369         /* "HTTP/" */
370         case sw_start:
371             if (ch == 'H') {
372                 state = sw_H;
373                 break;
374             }
375             goto next;
376
377         case sw_H:
378             if (ch == 'T') {
379                 state = sw_HT;
380                 break;
381             }
382             goto next;
383
384         case sw_HT:
385             if (ch == 'T') {
386                 state = sw_HTT;
387                 break;
388             }
389             goto next;
390
391         case sw_HTT:
392             if (ch == 'P') {
393                 state = sw_HTTP;
394                 break;
395             }
396             goto next;
397
398         case sw_HTTP:
399             if (ch == '/') {
400                 state = sw_skip;
401                 break;
402             }
403             goto next;
404
405         /* any text until end of line */
406         case sw_skip:
407             switch (ch) {
408             case CR:
409                 state = sw_almost_done;
410
411                 break;
412             case LF:
413                 goto done;
414             }
415             break;
416
417         /* end of status line */
418         case sw_almost_done:
419             if (ch == LF) {
420                 goto done;
421             }
422
423             ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
424                           "auth http server &V sent invalid response",
425                           ctx->peer.name);
426             ngx_close_connection(ctx->peer.connection);
427             ngx_destroy_pool(ctx->pool);
428             ngx_mail_session_internal_server_error(s);
429             return;
430         }
431     }
432
433     ctx->response->pos = p;
434     ctx->state = state;
435
436     return;
437
438 next:
439
440     p = ctx->response->start - 1;
441
442 done:
443
444     ctx->response->pos = p + 1;
445     ctx->state = 0;
446     ctx->handler = ngx_mail_auth_http_process_headers;
447     ctx->handler(s, ctx);
448 }
449
450
451 static void
452 ngx_mail_auth_http_process_headers(ngx_mail_session_t *s,
453     ngx_mail_auth_http_ctx_t *ctx)
454 {
455     u_char              *p;
456     time_t               timer;
457     size_t               len, size;
458     ngx_int_t            rc, port, n;
459     ngx_peer_addr_t     *peer;
460     struct sockaddr_in  *sin;
461
462     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
463                    "mail auth http process headers");
464
465     for ( ;; ) {
466         rc = ngx_mail_auth_http_parse_header_line(s, ctx);
467
468         if (rc == NGX_OK) {
469
470 #if (NGX_DEBUG)
471             {
472             ngx_str_t  key, value;
473
474             key.len = ctx->header_name_end - ctx->header_name_start;
475             key.data = ctx->header_name_start;
476             value.len = ctx->header_end - ctx->header_start;
477             value.data = ctx->header_start;
478
479             ngx_log_debug2(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
480                            "mail auth http header: \"%V: %V\"",
481                            &key, &value);
482             }
483 #endif
484
485             len = ctx->header_name_end - ctx->header_name_start;
486
487             if (len == sizeof("Auth-Status") - 1
488                 && ngx_strncasecmp(ctx->header_name_start,
489                                    (u_char *) "Auth-Status",
490                                    sizeof("Auth-Status") - 1)
491                    == 0)
492             {
493                 len = ctx->header_end - ctx->header_start;
494
495                 if (len == 2
496                     && ctx->header_start[0] == 'O'
497                     && ctx->header_start[1] == 'K')
498                 {
499                     continue;
500                 }
501
502                 if (len == 4
503                     && ctx->header_start[0] == 'W'
504                     && ctx->header_start[1] == 'A'
505                     && ctx->header_start[2] == 'I'
506                     && ctx->header_start[3] == 'T')
507                 {
508                     s->auth_wait = 1;
509                     continue;
510                 }
511
512                 ctx->errmsg.len = len;
513                 ctx->errmsg.data = ctx->header_start;
514
515                 switch (s->protocol) {
516
517                 case NGX_MAIL_POP3_PROTOCOL:
518                     size = sizeof("-ERR ") - 1 + len + sizeof(CRLF) - 1;
519                     break;
520
521                 case NGX_MAIL_IMAP_PROTOCOL:
522                     size = s->tag.len + sizeof("NO ") - 1 + len
523                            + sizeof(CRLF) - 1;
524                     break;
525
526                 default: /* NGX_MAIL_SMTP_PROTOCOL */
527                     ctx->err = ctx->errmsg;
528                     continue;
529                 }
530
531                 p = ngx_pnalloc(s->connection->pool, size);
532                 if (p == NULL) {
533                     ngx_close_connection(ctx->peer.connection);
534                     ngx_destroy_pool(ctx->pool);
535                     ngx_mail_session_internal_server_error(s);
536                     return;
537                 }
538
539                 ctx->err.data = p;
540
541                 switch (s->protocol) {
542
543                 case NGX_MAIL_POP3_PROTOCOL:
544                     *p++ = '-'; *p++ = 'E'; *p++ = 'R'; *p++ = 'R'; *p++ = ' ';
545                     break;
546
547                 case NGX_MAIL_IMAP_PROTOCOL:
548                     p = ngx_cpymem(p, s->tag.data, s->tag.len);
549                     *p++ = 'N'; *p++ = 'O'; *p++ = ' ';
550                     break;
551
552                 default: /* NGX_MAIL_SMTP_PROTOCOL */
553                     break;
554                 }
555
556                 p = ngx_cpymem(p, ctx->header_start, len);
557                 *p++ = CR; *p++ = LF;
558
559                 ctx->err.len = p - ctx->err.data;
560
561                 continue;
562             }
563
564             if (len == sizeof("Auth-Server") - 1
565                 && ngx_strncasecmp(ctx->header_name_start,
566                                    (u_char *) "Auth-Server",
567                                    sizeof("Auth-Server") - 1)
568                     == 0)
569             {
570                 ctx->addr.len = ctx->header_end - ctx->header_start;
571                 ctx->addr.data = ctx->header_start;
572
573                 continue;
574             }
575
576             if (len == sizeof("Auth-Port") - 1
577                 && ngx_strncasecmp(ctx->header_name_start,
578                                    (u_char *) "Auth-Port",
579                                    sizeof("Auth-Port") - 1)
580                    == 0)
581             {
582                 ctx->port.len = ctx->header_end - ctx->header_start;
583                 ctx->port.data = ctx->header_start;
584
585                 continue;
586             }
587
588             if (len == sizeof("Auth-User") - 1
589                 && ngx_strncasecmp(ctx->header_name_start,
590                                    (u_char *) "Auth-User",
591                                    sizeof("Auth-User") - 1)
592                    == 0)
593             {
594                 s->login.len = ctx->header_end - ctx->header_start;
595
596                 s->login.data = ngx_pnalloc(s->connection->pool, s->login.len);
597                 if (s->login.data == NULL) {
598                     ngx_close_connection(ctx->peer.connection);
599                     ngx_destroy_pool(ctx->pool);
600                     ngx_mail_session_internal_server_error(s);
601                     return;
602                 }
603
604                 ngx_memcpy(s->login.data, ctx->header_start, s->login.len);
605
606                 continue;
607             }
608
609             if (len == sizeof("Auth-Pass") - 1
610                 && ngx_strncasecmp(ctx->header_name_start,
611                                    (u_char *) "Auth-Pass",
612                                    sizeof("Auth-Pass") - 1)
613                    == 0)
614             {
615                 s->passwd.len = ctx->header_end - ctx->header_start;
616
617                 s->passwd.data = ngx_pnalloc(s->connection->pool,
618                                              s->passwd.len);
619                 if (s->passwd.data == NULL) {
620                     ngx_close_connection(ctx->peer.connection);
621                     ngx_destroy_pool(ctx->pool);
622                     ngx_mail_session_internal_server_error(s);
623                     return;
624                 }
625
626                 ngx_memcpy(s->passwd.data, ctx->header_start, s->passwd.len);
627
628                 continue;
629             }
630
631             if (len == sizeof("Auth-Wait") - 1
632                 && ngx_strncasecmp(ctx->header_name_start,
633                                    (u_char *) "Auth-Wait",
634                                    sizeof("Auth-Wait") - 1)
635                    == 0)
636             {
637                 n = ngx_atoi(ctx->header_start,
638                              ctx->header_end - ctx->header_start);
639
640                 if (n != NGX_ERROR) {
641                     ctx->sleep = n;
642                 }
643
644                 continue;
645             }
646
647             if (len == sizeof("Auth-Error-Code") - 1
648                 && ngx_strncasecmp(ctx->header_name_start,
649                                    (u_char *) "Auth-Error-Code",
650                                    sizeof("Auth-Error-Code") - 1)
651                    == 0)
652             {
653                 ctx->errcode.len = ctx->header_end - ctx->header_start;
654
655                 ctx->errcode.data = ngx_pnalloc(s->connection->pool,
656                                                 ctx->errcode.len);
657                 if (ctx->errcode.data == NULL) {
658                     ngx_close_connection(ctx->peer.connection);
659                     ngx_destroy_pool(ctx->pool);
660                     ngx_mail_session_internal_server_error(s);
661                     return;
662                 }
663
664                 ngx_memcpy(ctx->errcode.data, ctx->header_start,
665                            ctx->errcode.len);
666
667                 continue;
668             }
669
670             /* ignore other headers */
671
672             continue;
673         }
674
675         if (rc == NGX_DONE) {
676             ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
677                            "mail auth http header done");
678
679             ngx_close_connection(ctx->peer.connection);
680
681             if (ctx->err.len) {
682
683                 ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
684                               "client login failed: \"%V\"", &ctx->errmsg);
685
686                 if (s->protocol == NGX_MAIL_SMTP_PROTOCOL) {
687
688                     if (ctx->errcode.len == 0) {
689                         ctx->errcode = ngx_mail_smtp_errcode;
690                     }
691
692                     ctx->err.len = ctx->errcode.len + ctx->errmsg.len
693                                    + sizeof(" " CRLF) - 1;
694
695                     p = ngx_pnalloc(s->connection->pool, ctx->err.len);
696                     if (p == NULL) {
697                         ngx_close_connection(ctx->peer.connection);
698                         ngx_destroy_pool(ctx->pool);
699                         ngx_mail_session_internal_server_error(s);
700                         return;
701                     }
702
703                     ctx->err.data = p;
704
705                     p = ngx_cpymem(p, ctx->errcode.data, ctx->errcode.len);
706                     *p++ = ' ';
707                     p = ngx_cpymem(p, ctx->errmsg.data, ctx->errmsg.len);
708                     *p++ = CR; *p = LF;
709                 }
710
711                 s->out = ctx->err;
712                 timer = ctx->sleep;
713
714                 ngx_destroy_pool(ctx->pool);
715
716                 if (timer == 0) {
717                     s->quit = 1;
718                     ngx_mail_send(s->connection->write);
719                     return;
720                 }
721
722                 ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000));
723
724                 s->connection->read->handler = ngx_mail_auth_sleep_handler;
725
726                 return;
727             }
728
729             if (s->auth_wait) {
730                 timer = ctx->sleep;
731
732                 ngx_destroy_pool(ctx->pool);
733
734                 if (timer == 0) {
735                     ngx_mail_auth_http_init(s);
736                     return;
737                 }
738
739                 ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000));
740
741                 s->connection->read->handler = ngx_mail_auth_sleep_handler;
742
743                 return;
744             }
745
746             if (ctx->addr.len == 0 || ctx->port.len == 0) {
747                 ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
748                               "auth http server %V did not send server or port",
749                               ctx->peer.name);
750                 ngx_destroy_pool(ctx->pool);
751                 ngx_mail_session_internal_server_error(s);
752                 return;
753             }
754
755             if (s->passwd.data == NULL
756                 && s->protocol != NGX_MAIL_SMTP_PROTOCOL)
757             {
758                 ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
759                               "auth http server %V did not send password",
760                               ctx->peer.name);
761                 ngx_destroy_pool(ctx->pool);
762                 ngx_mail_session_internal_server_error(s);
763                 return;
764             }
765
766             peer = ngx_pcalloc(s->connection->pool, sizeof(ngx_peer_addr_t));
767             if (peer == NULL) {
768                 ngx_destroy_pool(ctx->pool);
769                 ngx_mail_session_internal_server_error(s);
770                 return;
771             }
772
773             sin = ngx_pcalloc(s->connection->pool, sizeof(struct sockaddr_in));
774             if (sin == NULL) {
775                 ngx_destroy_pool(ctx->pool);
776                 ngx_mail_session_internal_server_error(s);
777                 return;
778             }
779
780             sin->sin_family = AF_INET;
781
782             port = ngx_atoi(ctx->port.data, ctx->port.len);
783             if (port == NGX_ERROR || port < 1 || port > 65536) {
784                 ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
785                               "auth http server %V sent invalid server "
786                               "port:\"%V\"",
787                               ctx->peer.name, &ctx->port);
788                 ngx_destroy_pool(ctx->pool);
789                 ngx_mail_session_internal_server_error(s);
790                 return;
791             }
792
793             sin->sin_port = htons((in_port_t) port);
794
795             ctx->addr.data[ctx->addr.len] = '\0';
796             sin->sin_addr.s_addr = inet_addr((char *) ctx->addr.data);
797             if (sin->sin_addr.s_addr == INADDR_NONE) {
798                 ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
799                               "auth http server %V sent invalid server "
800                               "address:\"%V\"",
801                               ctx->peer.name, &ctx->addr);
802                 ngx_destroy_pool(ctx->pool);
803                 ngx_mail_session_internal_server_error(s);
804                 return;
805             }
806
807             peer->sockaddr = (struct sockaddr *) sin;
808             peer->socklen = sizeof(struct sockaddr_in);
809
810             len = ctx->addr.len + 1 + ctx->port.len;
811
812             peer->name.len = len;
813
814             peer->name.data = ngx_pnalloc(s->connection->pool, len);
815             if (peer->name.data == NULL) {
816                 ngx_destroy_pool(ctx->pool);
817                 ngx_mail_session_internal_server_error(s);
818                 return;
819             }
820
821             len = ctx->addr.len;
822
823             ngx_memcpy(peer->name.data, ctx->addr.data, len);
824
825             peer->name.data[len++] = ':';
826
827             ngx_memcpy(peer->name.data + len, ctx->port.data, ctx->port.len);
828
829             ngx_destroy_pool(ctx->pool);
830             ngx_mail_proxy_init(s, peer);
831
832             return;
833         }
834
835         if (rc == NGX_AGAIN ) {
836             return;
837         }
838
839         /* rc == NGX_ERROR */
840
841         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
842                       "auth http server %V sent invalid header in response",
843                       ctx->peer.name);
844         ngx_close_connection(ctx->peer.connection);
845         ngx_destroy_pool(ctx->pool);
846         ngx_mail_session_internal_server_error(s);
847
848         return;
849     }
850 }
851
852
853 static void
854 ngx_mail_auth_sleep_handler(ngx_event_t *rev)
855 {
856     ngx_connection_t          *c;
857     ngx_mail_session_t        *s;
858     ngx_mail_core_srv_conf_t  *cscf;
859
860     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail auth sleep handler");
861
862     c = rev->data;
863     s = c->data;
864
865     if (rev->timedout) {
866
867         rev->timedout = 0;
868
869         if (s->auth_wait) {
870             s->auth_wait = 0;
871             ngx_mail_auth_http_init(s);
872             return;
873         }
874
875         cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
876
877         rev->handler = cscf->protocol->auth_state;
878
879         s->mail_state = 0;
880         s->auth_method = NGX_MAIL_AUTH_PLAIN;
881
882         c->log->action = "in auth state";
883
884         ngx_mail_send(c->write);
885
886         if (c->destroyed) {
887             return;
888         }
889
890         ngx_add_timer(rev, cscf->timeout);
891
892         if (rev->ready) {
893             rev->handler(rev);
894             return;
895         }
896
897         if (ngx_handle_read_event(rev, 0) != NGX_OK) {
898             ngx_mail_close_connection(c);
899         }
900
901         return;
902     }
903
904     if (rev->active) {
905         if (ngx_handle_read_event(rev, 0) != NGX_OK) {
906             ngx_mail_close_connection(c);
907         }
908     }
909 }
910
911
912 static ngx_int_t
913 ngx_mail_auth_http_parse_header_line(ngx_mail_session_t *s,
914     ngx_mail_auth_http_ctx_t *ctx)
915 {
916     u_char      c, ch, *p;
917     enum {
918         sw_start = 0,
919         sw_name,
920         sw_space_before_value,
921         sw_value,
922         sw_space_after_value,
923         sw_almost_done,
924         sw_header_almost_done
925     } state;
926
927     state = ctx->state;
928
929     for (p = ctx->response->pos; p < ctx->response->last; p++) {
930         ch = *p;
931
932         switch (state) {
933
934         /* first char */
935         case sw_start:
936
937             switch (ch) {
938             case CR:
939                 ctx->header_end = p;
940                 state = sw_header_almost_done;
941                 break;
942             case LF:
943                 ctx->header_end = p;
944                 goto header_done;
945             default:
946                 state = sw_name;
947                 ctx->header_name_start = p;
948
949                 c = (u_char) (ch | 0x20);
950                 if (c >= 'a' && c <= 'z') {
951                     break;
952                 }
953
954                 if (ch >= '0' && ch <= '9') {
955                     break;
956                 }
957
958                 return NGX_ERROR;
959             }
960             break;
961
962         /* header name */
963         case sw_name:
964             c = (u_char) (ch | 0x20);
965             if (c >= 'a' && c <= 'z') {
966                 break;
967             }
968
969             if (ch == ':') {
970                 ctx->header_name_end = p;
971                 state = sw_space_before_value;
972                 break;
973             }
974
975             if (ch == '-') {
976                 break;
977             }
978
979             if (ch >= '0' && ch <= '9') {
980                 break;
981             }
982
983             if (ch == CR) {
984                 ctx->header_name_end = p;
985                 ctx->header_start = p;
986                 ctx->header_end = p;
987                 state = sw_almost_done;
988                 break;
989             }
990
991             if (ch == LF) {
992                 ctx->header_name_end = p;
993                 ctx->header_start = p;
994                 ctx->header_end = p;
995                 goto done;
996             }
997
998             return NGX_ERROR;
999
1000         /* space* before header value */
1001         case sw_space_before_value:
1002             switch (ch) {
1003             case ' ':
1004                 break;
1005             case CR:
1006                 ctx->header_start = p;
1007                 ctx->header_end = p;
1008                 state = sw_almost_done;
1009                 break;
1010             case LF:
1011                 ctx->header_start = p;
1012                 ctx->header_end = p;
1013                 goto done;
1014             default:
1015                 ctx->header_start = p;
1016                 state = sw_value;
1017                 break;
1018             }
1019             break;
1020
1021         /* header value */
1022         case sw_value:
1023             switch (ch) {
1024             case ' ':
1025                 ctx->header_end = p;
1026                 state = sw_space_after_value;
1027                 break;
1028             case CR:
1029                 ctx->header_end = p;
1030                 state = sw_almost_done;
1031                 break;
1032             case LF:
1033                 ctx->header_end = p;
1034                 goto done;
1035             }
1036             break;
1037
1038         /* space* before end of header line */
1039         case sw_space_after_value:
1040             switch (ch) {
1041             case ' ':
1042                 break;
1043             case CR:
1044                 state = sw_almost_done;
1045                 break;
1046             case LF:
1047                 goto done;
1048             default:
1049                 state = sw_value;
1050                 break;
1051             }
1052             break;
1053
1054         /* end of header line */
1055         case sw_almost_done:
1056             switch (ch) {
1057             case LF:
1058                 goto done;
1059             default:
1060                 return NGX_ERROR;
1061             }
1062
1063         /* end of header */
1064         case sw_header_almost_done:
1065             switch (ch) {
1066             case LF:
1067                 goto header_done;
1068             default:
1069                 return NGX_ERROR;
1070             }
1071         }
1072     }
1073
1074     ctx->response->pos = p;
1075     ctx->state = state;
1076
1077     return NGX_AGAIN;
1078
1079 done:
1080
1081     ctx->response->pos = p + 1;
1082     ctx->state = sw_start;
1083
1084     return NGX_OK;
1085
1086 header_done:
1087
1088     ctx->response->pos = p + 1;
1089     ctx->state = sw_start;
1090
1091     return NGX_DONE;
1092 }
1093
1094
1095 static void
1096 ngx_mail_auth_http_block_read(ngx_event_t *rev)
1097 {
1098     ngx_connection_t          *c;
1099     ngx_mail_session_t        *s;
1100     ngx_mail_auth_http_ctx_t  *ctx;
1101
1102     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
1103                    "mail auth http block read");
1104
1105     if (ngx_handle_read_event(rev, 0) != NGX_OK) {
1106         c = rev->data;
1107         s = c->data;
1108
1109         ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);
1110
1111         ngx_close_connection(ctx->peer.connection);
1112         ngx_destroy_pool(ctx->pool);
1113         ngx_mail_session_internal_server_error(s);
1114     }
1115 }
1116
1117
1118 static void
1119 ngx_mail_auth_http_dummy_handler(ngx_event_t *ev)
1120 {
1121     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, ev->log, 0,
1122                    "mail auth http dummy handler");
1123 }
1124
1125
1126 static ngx_buf_t *
1127 ngx_mail_auth_http_create_request(ngx_mail_session_t *s, ngx_pool_t *pool,
1128     ngx_mail_auth_http_conf_t *ahcf)
1129 {
1130     size_t                     len;
1131     ngx_buf_t                 *b;
1132     ngx_str_t                  login, passwd;
1133     ngx_mail_core_srv_conf_t  *cscf;
1134
1135     if (ngx_mail_auth_http_escape(pool, &s->login, &login) != NGX_OK) {
1136         return NULL;
1137     }
1138
1139     if (ngx_mail_auth_http_escape(pool, &s->passwd, &passwd) != NGX_OK) {
1140         return NULL;
1141     }
1142
1143     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
1144
1145     len = sizeof("GET ") - 1 + ahcf->uri.len + sizeof(" HTTP/1.0" CRLF) - 1
1146           + sizeof("Host: ") - 1 + ahcf->host_header.len + sizeof(CRLF) - 1
1147           + sizeof("Auth-Method: ") - 1
1148                 + ngx_mail_auth_http_method[s->auth_method].len
1149                 + sizeof(CRLF) - 1
1150           + sizeof("Auth-User: ") - 1 + login.len + sizeof(CRLF) - 1
1151           + sizeof("Auth-Pass: ") - 1 + passwd.len + sizeof(CRLF) - 1
1152           + sizeof("Auth-Salt: ") - 1 + s->salt.len
1153           + sizeof("Auth-Protocol: ") - 1 + cscf->protocol->name.len
1154                 + sizeof(CRLF) - 1
1155           + sizeof("Auth-Login-Attempt: ") - 1 + NGX_INT_T_LEN
1156                 + sizeof(CRLF) - 1
1157           + sizeof("Client-IP: ") - 1 + s->connection->addr_text.len
1158                 + sizeof(CRLF) - 1
1159           + sizeof("Client-Host: ") - 1 + s->host.len + sizeof(CRLF) - 1
1160           + sizeof("Auth-SMTP-Helo: ") - 1 + s->smtp_helo.len
1161           + sizeof("Auth-SMTP-From: ") - 1 + s->smtp_from.len
1162           + sizeof("Auth-SMTP-To: ") - 1 + s->smtp_to.len
1163           + ahcf->header.len
1164           + sizeof(CRLF) - 1;
1165
1166     b = ngx_create_temp_buf(pool, len);
1167     if (b == NULL) {
1168         return NULL;
1169     }
1170
1171     b->last = ngx_cpymem(b->last, "GET ", sizeof("GET ") - 1);
1172     b->last = ngx_copy(b->last, ahcf->uri.data, ahcf->uri.len);
1173     b->last = ngx_cpymem(b->last, " HTTP/1.0" CRLF,
1174                          sizeof(" HTTP/1.0" CRLF) - 1);
1175
1176     b->last = ngx_cpymem(b->last, "Host: ", sizeof("Host: ") - 1);
1177     b->last = ngx_copy(b->last, ahcf->host_header.data,
1178                          ahcf->host_header.len);
1179     *b->last++ = CR; *b->last++ = LF;
1180
1181     b->last = ngx_cpymem(b->last, "Auth-Method: ",
1182                          sizeof("Auth-Method: ") - 1);
1183     b->last = ngx_cpymem(b->last,
1184                          ngx_mail_auth_http_method[s->auth_method].data,
1185                          ngx_mail_auth_http_method[s->auth_method].len);
1186     *b->last++ = CR; *b->last++ = LF;
1187
1188     b->last = ngx_cpymem(b->last, "Auth-User: ", sizeof("Auth-User: ") - 1);
1189     b->last = ngx_copy(b->last, login.data, login.len);
1190     *b->last++ = CR; *b->last++ = LF;
1191
1192     b->last = ngx_cpymem(b->last, "Auth-Pass: ", sizeof("Auth-Pass: ") - 1);
1193     b->last = ngx_copy(b->last, passwd.data, passwd.len);
1194     *b->last++ = CR; *b->last++ = LF;
1195
1196     if (s->auth_method != NGX_MAIL_AUTH_PLAIN && s->salt.len) {
1197         b->last = ngx_cpymem(b->last, "Auth-Salt: ", sizeof("Auth-Salt: ") - 1);
1198         b->last = ngx_copy(b->last, s->salt.data, s->salt.len);
1199
1200         s->passwd.data = NULL;
1201     }
1202
1203     b->last = ngx_cpymem(b->last, "Auth-Protocol: ",
1204                          sizeof("Auth-Protocol: ") - 1);
1205     b->last = ngx_cpymem(b->last, cscf->protocol->name.data,
1206                          cscf->protocol->name.len);
1207     *b->last++ = CR; *b->last++ = LF;
1208
1209     b->last = ngx_sprintf(b->last, "Auth-Login-Attempt: %ui" CRLF,
1210                           s->login_attempt);
1211
1212     b->last = ngx_cpymem(b->last, "Client-IP: ", sizeof("Client-IP: ") - 1);
1213     b->last = ngx_copy(b->last, s->connection->addr_text.data,
1214                        s->connection->addr_text.len);
1215     *b->last++ = CR; *b->last++ = LF;
1216
1217     if (s->host.len) {
1218         b->last = ngx_cpymem(b->last, "Client-Host: ",
1219                              sizeof("Client-Host: ") - 1);
1220         b->last = ngx_copy(b->last, s->host.data, s->host.len);
1221         *b->last++ = CR; *b->last++ = LF;
1222     }
1223
1224     if (s->auth_method == NGX_MAIL_AUTH_NONE) {
1225
1226         /* HELO, MAIL FROM, and RCPT TO can't contain CRLF, no need to escape */
1227
1228         b->last = ngx_cpymem(b->last, "Auth-SMTP-Helo: ",
1229                              sizeof("Auth-SMTP-Helo: ") - 1);
1230         b->last = ngx_copy(b->last, s->smtp_helo.data, s->smtp_helo.len);
1231         *b->last++ = CR; *b->last++ = LF;
1232
1233         b->last = ngx_cpymem(b->last, "Auth-SMTP-From: ",
1234                              sizeof("Auth-SMTP-From: ") - 1);
1235         b->last = ngx_copy(b->last, s->smtp_from.data, s->smtp_from.len);
1236         *b->last++ = CR; *b->last++ = LF;
1237
1238         b->last = ngx_cpymem(b->last, "Auth-SMTP-To: ",
1239                              sizeof("Auth-SMTP-To: ") - 1);
1240         b->last = ngx_copy(b->last, s->smtp_to.data, s->smtp_to.len);
1241         *b->last++ = CR; *b->last++ = LF;
1242
1243     }
1244
1245     if (ahcf->header.len) {
1246         b->last = ngx_copy(b->last, ahcf->header.data, ahcf->header.len);
1247     }
1248
1249     /* add "\r\n" at the header end */
1250     *b->last++ = CR; *b->last++ = LF;
1251
1252 #if (NGX_DEBUG_MAIL_PASSWD)
1253     {
1254     ngx_str_t  l;
1255
1256     l.len = b->last - b->pos;
1257     l.data = b->pos;
1258     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
1259                    "mail auth http header:\n\"%V\"", &l);
1260     }
1261 #endif
1262
1263     return b;
1264 }
1265
1266
1267 static ngx_int_t
1268 ngx_mail_auth_http_escape(ngx_pool_t *pool, ngx_str_t *text, ngx_str_t *escaped)
1269 {
1270     u_char     *p;
1271     uintptr_t   n;
1272
1273     n = ngx_escape_uri(NULL, text->data, text->len, NGX_ESCAPE_MAIL_AUTH);
1274
1275     if (n == 0) {
1276         *escaped = *text;
1277         return NGX_OK;
1278     }
1279
1280     escaped->len = text->len + n * 2;
1281
1282     p = ngx_pnalloc(pool, escaped->len);
1283     if (p == NULL) {
1284         return NGX_ERROR;
1285     }
1286
1287     (void) ngx_escape_uri(p, text->data, text->len, NGX_ESCAPE_MAIL_AUTH);
1288
1289     escaped->data = p;
1290
1291     return NGX_OK;
1292 }
1293
1294
1295 static void *
1296 ngx_mail_auth_http_create_conf(ngx_conf_t *cf)
1297 {
1298     ngx_mail_auth_http_conf_t  *ahcf;
1299
1300     ahcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_auth_http_conf_t));
1301     if (ahcf == NULL) {
1302         return NGX_CONF_ERROR;
1303     }
1304
1305     ahcf->timeout = NGX_CONF_UNSET_MSEC;
1306
1307     ahcf->file = cf->conf_file->file.name.data;
1308     ahcf->line = cf->conf_file->line;
1309
1310     return ahcf;
1311 }
1312
1313
1314 static char *
1315 ngx_mail_auth_http_merge_conf(ngx_conf_t *cf, void *parent, void *child)
1316 {
1317     ngx_mail_auth_http_conf_t *prev = parent;
1318     ngx_mail_auth_http_conf_t *conf = child;
1319
1320     u_char           *p;
1321     size_t            len;
1322     ngx_uint_t        i;
1323     ngx_table_elt_t  *header;
1324
1325     if (conf->peer == NULL) {
1326         conf->peer = prev->peer;
1327         conf->host_header = prev->host_header;
1328         conf->uri = prev->uri;
1329
1330         if (conf->peer == NULL) {
1331             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
1332                           "no \"http_auth\" is defined for server in %s:%ui",
1333                           conf->file, conf->line);
1334
1335             return NGX_CONF_ERROR;
1336         }
1337     }
1338
1339     ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);
1340
1341     if (conf->headers == NULL) {
1342         conf->headers = prev->headers;
1343         conf->header = prev->header;
1344     }
1345
1346     if (conf->headers && conf->header.len == 0) {
1347         len = 0;
1348         header = conf->headers->elts;
1349         for (i = 0; i < conf->headers->nelts; i++) {
1350             len += header[i].key.len + 2 + header[i].value.len + 2;
1351         }
1352
1353         p = ngx_pnalloc(cf->pool, len);
1354         if (p == NULL) {
1355             return NGX_CONF_ERROR;
1356         }
1357
1358         conf->header.len = len;
1359         conf->header.data = p;
1360
1361         for (i = 0; i < conf->headers->nelts; i++) {
1362             p = ngx_cpymem(p, header[i].key.data, header[i].key.len);
1363             *p++ = ':'; *p++ = ' ';
1364             p = ngx_cpymem(p, header[i].value.data, header[i].value.len);
1365             *p++ = CR; *p++ = LF;
1366         }
1367     }
1368
1369     return NGX_CONF_OK;
1370 }
1371
1372
1373 static char *
1374 ngx_mail_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1375 {
1376     ngx_mail_auth_http_conf_t *ahcf = conf;
1377
1378     ngx_str_t  *value;
1379     ngx_url_t   u;
1380
1381     value = cf->args->elts;
1382
1383     ngx_memzero(&u, sizeof(ngx_url_t));
1384
1385     u.url = value[1];
1386     u.default_port = 80;
1387     u.uri_part = 1;
1388     u.one_addr = 1;
1389
1390     if (ngx_strncmp(u.url.data, "http://", 7) == 0) {
1391         u.url.len -= 7;
1392         u.url.data += 7;
1393     }
1394
1395     if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
1396         if (u.err) {
1397             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1398                                "%s in auth_http \"%V\"", u.err, &u.url);
1399         }
1400
1401         return NGX_CONF_ERROR;
1402     }
1403
1404     ahcf->peer = u.addrs;
1405
1406     ahcf->host_header = u.host;
1407     ahcf->uri = u.uri;
1408
1409     if (ahcf->uri.len == 0) {
1410         ahcf->uri.len = sizeof("/") - 1;
1411         ahcf->uri.data = (u_char *) "/";
1412     }
1413
1414     return NGX_CONF_OK;
1415 }
1416
1417
1418 static char *
1419 ngx_mail_auth_http_header(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1420 {
1421     ngx_mail_auth_http_conf_t *ahcf = conf;
1422
1423     ngx_str_t        *value;
1424     ngx_table_elt_t  *header;
1425
1426     if (ahcf->headers == NULL) {
1427         ahcf->headers = ngx_array_create(cf->pool, 1, sizeof(ngx_table_elt_t));
1428         if (ahcf->headers == NULL) {
1429             return NGX_CONF_ERROR;
1430         }
1431     }
1432
1433     header = ngx_array_push(ahcf->headers);
1434     if (header == NULL) {
1435         return NGX_CONF_ERROR;
1436     }
1437
1438     value = cf->args->elts;
1439
1440     header->key = value[1];
1441     header->value = value[2];
1442
1443     return NGX_CONF_OK;
1444 }