src/mail/ngx_mail_auth_http_module.c - nginx source code

Global variables defined

Data types defined

Functions defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_event.h>
  8. #include <ngx_event_connect.h>
  9. #include <ngx_mail.h>


  10. typedef struct {
  11.     ngx_addr_t                     *peer;

  12.     ngx_msec_t                      timeout;
  13.     ngx_flag_t                      pass_client_cert;

  14.     ngx_str_t                       host_header;
  15.     ngx_str_t                       uri;
  16.     ngx_str_t                       header;

  17.     ngx_array_t                    *headers;

  18.     u_char                         *file;
  19.     ngx_uint_t                      line;
  20. } ngx_mail_auth_http_conf_t;


  21. typedef struct ngx_mail_auth_http_ctx_s  ngx_mail_auth_http_ctx_t;

  22. typedef void (*ngx_mail_auth_http_handler_pt)(ngx_mail_session_t *s,
  23.     ngx_mail_auth_http_ctx_t *ctx);

  24. struct ngx_mail_auth_http_ctx_s {
  25.     ngx_buf_t                      *request;
  26.     ngx_buf_t                      *response;
  27.     ngx_peer_connection_t           peer;

  28.     ngx_mail_auth_http_handler_pt   handler;

  29.     ngx_uint_t                      state;

  30.     u_char                         *header_name_start;
  31.     u_char                         *header_name_end;
  32.     u_char                         *header_start;
  33.     u_char                         *header_end;

  34.     ngx_str_t                       addr;
  35.     ngx_str_t                       port;
  36.     ngx_str_t                       err;
  37.     ngx_str_t                       errmsg;
  38.     ngx_str_t                       errcode;

  39.     time_t                          sleep;

  40.     ngx_pool_t                     *pool;
  41. };


  42. static void ngx_mail_auth_http_write_handler(ngx_event_t *wev);
  43. static void ngx_mail_auth_http_read_handler(ngx_event_t *rev);
  44. static void ngx_mail_auth_http_ignore_status_line(ngx_mail_session_t *s,
  45.     ngx_mail_auth_http_ctx_t *ctx);
  46. static void ngx_mail_auth_http_process_headers(ngx_mail_session_t *s,
  47.     ngx_mail_auth_http_ctx_t *ctx);
  48. static void ngx_mail_auth_sleep_handler(ngx_event_t *rev);
  49. static ngx_int_t ngx_mail_auth_http_parse_header_line(ngx_mail_session_t *s,
  50.     ngx_mail_auth_http_ctx_t *ctx);
  51. static void ngx_mail_auth_http_block_read(ngx_event_t *rev);
  52. static void ngx_mail_auth_http_dummy_handler(ngx_event_t *ev);
  53. static ngx_buf_t *ngx_mail_auth_http_create_request(ngx_mail_session_t *s,
  54.     ngx_pool_t *pool, ngx_mail_auth_http_conf_t *ahcf);
  55. static ngx_int_t ngx_mail_auth_http_escape(ngx_pool_t *pool, ngx_str_t *text,
  56.     ngx_str_t *escaped);

  57. static void *ngx_mail_auth_http_create_conf(ngx_conf_t *cf);
  58. static char *ngx_mail_auth_http_merge_conf(ngx_conf_t *cf, void *parent,
  59.     void *child);
  60. static char *ngx_mail_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
  61. static char *ngx_mail_auth_http_header(ngx_conf_t *cf, ngx_command_t *cmd,
  62.     void *conf);


  63. static ngx_command_t  ngx_mail_auth_http_commands[] = {

  64.     { ngx_string("auth_http"),
  65.       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
  66.       ngx_mail_auth_http,
  67.       NGX_MAIL_SRV_CONF_OFFSET,
  68.       0,
  69.       NULL },

  70.     { ngx_string("auth_http_timeout"),
  71.       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
  72.       ngx_conf_set_msec_slot,
  73.       NGX_MAIL_SRV_CONF_OFFSET,
  74.       offsetof(ngx_mail_auth_http_conf_t, timeout),
  75.       NULL },

  76.     { ngx_string("auth_http_header"),
  77.       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE2,
  78.       ngx_mail_auth_http_header,
  79.       NGX_MAIL_SRV_CONF_OFFSET,
  80.       0,
  81.       NULL },

  82.     { ngx_string("auth_http_pass_client_cert"),
  83.       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
  84.       ngx_conf_set_flag_slot,
  85.       NGX_MAIL_SRV_CONF_OFFSET,
  86.       offsetof(ngx_mail_auth_http_conf_t, pass_client_cert),
  87.       NULL },

  88.       ngx_null_command
  89. };


  90. static ngx_mail_module_t  ngx_mail_auth_http_module_ctx = {
  91.     NULL,                                  /* protocol */

  92.     NULL,                                  /* create main configuration */
  93.     NULL,                                  /* init main configuration */

  94.     ngx_mail_auth_http_create_conf,        /* create server configuration */
  95.     ngx_mail_auth_http_merge_conf          /* merge server configuration */
  96. };


  97. ngx_module_t  ngx_mail_auth_http_module = {
  98.     NGX_MODULE_V1,
  99.     &ngx_mail_auth_http_module_ctx,        /* module context */
  100.     ngx_mail_auth_http_commands,           /* module directives */
  101.     NGX_MAIL_MODULE,                       /* module type */
  102.     NULL,                                  /* init master */
  103.     NULL,                                  /* init module */
  104.     NULL,                                  /* init process */
  105.     NULL,                                  /* init thread */
  106.     NULL,                                  /* exit thread */
  107.     NULL,                                  /* exit process */
  108.     NULL,                                  /* exit master */
  109.     NGX_MODULE_V1_PADDING
  110. };


  111. static ngx_str_t   ngx_mail_auth_http_method[] = {
  112.     ngx_string("plain"),
  113.     ngx_string("plain"),
  114.     ngx_string("plain"),
  115.     ngx_string("apop"),
  116.     ngx_string("cram-md5"),
  117.     ngx_string("external"),
  118.     ngx_string("none")
  119. };

  120. static ngx_str_t   ngx_mail_smtp_errcode = ngx_string("535 5.7.0");


  121. void
  122. ngx_mail_auth_http_init(ngx_mail_session_t *s)
  123. {
  124.     ngx_int_t                   rc;
  125.     ngx_pool_t                 *pool;
  126.     ngx_mail_auth_http_ctx_t   *ctx;
  127.     ngx_mail_auth_http_conf_t  *ahcf;

  128.     s->connection->log->action = "in http auth state";

  129.     pool = ngx_create_pool(2048, s->connection->log);
  130.     if (pool == NULL) {
  131.         ngx_mail_session_internal_server_error(s);
  132.         return;
  133.     }

  134.     ctx = ngx_pcalloc(pool, sizeof(ngx_mail_auth_http_ctx_t));
  135.     if (ctx == NULL) {
  136.         ngx_destroy_pool(pool);
  137.         ngx_mail_session_internal_server_error(s);
  138.         return;
  139.     }

  140.     ctx->pool = pool;

  141.     ahcf = ngx_mail_get_module_srv_conf(s, ngx_mail_auth_http_module);

  142.     ctx->request = ngx_mail_auth_http_create_request(s, pool, ahcf);
  143.     if (ctx->request == NULL) {
  144.         ngx_destroy_pool(ctx->pool);
  145.         ngx_mail_session_internal_server_error(s);
  146.         return;
  147.     }

  148.     ngx_mail_set_ctx(s, ctx, ngx_mail_auth_http_module);

  149.     ctx->peer.sockaddr = ahcf->peer->sockaddr;
  150.     ctx->peer.socklen = ahcf->peer->socklen;
  151.     ctx->peer.name = &ahcf->peer->name;
  152.     ctx->peer.get = ngx_event_get_peer;
  153.     ctx->peer.log = s->connection->log;
  154.     ctx->peer.log_error = NGX_ERROR_ERR;

  155.     rc = ngx_event_connect_peer(&ctx->peer);

  156.     if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {
  157.         if (ctx->peer.connection) {
  158.             ngx_close_connection(ctx->peer.connection);
  159.         }

  160.         ngx_destroy_pool(ctx->pool);
  161.         ngx_mail_session_internal_server_error(s);
  162.         return;
  163.     }

  164.     ctx->peer.connection->data = s;
  165.     ctx->peer.connection->pool = s->connection->pool;

  166.     s->connection->read->handler = ngx_mail_auth_http_block_read;
  167.     ctx->peer.connection->read->handler = ngx_mail_auth_http_read_handler;
  168.     ctx->peer.connection->write->handler = ngx_mail_auth_http_write_handler;

  169.     ctx->handler = ngx_mail_auth_http_ignore_status_line;

  170.     ngx_add_timer(ctx->peer.connection->read, ahcf->timeout);
  171.     ngx_add_timer(ctx->peer.connection->write, ahcf->timeout);

  172.     if (rc == NGX_OK) {
  173.         ngx_mail_auth_http_write_handler(ctx->peer.connection->write);
  174.         return;
  175.     }
  176. }


  177. static void
  178. ngx_mail_auth_http_write_handler(ngx_event_t *wev)
  179. {
  180.     ssize_t                     n, size;
  181.     ngx_connection_t           *c;
  182.     ngx_mail_session_t         *s;
  183.     ngx_mail_auth_http_ctx_t   *ctx;
  184.     ngx_mail_auth_http_conf_t  *ahcf;

  185.     c = wev->data;
  186.     s = c->data;

  187.     ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);

  188.     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0,
  189.                    "mail auth http write handler");

  190.     if (wev->timedout) {
  191.         ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT,
  192.                       "auth http server %V timed out", ctx->peer.name);
  193.         ngx_close_connection(c);
  194.         ngx_destroy_pool(ctx->pool);
  195.         ngx_mail_session_internal_server_error(s);
  196.         return;
  197.     }

  198.     size = ctx->request->last - ctx->request->pos;

  199.     n = ngx_send(c, ctx->request->pos, size);

  200.     if (n == NGX_ERROR) {
  201.         ngx_close_connection(c);
  202.         ngx_destroy_pool(ctx->pool);
  203.         ngx_mail_session_internal_server_error(s);
  204.         return;
  205.     }

  206.     if (n > 0) {
  207.         ctx->request->pos += n;

  208.         if (n == size) {
  209.             wev->handler = ngx_mail_auth_http_dummy_handler;

  210.             if (wev->timer_set) {
  211.                 ngx_del_timer(wev);
  212.             }

  213.             if (ngx_handle_write_event(wev, 0) != NGX_OK) {
  214.                 ngx_close_connection(c);
  215.                 ngx_destroy_pool(ctx->pool);
  216.                 ngx_mail_session_internal_server_error(s);
  217.             }

  218.             return;
  219.         }
  220.     }

  221.     if (!wev->timer_set) {
  222.         ahcf = ngx_mail_get_module_srv_conf(s, ngx_mail_auth_http_module);
  223.         ngx_add_timer(wev, ahcf->timeout);
  224.     }
  225. }


  226. static void
  227. ngx_mail_auth_http_read_handler(ngx_event_t *rev)
  228. {
  229.     ssize_t                     n, size;
  230.     ngx_connection_t          *c;
  231.     ngx_mail_session_t        *s;
  232.     ngx_mail_auth_http_ctx_t  *ctx;

  233.     c = rev->data;
  234.     s = c->data;

  235.     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
  236.                    "mail auth http read handler");

  237.     ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);

  238.     if (rev->timedout) {
  239.         ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,
  240.                       "auth http server %V timed out", ctx->peer.name);
  241.         ngx_close_connection(c);
  242.         ngx_destroy_pool(ctx->pool);
  243.         ngx_mail_session_internal_server_error(s);
  244.         return;
  245.     }

  246.     if (ctx->response == NULL) {
  247.         ctx->response = ngx_create_temp_buf(ctx->pool, 1024);
  248.         if (ctx->response == NULL) {
  249.             ngx_close_connection(c);
  250.             ngx_destroy_pool(ctx->pool);
  251.             ngx_mail_session_internal_server_error(s);
  252.             return;
  253.         }
  254.     }

  255.     size = ctx->response->end - ctx->response->last;

  256.     n = ngx_recv(c, ctx->response->pos, size);

  257.     if (n > 0) {
  258.         ctx->response->last += n;

  259.         ctx->handler(s, ctx);
  260.         return;
  261.     }

  262.     if (n == NGX_AGAIN) {
  263.         return;
  264.     }

  265.     ngx_close_connection(c);
  266.     ngx_destroy_pool(ctx->pool);
  267.     ngx_mail_session_internal_server_error(s);
  268. }


  269. static void
  270. ngx_mail_auth_http_ignore_status_line(ngx_mail_session_t *s,
  271.     ngx_mail_auth_http_ctx_t *ctx)
  272. {
  273.     u_char  *p, ch;
  274.     enum  {
  275.         sw_start = 0,
  276.         sw_H,
  277.         sw_HT,
  278.         sw_HTT,
  279.         sw_HTTP,
  280.         sw_skip,
  281.         sw_almost_done
  282.     } state;

  283.     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
  284.                    "mail auth http process status line");

  285.     state = ctx->state;

  286.     for (p = ctx->response->pos; p < ctx->response->last; p++) {
  287.         ch = *p;

  288.         switch (state) {

  289.         /* "HTTP/" */
  290.         case sw_start:
  291.             if (ch == 'H') {
  292.                 state = sw_H;
  293.                 break;
  294.             }
  295.             goto next;

  296.         case sw_H:
  297.             if (ch == 'T') {
  298.                 state = sw_HT;
  299.                 break;
  300.             }
  301.             goto next;

  302.         case sw_HT:
  303.             if (ch == 'T') {
  304.                 state = sw_HTT;
  305.                 break;
  306.             }
  307.             goto next;

  308.         case sw_HTT:
  309.             if (ch == 'P') {
  310.                 state = sw_HTTP;
  311.                 break;
  312.             }
  313.             goto next;

  314.         case sw_HTTP:
  315.             if (ch == '/') {
  316.                 state = sw_skip;
  317.                 break;
  318.             }
  319.             goto next;

  320.         /* any text until end of line */
  321.         case sw_skip:
  322.             switch (ch) {
  323.             case CR:
  324.                 state = sw_almost_done;

  325.                 break;
  326.             case LF:
  327.                 goto done;
  328.             }
  329.             break;

  330.         /* end of status line */
  331.         case sw_almost_done:
  332.             if (ch == LF) {
  333.                 goto done;
  334.             }

  335.             ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
  336.                           "auth http server %V sent invalid response",
  337.                           ctx->peer.name);
  338.             ngx_close_connection(ctx->peer.connection);
  339.             ngx_destroy_pool(ctx->pool);
  340.             ngx_mail_session_internal_server_error(s);
  341.             return;
  342.         }
  343.     }

  344.     ctx->response->pos = p;
  345.     ctx->state = state;

  346.     return;

  347. next:

  348.     p = ctx->response->start - 1;

  349. done:

  350.     ctx->response->pos = p + 1;
  351.     ctx->state = 0;
  352.     ctx->handler = ngx_mail_auth_http_process_headers;
  353.     ctx->handler(s, ctx);
  354. }


  355. static void
  356. ngx_mail_auth_http_process_headers(ngx_mail_session_t *s,
  357.     ngx_mail_auth_http_ctx_t *ctx)
  358. {
  359.     u_char      *p;
  360.     time_t       timer;
  361.     size_t       len, size;
  362.     ngx_int_t    rc, port, n;
  363.     ngx_addr_t  *peer;

  364.     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
  365.                    "mail auth http process headers");

  366.     for ( ;; ) {
  367.         rc = ngx_mail_auth_http_parse_header_line(s, ctx);

  368.         if (rc == NGX_OK) {

  369. #if (NGX_DEBUG)
  370.             {
  371.             ngx_str_t  key, value;

  372.             key.len = ctx->header_name_end - ctx->header_name_start;
  373.             key.data = ctx->header_name_start;
  374.             value.len = ctx->header_end - ctx->header_start;
  375.             value.data = ctx->header_start;

  376.             ngx_log_debug2(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
  377.                            "mail auth http header: \"%V: %V\"",
  378.                            &key, &value);
  379.             }
  380. #endif

  381.             len = ctx->header_name_end - ctx->header_name_start;

  382.             if (len == sizeof("Auth-Status") - 1
  383.                 && ngx_strncasecmp(ctx->header_name_start,
  384.                                    (u_char *) "Auth-Status",
  385.                                    sizeof("Auth-Status") - 1)
  386.                    == 0)
  387.             {
  388.                 len = ctx->header_end - ctx->header_start;

  389.                 if (len == 2
  390.                     && ctx->header_start[0] == 'O'
  391.                     && ctx->header_start[1] == 'K')
  392.                 {
  393.                     continue;
  394.                 }

  395.                 if (len == 4
  396.                     && ctx->header_start[0] == 'W'
  397.                     && ctx->header_start[1] == 'A'
  398.                     && ctx->header_start[2] == 'I'
  399.                     && ctx->header_start[3] == 'T')
  400.                 {
  401.                     s->auth_wait = 1;
  402.                     continue;
  403.                 }

  404.                 ctx->errmsg.len = len;
  405.                 ctx->errmsg.data = ctx->header_start;

  406.                 switch (s->protocol) {

  407.                 case NGX_MAIL_POP3_PROTOCOL:
  408.                     size = sizeof("-ERR ") - 1 + len + sizeof(CRLF) - 1;
  409.                     break;

  410.                 case NGX_MAIL_IMAP_PROTOCOL:
  411.                     size = s->tag.len + sizeof("NO ") - 1 + len
  412.                            + sizeof(CRLF) - 1;
  413.                     break;

  414.                 default: /* NGX_MAIL_SMTP_PROTOCOL */
  415.                     ctx->err = ctx->errmsg;
  416.                     continue;
  417.                 }

  418.                 p = ngx_pnalloc(s->connection->pool, size);
  419.                 if (p == NULL) {
  420.                     ngx_close_connection(ctx->peer.connection);
  421.                     ngx_destroy_pool(ctx->pool);
  422.                     ngx_mail_session_internal_server_error(s);
  423.                     return;
  424.                 }

  425.                 ctx->err.data = p;

  426.                 switch (s->protocol) {

  427.                 case NGX_MAIL_POP3_PROTOCOL:
  428.                     *p++ = '-'; *p++ = 'E'; *p++ = 'R'; *p++ = 'R'; *p++ = ' ';
  429.                     break;

  430.                 case NGX_MAIL_IMAP_PROTOCOL:
  431.                     p = ngx_cpymem(p, s->tag.data, s->tag.len);
  432.                     *p++ = 'N'; *p++ = 'O'; *p++ = ' ';
  433.                     break;

  434.                 default: /* NGX_MAIL_SMTP_PROTOCOL */
  435.                     break;
  436.                 }

  437.                 p = ngx_cpymem(p, ctx->header_start, len);
  438.                 *p++ = CR; *p++ = LF;

  439.                 ctx->err.len = p - ctx->err.data;

  440.                 continue;
  441.             }

  442.             if (len == sizeof("Auth-Server") - 1
  443.                 && ngx_strncasecmp(ctx->header_name_start,
  444.                                    (u_char *) "Auth-Server",
  445.                                    sizeof("Auth-Server") - 1)
  446.                     == 0)
  447.             {
  448.                 ctx->addr.len = ctx->header_end - ctx->header_start;
  449.                 ctx->addr.data = ctx->header_start;

  450.                 continue;
  451.             }

  452.             if (len == sizeof("Auth-Port") - 1
  453.                 && ngx_strncasecmp(ctx->header_name_start,
  454.                                    (u_char *) "Auth-Port",
  455.                                    sizeof("Auth-Port") - 1)
  456.                    == 0)
  457.             {
  458.                 ctx->port.len = ctx->header_end - ctx->header_start;
  459.                 ctx->port.data = ctx->header_start;

  460.                 continue;
  461.             }

  462.             if (len == sizeof("Auth-User") - 1
  463.                 && ngx_strncasecmp(ctx->header_name_start,
  464.                                    (u_char *) "Auth-User",
  465.                                    sizeof("Auth-User") - 1)
  466.                    == 0)
  467.             {
  468.                 s->login.len = ctx->header_end - ctx->header_start;

  469.                 s->login.data = ngx_pnalloc(s->connection->pool, s->login.len);
  470.                 if (s->login.data == NULL) {
  471.                     ngx_close_connection(ctx->peer.connection);
  472.                     ngx_destroy_pool(ctx->pool);
  473.                     ngx_mail_session_internal_server_error(s);
  474.                     return;
  475.                 }

  476.                 ngx_memcpy(s->login.data, ctx->header_start, s->login.len);

  477.                 continue;
  478.             }

  479.             if (len == sizeof("Auth-Pass") - 1
  480.                 && ngx_strncasecmp(ctx->header_name_start,
  481.                                    (u_char *) "Auth-Pass",
  482.                                    sizeof("Auth-Pass") - 1)
  483.                    == 0)
  484.             {
  485.                 s->passwd.len = ctx->header_end - ctx->header_start;

  486.                 s->passwd.data = ngx_pnalloc(s->connection->pool,
  487.                                              s->passwd.len);
  488.                 if (s->passwd.data == NULL) {
  489.                     ngx_close_connection(ctx->peer.connection);
  490.                     ngx_destroy_pool(ctx->pool);
  491.                     ngx_mail_session_internal_server_error(s);
  492.                     return;
  493.                 }

  494.                 ngx_memcpy(s->passwd.data, ctx->header_start, s->passwd.len);

  495.                 continue;
  496.             }

  497.             if (len == sizeof("Auth-Wait") - 1
  498.                 && ngx_strncasecmp(ctx->header_name_start,
  499.                                    (u_char *) "Auth-Wait",
  500.                                    sizeof("Auth-Wait") - 1)
  501.                    == 0)
  502.             {
  503.                 n = ngx_atoi(ctx->header_start,
  504.                              ctx->header_end - ctx->header_start);

  505.                 if (n != NGX_ERROR) {
  506.                     ctx->sleep = n;
  507.                 }

  508.                 continue;
  509.             }

  510.             if (len == sizeof("Auth-Error-Code") - 1
  511.                 && ngx_strncasecmp(ctx->header_name_start,
  512.                                    (u_char *) "Auth-Error-Code",
  513.                                    sizeof("Auth-Error-Code") - 1)
  514.                    == 0)
  515.             {
  516.                 ctx->errcode.len = ctx->header_end - ctx->header_start;

  517.                 ctx->errcode.data = ngx_pnalloc(s->connection->pool,
  518.                                                 ctx->errcode.len);
  519.                 if (ctx->errcode.data == NULL) {
  520.                     ngx_close_connection(ctx->peer.connection);
  521.                     ngx_destroy_pool(ctx->pool);
  522.                     ngx_mail_session_internal_server_error(s);
  523.                     return;
  524.                 }

  525.                 ngx_memcpy(ctx->errcode.data, ctx->header_start,
  526.                            ctx->errcode.len);

  527.                 continue;
  528.             }

  529.             /* ignore other headers */

  530.             continue;
  531.         }

  532.         if (rc == NGX_DONE) {
  533.             ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
  534.                            "mail auth http header done");

  535.             ngx_close_connection(ctx->peer.connection);

  536.             if (ctx->err.len) {

  537.                 ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
  538.                               "client login failed: \"%V\"", &ctx->errmsg);

  539.                 if (s->protocol == NGX_MAIL_SMTP_PROTOCOL) {

  540.                     if (ctx->errcode.len == 0) {
  541.                         ctx->errcode = ngx_mail_smtp_errcode;
  542.                     }

  543.                     ctx->err.len = ctx->errcode.len + ctx->errmsg.len
  544.                                    + sizeof(" " CRLF) - 1;

  545.                     p = ngx_pnalloc(s->connection->pool, ctx->err.len);
  546.                     if (p == NULL) {
  547.                         ngx_destroy_pool(ctx->pool);
  548.                         ngx_mail_session_internal_server_error(s);
  549.                         return;
  550.                     }

  551.                     ctx->err.data = p;

  552.                     p = ngx_cpymem(p, ctx->errcode.data, ctx->errcode.len);
  553.                     *p++ = ' ';
  554.                     p = ngx_cpymem(p, ctx->errmsg.data, ctx->errmsg.len);
  555.                     *p++ = CR; *p = LF;
  556.                 }

  557.                 s->out = ctx->err;
  558.                 timer = ctx->sleep;

  559.                 ngx_destroy_pool(ctx->pool);

  560.                 if (timer == 0) {
  561.                     s->quit = 1;
  562.                     ngx_mail_send(s->connection->write);
  563.                     return;
  564.                 }

  565.                 ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000));

  566.                 s->connection->read->handler = ngx_mail_auth_sleep_handler;

  567.                 return;
  568.             }

  569.             if (s->auth_wait) {
  570.                 timer = ctx->sleep;

  571.                 ngx_destroy_pool(ctx->pool);

  572.                 if (timer == 0) {
  573.                     ngx_mail_auth_http_init(s);
  574.                     return;
  575.                 }

  576.                 ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000));

  577.                 s->connection->read->handler = ngx_mail_auth_sleep_handler;

  578.                 return;
  579.             }

  580.             if (ctx->addr.len == 0 || ctx->port.len == 0) {
  581.                 ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
  582.                               "auth http server %V did not send server or port",
  583.                               ctx->peer.name);
  584.                 ngx_destroy_pool(ctx->pool);
  585.                 ngx_mail_session_internal_server_error(s);
  586.                 return;
  587.             }

  588.             if (s->passwd.data == NULL
  589.                 && s->protocol != NGX_MAIL_SMTP_PROTOCOL)
  590.             {
  591.                 ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
  592.                               "auth http server %V did not send password",
  593.                               ctx->peer.name);
  594.                 ngx_destroy_pool(ctx->pool);
  595.                 ngx_mail_session_internal_server_error(s);
  596.                 return;
  597.             }

  598.             peer = ngx_pcalloc(s->connection->pool, sizeof(ngx_addr_t));
  599.             if (peer == NULL) {
  600.                 ngx_destroy_pool(ctx->pool);
  601.                 ngx_mail_session_internal_server_error(s);
  602.                 return;
  603.             }

  604.             rc = ngx_parse_addr(s->connection->pool, peer,
  605.                                 ctx->addr.data, ctx->addr.len);

  606.             switch (rc) {
  607.             case NGX_OK:
  608.                 break;

  609.             case NGX_DECLINED:
  610.                 ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
  611.                               "auth http server %V sent invalid server "
  612.                               "address:\"%V\"",
  613.                               ctx->peer.name, &ctx->addr);
  614.                 /* fall through */

  615.             default:
  616.                 ngx_destroy_pool(ctx->pool);
  617.                 ngx_mail_session_internal_server_error(s);
  618.                 return;
  619.             }

  620.             port = ngx_atoi(ctx->port.data, ctx->port.len);
  621.             if (port == NGX_ERROR || port < 1 || port > 65535) {
  622.                 ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
  623.                               "auth http server %V sent invalid server "
  624.                               "port:\"%V\"",
  625.                               ctx->peer.name, &ctx->port);
  626.                 ngx_destroy_pool(ctx->pool);
  627.                 ngx_mail_session_internal_server_error(s);
  628.                 return;
  629.             }

  630.             ngx_inet_set_port(peer->sockaddr, (in_port_t) port);

  631.             len = ctx->addr.len + 1 + ctx->port.len;

  632.             peer->name.len = len;

  633.             peer->name.data = ngx_pnalloc(s->connection->pool, len);
  634.             if (peer->name.data == NULL) {
  635.                 ngx_destroy_pool(ctx->pool);
  636.                 ngx_mail_session_internal_server_error(s);
  637.                 return;
  638.             }

  639.             len = ctx->addr.len;

  640.             ngx_memcpy(peer->name.data, ctx->addr.data, len);

  641.             peer->name.data[len++] = ':';

  642.             ngx_memcpy(peer->name.data + len, ctx->port.data, ctx->port.len);

  643.             ngx_destroy_pool(ctx->pool);
  644.             ngx_mail_proxy_init(s, peer);

  645.             return;
  646.         }

  647.         if (rc == NGX_AGAIN ) {
  648.             return;
  649.         }

  650.         /* rc == NGX_ERROR */

  651.         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
  652.                       "auth http server %V sent invalid header in response",
  653.                       ctx->peer.name);
  654.         ngx_close_connection(ctx->peer.connection);
  655.         ngx_destroy_pool(ctx->pool);
  656.         ngx_mail_session_internal_server_error(s);

  657.         return;
  658.     }
  659. }


  660. static void
  661. ngx_mail_auth_sleep_handler(ngx_event_t *rev)
  662. {
  663.     ngx_connection_t          *c;
  664.     ngx_mail_session_t        *s;
  665.     ngx_mail_core_srv_conf_t  *cscf;

  666.     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail auth sleep handler");

  667.     c = rev->data;
  668.     s = c->data;

  669.     if (rev->timedout) {

  670.         rev->timedout = 0;

  671.         if (s->auth_wait) {
  672.             s->auth_wait = 0;
  673.             ngx_mail_auth_http_init(s);
  674.             return;
  675.         }

  676.         cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

  677.         rev->handler = cscf->protocol->auth_state;

  678.         s->mail_state = 0;
  679.         s->auth_method = NGX_MAIL_AUTH_PLAIN;

  680.         c->log->action = "in auth state";

  681.         ngx_mail_send(c->write);

  682.         if (c->destroyed) {
  683.             return;
  684.         }

  685.         ngx_add_timer(rev, cscf->timeout);

  686.         if (rev->ready) {
  687.             rev->handler(rev);
  688.             return;
  689.         }

  690.         if (ngx_handle_read_event(rev, 0) != NGX_OK) {
  691.             ngx_mail_close_connection(c);
  692.         }

  693.         return;
  694.     }

  695.     if (rev->active) {
  696.         if (ngx_handle_read_event(rev, 0) != NGX_OK) {
  697.             ngx_mail_close_connection(c);
  698.         }
  699.     }
  700. }


  701. static ngx_int_t
  702. ngx_mail_auth_http_parse_header_line(ngx_mail_session_t *s,
  703.     ngx_mail_auth_http_ctx_t *ctx)
  704. {
  705.     u_char      c, ch, *p;
  706.     enum {
  707.         sw_start = 0,
  708.         sw_name,
  709.         sw_space_before_value,
  710.         sw_value,
  711.         sw_space_after_value,
  712.         sw_almost_done,
  713.         sw_header_almost_done
  714.     } state;

  715.     state = ctx->state;

  716.     for (p = ctx->response->pos; p < ctx->response->last; p++) {
  717.         ch = *p;

  718.         switch (state) {

  719.         /* first char */
  720.         case sw_start:

  721.             switch (ch) {
  722.             case CR:
  723.                 ctx->header_end = p;
  724.                 state = sw_header_almost_done;
  725.                 break;
  726.             case LF:
  727.                 ctx->header_end = p;
  728.                 goto header_done;
  729.             default:
  730.                 state = sw_name;
  731.                 ctx->header_name_start = p;

  732.                 c = (u_char) (ch | 0x20);
  733.                 if (c >= 'a' && c <= 'z') {
  734.                     break;
  735.                 }

  736.                 if (ch >= '0' && ch <= '9') {
  737.                     break;
  738.                 }

  739.                 return NGX_ERROR;
  740.             }
  741.             break;

  742.         /* header name */
  743.         case sw_name:
  744.             c = (u_char) (ch | 0x20);
  745.             if (c >= 'a' && c <= 'z') {
  746.                 break;
  747.             }

  748.             if (ch == ':') {
  749.                 ctx->header_name_end = p;
  750.                 state = sw_space_before_value;
  751.                 break;
  752.             }

  753.             if (ch == '-') {
  754.                 break;
  755.             }

  756.             if (ch >= '0' && ch <= '9') {
  757.                 break;
  758.             }

  759.             if (ch == CR) {
  760.                 ctx->header_name_end = p;
  761.                 ctx->header_start = p;
  762.                 ctx->header_end = p;
  763.                 state = sw_almost_done;
  764.                 break;
  765.             }

  766.             if (ch == LF) {
  767.                 ctx->header_name_end = p;
  768.                 ctx->header_start = p;
  769.                 ctx->header_end = p;
  770.                 goto done;
  771.             }

  772.             return NGX_ERROR;

  773.         /* space* before header value */
  774.         case sw_space_before_value:
  775.             switch (ch) {
  776.             case ' ':
  777.                 break;
  778.             case CR:
  779.                 ctx->header_start = p;
  780.                 ctx->header_end = p;
  781.                 state = sw_almost_done;
  782.                 break;
  783.             case LF:
  784.                 ctx->header_start = p;
  785.                 ctx->header_end = p;
  786.                 goto done;
  787.             default:
  788.                 ctx->header_start = p;
  789.                 state = sw_value;
  790.                 break;
  791.             }
  792.             break;

  793.         /* header value */
  794.         case sw_value:
  795.             switch (ch) {
  796.             case ' ':
  797.                 ctx->header_end = p;
  798.                 state = sw_space_after_value;
  799.                 break;
  800.             case CR:
  801.                 ctx->header_end = p;
  802.                 state = sw_almost_done;
  803.                 break;
  804.             case LF:
  805.                 ctx->header_end = p;
  806.                 goto done;
  807.             }
  808.             break;

  809.         /* space* before end of header line */
  810.         case sw_space_after_value:
  811.             switch (ch) {
  812.             case ' ':
  813.                 break;
  814.             case CR:
  815.                 state = sw_almost_done;
  816.                 break;
  817.             case LF:
  818.                 goto done;
  819.             default:
  820.                 state = sw_value;
  821.                 break;
  822.             }
  823.             break;

  824.         /* end of header line */
  825.         case sw_almost_done:
  826.             switch (ch) {
  827.             case LF:
  828.                 goto done;
  829.             default:
  830.                 return NGX_ERROR;
  831.             }

  832.         /* end of header */
  833.         case sw_header_almost_done:
  834.             switch (ch) {
  835.             case LF:
  836.                 goto header_done;
  837.             default:
  838.                 return NGX_ERROR;
  839.             }
  840.         }
  841.     }

  842.     ctx->response->pos = p;
  843.     ctx->state = state;

  844.     return NGX_AGAIN;

  845. done:

  846.     ctx->response->pos = p + 1;
  847.     ctx->state = sw_start;

  848.     return NGX_OK;

  849. header_done:

  850.     ctx->response->pos = p + 1;
  851.     ctx->state = sw_start;

  852.     return NGX_DONE;
  853. }


  854. static void
  855. ngx_mail_auth_http_block_read(ngx_event_t *rev)
  856. {
  857.     ngx_connection_t          *c;
  858.     ngx_mail_session_t        *s;
  859.     ngx_mail_auth_http_ctx_t  *ctx;

  860.     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
  861.                    "mail auth http block read");

  862.     if (ngx_handle_read_event(rev, 0) != NGX_OK) {
  863.         c = rev->data;
  864.         s = c->data;

  865.         ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);

  866.         ngx_close_connection(ctx->peer.connection);
  867.         ngx_destroy_pool(ctx->pool);
  868.         ngx_mail_session_internal_server_error(s);
  869.     }
  870. }


  871. static void
  872. ngx_mail_auth_http_dummy_handler(ngx_event_t *ev)
  873. {
  874.     ngx_log_debug0(NGX_LOG_DEBUG_MAIL, ev->log, 0,
  875.                    "mail auth http dummy handler");
  876. }


  877. static ngx_buf_t *
  878. ngx_mail_auth_http_create_request(ngx_mail_session_t *s, ngx_pool_t *pool,
  879.     ngx_mail_auth_http_conf_t *ahcf)
  880. {
  881.     size_t                     len;
  882.     ngx_buf_t                 *b;
  883.     ngx_str_t                  login, passwd;
  884.     ngx_connection_t          *c;
  885. #if (NGX_MAIL_SSL)
  886.     ngx_str_t                  protocol, cipher, verify, subject, issuer,
  887.                                serial, fingerprint, raw_cert, cert;
  888.     ngx_mail_ssl_conf_t       *sslcf;
  889. #endif
  890.     ngx_mail_core_srv_conf_t  *cscf;

  891.     if (ngx_mail_auth_http_escape(pool, &s->login, &login) != NGX_OK) {
  892.         return NULL;
  893.     }

  894.     if (ngx_mail_auth_http_escape(pool, &s->passwd, &passwd) != NGX_OK) {
  895.         return NULL;
  896.     }

  897.     c = s->connection;

  898. #if (NGX_MAIL_SSL)

  899.     if (c->ssl) {

  900.         if (ngx_ssl_get_protocol(c, pool, &protocol) != NGX_OK) {
  901.             return NULL;
  902.         }

  903.         protocol.len = ngx_strlen(protocol.data);

  904.         if (ngx_ssl_get_cipher_name(c, pool, &cipher) != NGX_OK) {
  905.             return NULL;
  906.         }

  907.         cipher.len = ngx_strlen(cipher.data);

  908.     } else {
  909.         ngx_str_null(&protocol);
  910.         ngx_str_null(&cipher);
  911.     }

  912.     sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);

  913.     if (c->ssl && sslcf->verify) {

  914.         /* certificate details */

  915.         if (ngx_ssl_get_client_verify(c, pool, &verify) != NGX_OK) {
  916.             return NULL;
  917.         }

  918.         if (ngx_ssl_get_subject_dn(c, pool, &subject) != NGX_OK) {
  919.             return NULL;
  920.         }

  921.         if (ngx_ssl_get_issuer_dn(c, pool, &issuer) != NGX_OK) {
  922.             return NULL;
  923.         }

  924.         if (ngx_ssl_get_serial_number(c, pool, &serial) != NGX_OK) {
  925.             return NULL;
  926.         }

  927.         if (ngx_ssl_get_fingerprint(c, pool, &fingerprint) != NGX_OK) {
  928.             return NULL;
  929.         }

  930.         if (ahcf->pass_client_cert) {

  931.             /* certificate itself, if configured */

  932.             if (ngx_ssl_get_raw_certificate(c, pool, &raw_cert) != NGX_OK) {
  933.                 return NULL;
  934.             }

  935.             if (ngx_mail_auth_http_escape(pool, &raw_cert, &cert) != NGX_OK) {
  936.                 return NULL;
  937.             }

  938.         } else {
  939.             ngx_str_null(&cert);
  940.         }

  941.     } else {
  942.         ngx_str_null(&verify);
  943.         ngx_str_null(&subject);
  944.         ngx_str_null(&issuer);
  945.         ngx_str_null(&serial);
  946.         ngx_str_null(&fingerprint);
  947.         ngx_str_null(&cert);
  948.     }

  949. #endif

  950.     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

  951.     len = sizeof("GET ") - 1 + ahcf->uri.len + sizeof(" HTTP/1.0" CRLF) - 1
  952.           + sizeof("Host: ") - 1 + ahcf->host_header.len + sizeof(CRLF) - 1
  953.           + sizeof("Auth-Method: ") - 1
  954.                 + ngx_mail_auth_http_method[s->auth_method].len
  955.                 + sizeof(CRLF) - 1
  956.           + sizeof("Auth-User: ") - 1 + login.len + sizeof(CRLF) - 1
  957.           + sizeof("Auth-Pass: ") - 1 + passwd.len + sizeof(CRLF) - 1
  958.           + sizeof("Auth-Salt: ") - 1 + s->salt.len
  959.           + sizeof("Auth-Protocol: ") - 1 + cscf->protocol->name.len
  960.                 + sizeof(CRLF) - 1
  961.           + sizeof("Auth-Login-Attempt: ") - 1 + NGX_INT_T_LEN
  962.                 + sizeof(CRLF) - 1
  963.           + sizeof("Client-IP: ") - 1 + s->connection->addr_text.len
  964.                 + sizeof(CRLF) - 1
  965.           + sizeof("Client-Host: ") - 1 + s->host.len + sizeof(CRLF) - 1
  966.           + ahcf->header.len
  967.           + sizeof(CRLF) - 1;

  968.     if (c->proxy_protocol) {
  969.         len += sizeof("Proxy-Protocol-Addr: ") - 1
  970.                      + c->proxy_protocol->src_addr.len + sizeof(CRLF) - 1
  971.                + sizeof("Proxy-Protocol-Port: ") - 1
  972.                      + sizeof("65535") - 1 + sizeof(CRLF) - 1
  973.                + sizeof("Proxy-Protocol-Server-Addr: ") - 1
  974.                      + c->proxy_protocol->dst_addr.len + sizeof(CRLF) - 1
  975.                + sizeof("Proxy-Protocol-Server-Port: ") - 1
  976.                      + sizeof("65535") - 1 + sizeof(CRLF) - 1;
  977.     }

  978.     if (s->auth_method == NGX_MAIL_AUTH_NONE) {
  979.         len += sizeof("Auth-SMTP-Helo: ") - 1 + s->smtp_helo.len
  980.                      + sizeof(CRLF) - 1
  981.                + sizeof("Auth-SMTP-From: ") - 1 + s->smtp_from.len
  982.                      + sizeof(CRLF) - 1
  983.                + sizeof("Auth-SMTP-To: ") - 1 + s->smtp_to.len
  984.                      + sizeof(CRLF) - 1;
  985.     }

  986. #if (NGX_MAIL_SSL)

  987.     if (c->ssl) {
  988.         len += sizeof("Auth-SSL: on" CRLF) - 1
  989.                + sizeof("Auth-SSL-Protocol: ") - 1 + protocol.len
  990.                      + sizeof(CRLF) - 1
  991.                + sizeof("Auth-SSL-Cipher: ") - 1 + cipher.len
  992.                      + sizeof(CRLF) - 1
  993.                + sizeof("Auth-SSL-Verify: ") - 1 + verify.len
  994.                      + sizeof(CRLF) - 1
  995.                + sizeof("Auth-SSL-Subject: ") - 1 + subject.len
  996.                      + sizeof(CRLF) - 1
  997.                + sizeof("Auth-SSL-Issuer: ") - 1 + issuer.len
  998.                      + sizeof(CRLF) - 1
  999.                + sizeof("Auth-SSL-Serial: ") - 1 + serial.len
  1000.                      + sizeof(CRLF) - 1
  1001.                + sizeof("Auth-SSL-Fingerprint: ") - 1 + fingerprint.len
  1002.                      + sizeof(CRLF) - 1
  1003.                + sizeof("Auth-SSL-Cert: ") - 1 + cert.len
  1004.                      + sizeof(CRLF) - 1;
  1005.     }

  1006. #endif

  1007.     b = ngx_create_temp_buf(pool, len);
  1008.     if (b == NULL) {
  1009.         return NULL;
  1010.     }

  1011.     b->last = ngx_cpymem(b->last, "GET ", sizeof("GET ") - 1);
  1012.     b->last = ngx_copy(b->last, ahcf->uri.data, ahcf->uri.len);
  1013.     b->last = ngx_cpymem(b->last, " HTTP/1.0" CRLF,
  1014.                          sizeof(" HTTP/1.0" CRLF) - 1);

  1015.     b->last = ngx_cpymem(b->last, "Host: ", sizeof("Host: ") - 1);
  1016.     b->last = ngx_copy(b->last, ahcf->host_header.data,
  1017.                          ahcf->host_header.len);
  1018.     *b->last++ = CR; *b->last++ = LF;

  1019.     b->last = ngx_cpymem(b->last, "Auth-Method: ",
  1020.                          sizeof("Auth-Method: ") - 1);
  1021.     b->last = ngx_cpymem(b->last,
  1022.                          ngx_mail_auth_http_method[s->auth_method].data,
  1023.                          ngx_mail_auth_http_method[s->auth_method].len);
  1024.     *b->last++ = CR; *b->last++ = LF;

  1025.     b->last = ngx_cpymem(b->last, "Auth-User: ", sizeof("Auth-User: ") - 1);
  1026.     b->last = ngx_copy(b->last, login.data, login.len);
  1027.     *b->last++ = CR; *b->last++ = LF;

  1028.     b->last = ngx_cpymem(b->last, "Auth-Pass: ", sizeof("Auth-Pass: ") - 1);
  1029.     b->last = ngx_copy(b->last, passwd.data, passwd.len);
  1030.     *b->last++ = CR; *b->last++ = LF;

  1031.     if (s->auth_method != NGX_MAIL_AUTH_PLAIN && s->salt.len) {
  1032.         b->last = ngx_cpymem(b->last, "Auth-Salt: ", sizeof("Auth-Salt: ") - 1);
  1033.         b->last = ngx_copy(b->last, s->salt.data, s->salt.len);

  1034.         s->passwd.data = NULL;
  1035.     }

  1036.     b->last = ngx_cpymem(b->last, "Auth-Protocol: ",
  1037.                          sizeof("Auth-Protocol: ") - 1);
  1038.     b->last = ngx_cpymem(b->last, cscf->protocol->name.data,
  1039.                          cscf->protocol->name.len);
  1040.     *b->last++ = CR; *b->last++ = LF;

  1041.     b->last = ngx_sprintf(b->last, "Auth-Login-Attempt: %ui" CRLF,
  1042.                           s->login_attempt);

  1043.     b->last = ngx_cpymem(b->last, "Client-IP: ", sizeof("Client-IP: ") - 1);
  1044.     b->last = ngx_copy(b->last, s->connection->addr_text.data,
  1045.                        s->connection->addr_text.len);
  1046.     *b->last++ = CR; *b->last++ = LF;

  1047.     if (s->host.len) {
  1048.         b->last = ngx_cpymem(b->last, "Client-Host: ",
  1049.                              sizeof("Client-Host: ") - 1);
  1050.         b->last = ngx_copy(b->last, s->host.data, s->host.len);
  1051.         *b->last++ = CR; *b->last++ = LF;
  1052.     }

  1053.     if (c->proxy_protocol) {
  1054.         b->last = ngx_cpymem(b->last, "Proxy-Protocol-Addr: ",
  1055.                              sizeof("Proxy-Protocol-Addr: ") - 1);
  1056.         b->last = ngx_copy(b->last, c->proxy_protocol->src_addr.data,
  1057.                            c->proxy_protocol->src_addr.len);
  1058.         *b->last++ = CR; *b->last++ = LF;

  1059.         b->last = ngx_sprintf(b->last, "Proxy-Protocol-Port: %d" CRLF,
  1060.                               c->proxy_protocol->src_port);

  1061.         b->last = ngx_cpymem(b->last, "Proxy-Protocol-Server-Addr: ",
  1062.                              sizeof("Proxy-Protocol-Server-Addr: ") - 1);
  1063.         b->last = ngx_copy(b->last, c->proxy_protocol->dst_addr.data,
  1064.                            c->proxy_protocol->dst_addr.len);
  1065.         *b->last++ = CR; *b->last++ = LF;

  1066.         b->last = ngx_sprintf(b->last, "Proxy-Protocol-Server-Port: %d" CRLF,
  1067.                               c->proxy_protocol->dst_port);
  1068.     }

  1069.     if (s->auth_method == NGX_MAIL_AUTH_NONE) {

  1070.         /* HELO, MAIL FROM, and RCPT TO can't contain CRLF, no need to escape */

  1071.         b->last = ngx_cpymem(b->last, "Auth-SMTP-Helo: ",
  1072.                              sizeof("Auth-SMTP-Helo: ") - 1);
  1073.         b->last = ngx_copy(b->last, s->smtp_helo.data, s->smtp_helo.len);
  1074.         *b->last++ = CR; *b->last++ = LF;

  1075.         b->last = ngx_cpymem(b->last, "Auth-SMTP-From: ",
  1076.                              sizeof("Auth-SMTP-From: ") - 1);
  1077.         b->last = ngx_copy(b->last, s->smtp_from.data, s->smtp_from.len);
  1078.         *b->last++ = CR; *b->last++ = LF;

  1079.         b->last = ngx_cpymem(b->last, "Auth-SMTP-To: ",
  1080.                              sizeof("Auth-SMTP-To: ") - 1);
  1081.         b->last = ngx_copy(b->last, s->smtp_to.data, s->smtp_to.len);
  1082.         *b->last++ = CR; *b->last++ = LF;

  1083.     }

  1084. #if (NGX_MAIL_SSL)

  1085.     if (c->ssl) {
  1086.         b->last = ngx_cpymem(b->last, "Auth-SSL: on" CRLF,
  1087.                              sizeof("Auth-SSL: on" CRLF) - 1);

  1088.         if (protocol.len) {
  1089.             b->last = ngx_cpymem(b->last, "Auth-SSL-Protocol: ",
  1090.                                  sizeof("Auth-SSL-Protocol: ") - 1);
  1091.             b->last = ngx_copy(b->last, protocol.data, protocol.len);
  1092.             *b->last++ = CR; *b->last++ = LF;
  1093.         }

  1094.         if (cipher.len) {
  1095.             b->last = ngx_cpymem(b->last, "Auth-SSL-Cipher: ",
  1096.                                  sizeof("Auth-SSL-Cipher: ") - 1);
  1097.             b->last = ngx_copy(b->last, cipher.data, cipher.len);
  1098.             *b->last++ = CR; *b->last++ = LF;
  1099.         }

  1100.         if (verify.len) {
  1101.             b->last = ngx_cpymem(b->last, "Auth-SSL-Verify: ",
  1102.                                  sizeof("Auth-SSL-Verify: ") - 1);
  1103.             b->last = ngx_copy(b->last, verify.data, verify.len);
  1104.             *b->last++ = CR; *b->last++ = LF;
  1105.         }

  1106.         if (subject.len) {
  1107.             b->last = ngx_cpymem(b->last, "Auth-SSL-Subject: ",
  1108.                                  sizeof("Auth-SSL-Subject: ") - 1);
  1109.             b->last = ngx_copy(b->last, subject.data, subject.len);
  1110.             *b->last++ = CR; *b->last++ = LF;
  1111.         }

  1112.         if (issuer.len) {
  1113.             b->last = ngx_cpymem(b->last, "Auth-SSL-Issuer: ",
  1114.                                  sizeof("Auth-SSL-Issuer: ") - 1);
  1115.             b->last = ngx_copy(b->last, issuer.data, issuer.len);
  1116.             *b->last++ = CR; *b->last++ = LF;
  1117.         }

  1118.         if (serial.len) {
  1119.             b->last = ngx_cpymem(b->last, "Auth-SSL-Serial: ",
  1120.                                  sizeof("Auth-SSL-Serial: ") - 1);
  1121.             b->last = ngx_copy(b->last, serial.data, serial.len);
  1122.             *b->last++ = CR; *b->last++ = LF;
  1123.         }

  1124.         if (fingerprint.len) {
  1125.             b->last = ngx_cpymem(b->last, "Auth-SSL-Fingerprint: ",
  1126.                                  sizeof("Auth-SSL-Fingerprint: ") - 1);
  1127.             b->last = ngx_copy(b->last, fingerprint.data, fingerprint.len);
  1128.             *b->last++ = CR; *b->last++ = LF;
  1129.         }

  1130.         if (cert.len) {
  1131.             b->last = ngx_cpymem(b->last, "Auth-SSL-Cert: ",
  1132.                                  sizeof("Auth-SSL-Cert: ") - 1);
  1133.             b->last = ngx_copy(b->last, cert.data, cert.len);
  1134.             *b->last++ = CR; *b->last++ = LF;
  1135.         }
  1136.     }

  1137. #endif

  1138.     if (ahcf->header.len) {
  1139.         b->last = ngx_copy(b->last, ahcf->header.data, ahcf->header.len);
  1140.     }

  1141.     /* add "\r\n" at the header end */
  1142.     *b->last++ = CR; *b->last++ = LF;

  1143. #if (NGX_DEBUG_MAIL_PASSWD)
  1144.     ngx_log_debug2(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
  1145.                    "mail auth http header:%N\"%*s\"",
  1146.                    (size_t) (b->last - b->pos), b->pos);
  1147. #endif

  1148.     return b;
  1149. }


  1150. static ngx_int_t
  1151. ngx_mail_auth_http_escape(ngx_pool_t *pool, ngx_str_t *text, ngx_str_t *escaped)
  1152. {
  1153.     u_char     *p;
  1154.     uintptr_t   n;

  1155.     n = ngx_escape_uri(NULL, text->data, text->len, NGX_ESCAPE_MAIL_AUTH);

  1156.     if (n == 0) {
  1157.         *escaped = *text;
  1158.         return NGX_OK;
  1159.     }

  1160.     escaped->len = text->len + n * 2;

  1161.     p = ngx_pnalloc(pool, escaped->len);
  1162.     if (p == NULL) {
  1163.         return NGX_ERROR;
  1164.     }

  1165.     (void) ngx_escape_uri(p, text->data, text->len, NGX_ESCAPE_MAIL_AUTH);

  1166.     escaped->data = p;

  1167.     return NGX_OK;
  1168. }


  1169. static void *
  1170. ngx_mail_auth_http_create_conf(ngx_conf_t *cf)
  1171. {
  1172.     ngx_mail_auth_http_conf_t  *ahcf;

  1173.     ahcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_auth_http_conf_t));
  1174.     if (ahcf == NULL) {
  1175.         return NULL;
  1176.     }

  1177.     ahcf->timeout = NGX_CONF_UNSET_MSEC;
  1178.     ahcf->pass_client_cert = NGX_CONF_UNSET;

  1179.     ahcf->file = cf->conf_file->file.name.data;
  1180.     ahcf->line = cf->conf_file->line;

  1181.     return ahcf;
  1182. }


  1183. static char *
  1184. ngx_mail_auth_http_merge_conf(ngx_conf_t *cf, void *parent, void *child)
  1185. {
  1186.     ngx_mail_auth_http_conf_t *prev = parent;
  1187.     ngx_mail_auth_http_conf_t *conf = child;

  1188.     u_char           *p;
  1189.     size_t            len;
  1190.     ngx_uint_t        i;
  1191.     ngx_table_elt_t  *header;

  1192.     if (conf->peer == NULL) {
  1193.         conf->peer = prev->peer;
  1194.         conf->host_header = prev->host_header;
  1195.         conf->uri = prev->uri;

  1196.         if (conf->peer == NULL) {
  1197.             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
  1198.                           "no \"auth_http\" is defined for server in %s:%ui",
  1199.                           conf->file, conf->line);

  1200.             return NGX_CONF_ERROR;
  1201.         }
  1202.     }

  1203.     ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);

  1204.     ngx_conf_merge_value(conf->pass_client_cert, prev->pass_client_cert, 0);

  1205.     if (conf->headers == NULL) {
  1206.         conf->headers = prev->headers;
  1207.         conf->header = prev->header;
  1208.     }

  1209.     if (conf->headers && conf->header.len == 0) {
  1210.         len = 0;
  1211.         header = conf->headers->elts;
  1212.         for (i = 0; i < conf->headers->nelts; i++) {
  1213.             len += header[i].key.len + 2 + header[i].value.len + 2;
  1214.         }

  1215.         p = ngx_pnalloc(cf->pool, len);
  1216.         if (p == NULL) {
  1217.             return NGX_CONF_ERROR;
  1218.         }

  1219.         conf->header.len = len;
  1220.         conf->header.data = p;

  1221.         for (i = 0; i < conf->headers->nelts; i++) {
  1222.             p = ngx_cpymem(p, header[i].key.data, header[i].key.len);
  1223.             *p++ = ':'; *p++ = ' ';
  1224.             p = ngx_cpymem(p, header[i].value.data, header[i].value.len);
  1225.             *p++ = CR; *p++ = LF;
  1226.         }
  1227.     }

  1228.     return NGX_CONF_OK;
  1229. }


  1230. static char *
  1231. ngx_mail_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  1232. {
  1233.     ngx_mail_auth_http_conf_t *ahcf = conf;

  1234.     ngx_str_t  *value;
  1235.     ngx_url_t   u;

  1236.     value = cf->args->elts;

  1237.     ngx_memzero(&u, sizeof(ngx_url_t));

  1238.     u.url = value[1];
  1239.     u.default_port = 80;
  1240.     u.uri_part = 1;

  1241.     if (ngx_strncmp(u.url.data, "http://", 7) == 0) {
  1242.         u.url.len -= 7;
  1243.         u.url.data += 7;
  1244.     }

  1245.     if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
  1246.         if (u.err) {
  1247.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1248.                                "%s in auth_http \"%V\"", u.err, &u.url);
  1249.         }

  1250.         return NGX_CONF_ERROR;
  1251.     }

  1252.     ahcf->peer = u.addrs;

  1253.     if (u.family != AF_UNIX) {
  1254.         ahcf->host_header = u.host;

  1255.     } else {
  1256.         ngx_str_set(&ahcf->host_header, "localhost");
  1257.     }

  1258.     ahcf->uri = u.uri;

  1259.     if (ahcf->uri.len == 0) {
  1260.         ngx_str_set(&ahcf->uri, "/");
  1261.     }

  1262.     return NGX_CONF_OK;
  1263. }


  1264. static char *
  1265. ngx_mail_auth_http_header(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  1266. {
  1267.     ngx_mail_auth_http_conf_t *ahcf = conf;

  1268.     ngx_str_t        *value;
  1269.     ngx_table_elt_t  *header;

  1270.     if (ahcf->headers == NULL) {
  1271.         ahcf->headers = ngx_array_create(cf->pool, 1, sizeof(ngx_table_elt_t));
  1272.         if (ahcf->headers == NULL) {
  1273.             return NGX_CONF_ERROR;
  1274.         }
  1275.     }

  1276.     header = ngx_array_push(ahcf->headers);
  1277.     if (header == NULL) {
  1278.         return NGX_CONF_ERROR;
  1279.     }

  1280.     value = cf->args->elts;

  1281.     header->key = value[1];
  1282.     header->value = value[2];

  1283.     return NGX_CONF_OK;
  1284. }