src/http/modules/ngx_http_browser_module.c - nginx source code

Global variables defined

Data types defined

Functions defined

Macros 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_http.h>


  8. /*
  9. * The module can check browser versions conforming to the following formats:
  10. * X, X.X, X.X.X, and X.X.X.X.  The maximum values of each format may be
  11. * 4000, 4000.99, 4000.99.99, and 4000.99.99.99.
  12. */


  13. #define  NGX_HTTP_MODERN_BROWSER   0
  14. #define  NGX_HTTP_ANCIENT_BROWSER  1


  15. typedef struct {
  16.     u_char                      browser[12];
  17.     size_t                      skip;
  18.     size_t                      add;
  19.     u_char                      name[12];
  20. } ngx_http_modern_browser_mask_t;


  21. typedef struct {
  22.     ngx_uint_t                  version;
  23.     size_t                      skip;
  24.     size_t                      add;
  25.     u_char                      name[12];
  26. } ngx_http_modern_browser_t;


  27. typedef struct {
  28.     ngx_array_t                *modern_browsers;
  29.     ngx_array_t                *ancient_browsers;
  30.     ngx_http_variable_value_t  *modern_browser_value;
  31.     ngx_http_variable_value_t  *ancient_browser_value;

  32.     unsigned                    modern_unlisted_browsers:1;
  33.     unsigned                    netscape4:1;
  34. } ngx_http_browser_conf_t;


  35. static ngx_int_t ngx_http_msie_variable(ngx_http_request_t *r,
  36.     ngx_http_variable_value_t *v, uintptr_t data);
  37. static ngx_int_t ngx_http_browser_variable(ngx_http_request_t *r,
  38.     ngx_http_variable_value_t *v, uintptr_t data);

  39. static ngx_uint_t ngx_http_browser(ngx_http_request_t *r,
  40.     ngx_http_browser_conf_t *cf);

  41. static ngx_int_t ngx_http_browser_add_variables(ngx_conf_t *cf);
  42. static void *ngx_http_browser_create_conf(ngx_conf_t *cf);
  43. static char *ngx_http_browser_merge_conf(ngx_conf_t *cf, void *parent,
  44.     void *child);
  45. static int ngx_libc_cdecl ngx_http_modern_browser_sort(const void *one,
  46.     const void *two);
  47. static char *ngx_http_modern_browser(ngx_conf_t *cf, ngx_command_t *cmd,
  48.     void *conf);
  49. static char *ngx_http_ancient_browser(ngx_conf_t *cf, ngx_command_t *cmd,
  50.     void *conf);
  51. static char *ngx_http_modern_browser_value(ngx_conf_t *cf, ngx_command_t *cmd,
  52.     void *conf);
  53. static char *ngx_http_ancient_browser_value(ngx_conf_t *cf, ngx_command_t *cmd,
  54.     void *conf);


  55. static ngx_command_t  ngx_http_browser_commands[] = {

  56.     { ngx_string("modern_browser"),
  57.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
  58.       ngx_http_modern_browser,
  59.       NGX_HTTP_LOC_CONF_OFFSET,
  60.       0,
  61.       NULL },

  62.     { ngx_string("ancient_browser"),
  63.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
  64.       ngx_http_ancient_browser,
  65.       NGX_HTTP_LOC_CONF_OFFSET,
  66.       0,
  67.       NULL },

  68.     { ngx_string("modern_browser_value"),
  69.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  70.       ngx_http_modern_browser_value,
  71.       NGX_HTTP_LOC_CONF_OFFSET,
  72.       0,
  73.       NULL },

  74.     { ngx_string("ancient_browser_value"),
  75.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  76.       ngx_http_ancient_browser_value,
  77.       NGX_HTTP_LOC_CONF_OFFSET,
  78.       0,
  79.       NULL },

  80.       ngx_null_command
  81. };


  82. static ngx_http_module_t  ngx_http_browser_module_ctx = {
  83.     ngx_http_browser_add_variables,        /* preconfiguration */
  84.     NULL,                                  /* postconfiguration */

  85.     NULL,                                  /* create main configuration */
  86.     NULL,                                  /* init main configuration */

  87.     NULL,                                  /* create server configuration */
  88.     NULL,                                  /* merge server configuration */

  89.     ngx_http_browser_create_conf,          /* create location configuration */
  90.     ngx_http_browser_merge_conf            /* merge location configuration */
  91. };


  92. ngx_module_t  ngx_http_browser_module = {
  93.     NGX_MODULE_V1,
  94.     &ngx_http_browser_module_ctx,          /* module context */
  95.     ngx_http_browser_commands,             /* module directives */
  96.     NGX_HTTP_MODULE,                       /* module type */
  97.     NULL,                                  /* init master */
  98.     NULL,                                  /* init module */
  99.     NULL,                                  /* init process */
  100.     NULL,                                  /* init thread */
  101.     NULL,                                  /* exit thread */
  102.     NULL,                                  /* exit process */
  103.     NULL,                                  /* exit master */
  104.     NGX_MODULE_V1_PADDING
  105. };


  106. static ngx_http_modern_browser_mask_t  ngx_http_modern_browser_masks[] = {

  107.     /* Opera must be the first browser to check */

  108.     /*
  109.      * "Opera/7.50 (X11; FreeBSD i386; U)  [en]"
  110.      * "Mozilla/5.0 (X11; FreeBSD i386; U) Opera 7.50  [en]"
  111.      * "Mozilla/4.0 (compatible; MSIE 6.0; X11; FreeBSD i386) Opera 7.50  [en]"
  112.      * "Opera/8.0 (Windows NT 5.1; U; ru)"
  113.      * "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; en) Opera 8.0"
  114.      * "Opera/9.01 (X11; FreeBSD 6 i386; U; en)"
  115.      */

  116.     { "opera",
  117.       0,
  118.       sizeof("Opera ") - 1,
  119.       "Opera"},

  120.     /* "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)" */

  121.     { "msie",
  122.       sizeof("Mozilla/4.0 (compatible; ") - 1,
  123.       sizeof("MSIE ") - 1,
  124.       "MSIE "},

  125.     /*
  126.      * "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.0.0) Gecko/20020610"
  127.      * "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.5) Gecko/20031006"
  128.      * "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.6) Gecko/20040206
  129.      *              Firefox/0.8"
  130.      * "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.7.8)
  131.      *              Gecko/20050511 Firefox/1.0.4"
  132.      * "Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.8.0.5) Gecko/20060729
  133.      *              Firefox/1.5.0.5"
  134.      */

  135.     { "gecko",
  136.       sizeof("Mozilla/5.0 (") - 1,
  137.       sizeof("rv:") - 1,
  138.       "rv:"},

  139.     /*
  140.      * "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ru-ru) AppleWebKit/125.2
  141.      *              (KHTML, like Gecko) Safari/125.7"
  142.      * "Mozilla/5.0 (SymbianOS/9.1; U; en-us) AppleWebKit/413
  143.      *              (KHTML, like Gecko) Safari/413"
  144.      * "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/418
  145.      *              (KHTML, like Gecko) Safari/417.9.3"
  146.      * "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ru-ru) AppleWebKit/418.8
  147.      *              (KHTML, like Gecko) Safari/419.3"
  148.      */

  149.     { "safari",
  150.       sizeof("Mozilla/5.0 (") - 1,
  151.       sizeof("Safari/") - 1,
  152.       "Safari/"},

  153.     /*
  154.      * "Mozilla/5.0 (compatible; Konqueror/3.1; Linux)"
  155.      * "Mozilla/5.0 (compatible; Konqueror/3.4; Linux) KHTML/3.4.2 (like Gecko)"
  156.      * "Mozilla/5.0 (compatible; Konqueror/3.5; FreeBSD) KHTML/3.5.1
  157.      *              (like Gecko)"
  158.      */

  159.     { "konqueror",
  160.       sizeof("Mozilla/5.0 (compatible; ") - 1,
  161.       sizeof("Konqueror/") - 1,
  162.       "Konqueror/"},

  163.     { "", 0, 0, "" }

  164. };


  165. static ngx_http_variable_t  ngx_http_browser_vars[] = {

  166.     { ngx_string("msie"), NULL, ngx_http_msie_variable,
  167.       0, NGX_HTTP_VAR_CHANGEABLE, 0 },

  168.     { ngx_string("modern_browser"), NULL, ngx_http_browser_variable,
  169.       NGX_HTTP_MODERN_BROWSER, NGX_HTTP_VAR_CHANGEABLE, 0 },

  170.     { ngx_string("ancient_browser"), NULL, ngx_http_browser_variable,
  171.       NGX_HTTP_ANCIENT_BROWSER, NGX_HTTP_VAR_CHANGEABLE, 0 },

  172.       ngx_http_null_variable
  173. };


  174. static ngx_int_t
  175. ngx_http_browser_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
  176.     uintptr_t data)
  177. {
  178.     ngx_uint_t                rc;
  179.     ngx_http_browser_conf_t  *cf;

  180.     cf = ngx_http_get_module_loc_conf(r, ngx_http_browser_module);

  181.     rc = ngx_http_browser(r, cf);

  182.     if (data == NGX_HTTP_MODERN_BROWSER && rc == NGX_HTTP_MODERN_BROWSER) {
  183.         *v = *cf->modern_browser_value;
  184.         return NGX_OK;
  185.     }

  186.     if (data == NGX_HTTP_ANCIENT_BROWSER && rc == NGX_HTTP_ANCIENT_BROWSER) {
  187.         *v = *cf->ancient_browser_value;
  188.         return NGX_OK;
  189.     }

  190.     *v = ngx_http_variable_null_value;
  191.     return NGX_OK;
  192. }


  193. static ngx_uint_t
  194. ngx_http_browser(ngx_http_request_t *r, ngx_http_browser_conf_t *cf)
  195. {
  196.     size_t                      len;
  197.     u_char                     *name, *ua, *last, c;
  198.     ngx_str_t                  *ancient;
  199.     ngx_uint_t                  i, version, ver, scale;
  200.     ngx_http_modern_browser_t  *modern;

  201.     if (r->headers_in.user_agent == NULL) {
  202.         if (cf->modern_unlisted_browsers) {
  203.             return NGX_HTTP_MODERN_BROWSER;
  204.         }

  205.         return NGX_HTTP_ANCIENT_BROWSER;
  206.     }

  207.     ua = r->headers_in.user_agent->value.data;
  208.     len = r->headers_in.user_agent->value.len;
  209.     last = ua + len;

  210.     if (cf->modern_browsers) {
  211.         modern = cf->modern_browsers->elts;

  212.         for (i = 0; i < cf->modern_browsers->nelts; i++) {
  213.             name = ua + modern[i].skip;

  214.             if (name >= last) {
  215.                 continue;
  216.             }

  217.             name = (u_char *) ngx_strstr(name, modern[i].name);

  218.             if (name == NULL) {
  219.                 continue;
  220.             }

  221.             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  222.                            "browser: \"%s\"", name);

  223.             name += modern[i].add;

  224.             if (name >= last) {
  225.                 continue;
  226.             }

  227.             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  228.                            "version: \"%ui\" \"%s\"", modern[i].version, name);

  229.             version = 0;
  230.             ver = 0;
  231.             scale = 1000000;

  232.             while (name < last) {

  233.                 c = *name++;

  234.                 if (c >= '0' && c <= '9') {
  235.                     ver = ver * 10 + (c - '0');
  236.                     continue;
  237.                 }

  238.                 if (c == '.') {
  239.                     version += ver * scale;

  240.                     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  241.                                    "version: \"%ui\" \"%ui\"",
  242.                                    modern[i].version, version);

  243.                     if (version > modern[i].version) {
  244.                         return NGX_HTTP_MODERN_BROWSER;
  245.                     }

  246.                     ver = 0;
  247.                     scale /= 100;
  248.                     continue;
  249.                 }

  250.                 break;
  251.             }

  252.             version += ver * scale;

  253.             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  254.                            "version: \"%ui\" \"%ui\"",
  255.                            modern[i].version, version);

  256.             if (version >= modern[i].version) {
  257.                 return NGX_HTTP_MODERN_BROWSER;
  258.             }

  259.             return NGX_HTTP_ANCIENT_BROWSER;
  260.         }

  261.         if (!cf->modern_unlisted_browsers) {
  262.             return NGX_HTTP_ANCIENT_BROWSER;
  263.         }
  264.     }

  265.     if (cf->netscape4) {
  266.         if (len > sizeof("Mozilla/4.72 ") - 1
  267.             && ngx_strncmp(ua, "Mozilla/", sizeof("Mozilla/") - 1) == 0
  268.             && ua[8] > '0' && ua[8] < '5')
  269.         {
  270.             return NGX_HTTP_ANCIENT_BROWSER;
  271.         }
  272.     }

  273.     if (cf->ancient_browsers) {
  274.         ancient = cf->ancient_browsers->elts;

  275.         for (i = 0; i < cf->ancient_browsers->nelts; i++) {
  276.             if (len >= ancient[i].len
  277.                 && ngx_strstr(ua, ancient[i].data) != NULL)
  278.             {
  279.                 return NGX_HTTP_ANCIENT_BROWSER;
  280.             }
  281.         }
  282.     }

  283.     if (cf->modern_unlisted_browsers) {
  284.         return NGX_HTTP_MODERN_BROWSER;
  285.     }

  286.     return NGX_HTTP_ANCIENT_BROWSER;
  287. }


  288. static ngx_int_t
  289. ngx_http_msie_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
  290.     uintptr_t data)
  291. {
  292.     if (r->headers_in.msie) {
  293.         *v = ngx_http_variable_true_value;
  294.         return NGX_OK;
  295.     }

  296.     *v = ngx_http_variable_null_value;
  297.     return NGX_OK;
  298. }


  299. static ngx_int_t
  300. ngx_http_browser_add_variables(ngx_conf_t *cf)
  301. {
  302.     ngx_http_variable_t  *var, *v;

  303.     for (v = ngx_http_browser_vars; v->name.len; v++) {

  304.         var = ngx_http_add_variable(cf, &v->name, v->flags);
  305.         if (var == NULL) {
  306.             return NGX_ERROR;
  307.         }

  308.         var->get_handler = v->get_handler;
  309.         var->data = v->data;
  310.     }

  311.     return NGX_OK;
  312. }


  313. static void *
  314. ngx_http_browser_create_conf(ngx_conf_t *cf)
  315. {
  316.     ngx_http_browser_conf_t  *conf;

  317.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_browser_conf_t));
  318.     if (conf == NULL) {
  319.         return NULL;
  320.     }

  321.     /*
  322.      * set by ngx_pcalloc():
  323.      *
  324.      *     conf->modern_browsers = NULL;
  325.      *     conf->ancient_browsers = NULL;
  326.      *     conf->modern_browser_value = NULL;
  327.      *     conf->ancient_browser_value = NULL;
  328.      *
  329.      *     conf->modern_unlisted_browsers = 0;
  330.      *     conf->netscape4 = 0;
  331.      */

  332.     return conf;
  333. }


  334. static char *
  335. ngx_http_browser_merge_conf(ngx_conf_t *cf, void *parent, void *child)
  336. {
  337.     ngx_http_browser_conf_t *prev = parent;
  338.     ngx_http_browser_conf_t *conf = child;

  339.     ngx_uint_t                  i, n;
  340.     ngx_http_modern_browser_t  *browsers, *opera;

  341.     /*
  342.      * At the merge the skip field is used to store the browser slot,
  343.      * it will be used in sorting and then will overwritten
  344.      * with a real skip value.  The zero value means Opera.
  345.      */

  346.     if (conf->modern_browsers == NULL && conf->modern_unlisted_browsers == 0) {
  347.         conf->modern_browsers = prev->modern_browsers;
  348.         conf->modern_unlisted_browsers = prev->modern_unlisted_browsers;

  349.     } else if (conf->modern_browsers != NULL) {
  350.         browsers = conf->modern_browsers->elts;

  351.         for (i = 0; i < conf->modern_browsers->nelts; i++) {
  352.             if (browsers[i].skip == 0) {
  353.                 goto found;
  354.             }
  355.         }

  356.         /*
  357.          * Opera may contain MSIE string, so if Opera was not enumerated
  358.          * as modern browsers, then add it and set a unreachable version
  359.          */

  360.         opera = ngx_array_push(conf->modern_browsers);
  361.         if (opera == NULL) {
  362.             return NGX_CONF_ERROR;
  363.         }

  364.         opera->skip = 0;
  365.         opera->version = 4001000000U;

  366.         browsers = conf->modern_browsers->elts;

  367. found:

  368.         ngx_qsort(browsers, (size_t) conf->modern_browsers->nelts,
  369.                   sizeof(ngx_http_modern_browser_t),
  370.                   ngx_http_modern_browser_sort);

  371.         for (i = 0; i < conf->modern_browsers->nelts; i++) {
  372.              n = browsers[i].skip;

  373.              browsers[i].skip = ngx_http_modern_browser_masks[n].skip;
  374.              browsers[i].add = ngx_http_modern_browser_masks[n].add;
  375.              (void) ngx_cpystrn(browsers[i].name,
  376.                                 ngx_http_modern_browser_masks[n].name, 12);
  377.         }
  378.     }

  379.     if (conf->ancient_browsers == NULL && conf->netscape4 == 0) {
  380.         conf->ancient_browsers = prev->ancient_browsers;
  381.         conf->netscape4 = prev->netscape4;
  382.     }

  383.     if (conf->modern_browser_value == NULL) {
  384.         conf->modern_browser_value = prev->modern_browser_value;
  385.     }

  386.     if (conf->modern_browser_value == NULL) {
  387.         conf->modern_browser_value = &ngx_http_variable_true_value;
  388.     }

  389.     if (conf->ancient_browser_value == NULL) {
  390.         conf->ancient_browser_value = prev->ancient_browser_value;
  391.     }

  392.     if (conf->ancient_browser_value == NULL) {
  393.         conf->ancient_browser_value = &ngx_http_variable_true_value;
  394.     }

  395.     return NGX_CONF_OK;
  396. }


  397. static int ngx_libc_cdecl
  398. ngx_http_modern_browser_sort(const void *one, const void *two)
  399. {
  400.     ngx_http_modern_browser_t *first = (ngx_http_modern_browser_t *) one;
  401.     ngx_http_modern_browser_t *second = (ngx_http_modern_browser_t *) two;

  402.     return (first->skip - second->skip);
  403. }


  404. static char *
  405. ngx_http_modern_browser(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  406. {
  407.     ngx_http_browser_conf_t *bcf = conf;

  408.     u_char                           c;
  409.     ngx_str_t                       *value;
  410.     ngx_uint_t                       i, n, version, ver, scale;
  411.     ngx_http_modern_browser_t       *browser;
  412.     ngx_http_modern_browser_mask_t  *mask;

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

  414.     if (cf->args->nelts == 2) {
  415.         if (ngx_strcmp(value[1].data, "unlisted") == 0) {
  416.             bcf->modern_unlisted_browsers = 1;
  417.             return NGX_CONF_OK;
  418.         }

  419.         return NGX_CONF_ERROR;
  420.     }

  421.     if (bcf->modern_browsers == NULL) {
  422.         bcf->modern_browsers = ngx_array_create(cf->pool, 5,
  423.                                             sizeof(ngx_http_modern_browser_t));
  424.         if (bcf->modern_browsers == NULL) {
  425.             return NGX_CONF_ERROR;
  426.         }
  427.     }

  428.     browser = ngx_array_push(bcf->modern_browsers);
  429.     if (browser == NULL) {
  430.         return NGX_CONF_ERROR;
  431.     }

  432.     mask = ngx_http_modern_browser_masks;

  433.     for (n = 0; mask[n].browser[0] != '\0'; n++) {
  434.         if (ngx_strcasecmp(mask[n].browser, value[1].data) == 0) {
  435.             goto found;
  436.         }
  437.     }

  438.     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  439.                        "unknown browser name \"%V\"", &value[1]);

  440.     return NGX_CONF_ERROR;

  441. found:

  442.     /*
  443.      * at this stage the skip field is used to store the browser slot,
  444.      * it will be used in sorting in merge stage and then will overwritten
  445.      * with a real value
  446.      */

  447.     browser->skip = n;

  448.     version = 0;
  449.     ver = 0;
  450.     scale = 1000000;

  451.     for (i = 0; i < value[2].len; i++) {

  452.         c = value[2].data[i];

  453.         if (c >= '0' && c <= '9') {
  454.             ver = ver * 10 + (c - '0');
  455.             continue;
  456.         }

  457.         if (c == '.') {
  458.             version += ver * scale;
  459.             ver = 0;
  460.             scale /= 100;
  461.             continue;
  462.         }

  463.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  464.                            "invalid browser version \"%V\"", &value[2]);

  465.         return NGX_CONF_ERROR;
  466.     }

  467.     version += ver * scale;

  468.     browser->version = version;

  469.     return NGX_CONF_OK;
  470. }


  471. static char *
  472. ngx_http_ancient_browser(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  473. {
  474.     ngx_http_browser_conf_t *bcf = conf;

  475.     ngx_str_t   *value, *browser;
  476.     ngx_uint_t   i;

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

  478.     for (i = 1; i < cf->args->nelts; i++) {
  479.         if (ngx_strcmp(value[i].data, "netscape4") == 0) {
  480.             bcf->netscape4 = 1;
  481.             continue;
  482.         }

  483.         if (bcf->ancient_browsers == NULL) {
  484.             bcf->ancient_browsers = ngx_array_create(cf->pool, 4,
  485.                                                      sizeof(ngx_str_t));
  486.             if (bcf->ancient_browsers == NULL) {
  487.                 return NGX_CONF_ERROR;
  488.             }
  489.         }

  490.         browser = ngx_array_push(bcf->ancient_browsers);
  491.         if (browser == NULL) {
  492.             return NGX_CONF_ERROR;
  493.         }

  494.         *browser = value[i];
  495.     }

  496.     return NGX_CONF_OK;
  497. }


  498. static char *
  499. ngx_http_modern_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  500. {
  501.     ngx_http_browser_conf_t *bcf = conf;

  502.     ngx_str_t  *value;

  503.     bcf->modern_browser_value = ngx_palloc(cf->pool,
  504.                                            sizeof(ngx_http_variable_value_t));
  505.     if (bcf->modern_browser_value == NULL) {
  506.         return NGX_CONF_ERROR;
  507.     }

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

  509.     bcf->modern_browser_value->len = value[1].len;
  510.     bcf->modern_browser_value->valid = 1;
  511.     bcf->modern_browser_value->no_cacheable = 0;
  512.     bcf->modern_browser_value->not_found = 0;
  513.     bcf->modern_browser_value->data = value[1].data;

  514.     return NGX_CONF_OK;
  515. }


  516. static char *
  517. ngx_http_ancient_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  518. {
  519.     ngx_http_browser_conf_t *bcf = conf;

  520.     ngx_str_t  *value;

  521.     bcf->ancient_browser_value = ngx_palloc(cf->pool,
  522.                                             sizeof(ngx_http_variable_value_t));
  523.     if (bcf->ancient_browser_value == NULL) {
  524.         return NGX_CONF_ERROR;
  525.     }

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

  527.     bcf->ancient_browser_value->len = value[1].len;
  528.     bcf->ancient_browser_value->valid = 1;
  529.     bcf->ancient_browser_value->no_cacheable = 0;
  530.     bcf->ancient_browser_value->not_found = 0;
  531.     bcf->ancient_browser_value->data = value[1].data;

  532.     return NGX_CONF_OK;
  533. }