src/http/modules/ngx_http_browser_module.c - nginx-1.7.10

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_str_t                   name;
  29.     ngx_http_get_variable_pt    handler;
  30.     uintptr_t                   data;
  31. } ngx_http_browser_variable_t;


  32. typedef struct {
  33.     ngx_array_t                *modern_browsers;
  34.     ngx_array_t                *ancient_browsers;
  35.     ngx_http_variable_value_t  *modern_browser_value;
  36.     ngx_http_variable_value_t  *ancient_browser_value;

  37.     unsigned                    modern_unlisted_browsers:1;
  38.     unsigned                    netscape4:1;
  39. } ngx_http_browser_conf_t;


  40. static ngx_int_t ngx_http_msie_variable(ngx_http_request_t *r,
  41.     ngx_http_variable_value_t *v, uintptr_t data);
  42. static ngx_int_t ngx_http_browser_variable(ngx_http_request_t *r,
  43.     ngx_http_variable_value_t *v, uintptr_t data);

  44. static ngx_uint_t ngx_http_browser(ngx_http_request_t *r,
  45.     ngx_http_browser_conf_t *cf);

  46. static ngx_int_t ngx_http_browser_add_variable(ngx_conf_t *cf);
  47. static void *ngx_http_browser_create_conf(ngx_conf_t *cf);
  48. static char *ngx_http_browser_merge_conf(ngx_conf_t *cf, void *parent,
  49.     void *child);
  50. static int ngx_libc_cdecl ngx_http_modern_browser_sort(const void *one,
  51.     const void *two);
  52. static char *ngx_http_modern_browser(ngx_conf_t *cf, ngx_command_t *cmd,
  53.     void *conf);
  54. static char *ngx_http_ancient_browser(ngx_conf_t *cf, ngx_command_t *cmd,
  55.     void *conf);
  56. static char *ngx_http_modern_browser_value(ngx_conf_t *cf, ngx_command_t *cmd,
  57.     void *conf);
  58. static char *ngx_http_ancient_browser_value(ngx_conf_t *cf, ngx_command_t *cmd,
  59.     void *conf);


  60. static ngx_command_t  ngx_http_browser_commands[] = {

  61.     { ngx_string("modern_browser"),
  62.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
  63.       ngx_http_modern_browser,
  64.       NGX_HTTP_LOC_CONF_OFFSET,
  65.       0,
  66.       NULL },

  67.     { ngx_string("ancient_browser"),
  68.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
  69.       ngx_http_ancient_browser,
  70.       NGX_HTTP_LOC_CONF_OFFSET,
  71.       0,
  72.       NULL },

  73.     { ngx_string("modern_browser_value"),
  74.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  75.       ngx_http_modern_browser_value,
  76.       NGX_HTTP_LOC_CONF_OFFSET,
  77.       0,
  78.       NULL },

  79.     { ngx_string("ancient_browser_value"),
  80.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  81.       ngx_http_ancient_browser_value,
  82.       NGX_HTTP_LOC_CONF_OFFSET,
  83.       0,
  84.       NULL },

  85.       ngx_null_command
  86. };


  87. static ngx_http_module_t  ngx_http_browser_module_ctx = {
  88.     ngx_http_browser_add_variable,         /* preconfiguration */
  89.     NULL,                                  /* postconfiguration */

  90.     NULL,                                  /* create main configuration */
  91.     NULL,                                  /* init main configuration */

  92.     NULL,                                  /* create server configuration */
  93.     NULL,                                  /* merge server configuration */

  94.     ngx_http_browser_create_conf,          /* create location configuration */
  95.     ngx_http_browser_merge_conf            /* merge location configuration */
  96. };


  97. ngx_module_t  ngx_http_browser_module = {
  98.     NGX_MODULE_V1,
  99.     &ngx_http_browser_module_ctx,          /* module context */
  100.     ngx_http_browser_commands,             /* module directives */
  101.     NGX_HTTP_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_http_modern_browser_mask_t  ngx_http_modern_browser_masks[] = {

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

  113.     /*
  114.      * "Opera/7.50 (X11; FreeBSD i386; U)  [en]"
  115.      * "Mozilla/5.0 (X11; FreeBSD i386; U) Opera 7.50  [en]"
  116.      * "Mozilla/4.0 (compatible; MSIE 6.0; X11; FreeBSD i386) Opera 7.50  [en]"
  117.      * "Opera/8.0 (Windows NT 5.1; U; ru)"
  118.      * "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; en) Opera 8.0"
  119.      * "Opera/9.01 (X11; FreeBSD 6 i386; U; en)"
  120.      */

  121.     { "opera",
  122.       0,
  123.       sizeof("Opera ") - 1,
  124.       "Opera"},

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

  126.     { "msie",
  127.       sizeof("Mozilla/4.0 (compatible; ") - 1,
  128.       sizeof("MSIE ") - 1,
  129.       "MSIE "},

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

  140.     { "gecko",
  141.       sizeof("Mozilla/5.0 (") - 1,
  142.       sizeof("rv:") - 1,
  143.       "rv:"},

  144.     /*
  145.      * "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ru-ru) AppleWebKit/125.2
  146.      *              (KHTML, like Gecko) Safari/125.7"
  147.      * "Mozilla/5.0 (SymbianOS/9.1; U; en-us) AppleWebKit/413
  148.      *              (KHTML, like Gecko) Safari/413"
  149.      * "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/418
  150.      *              (KHTML, like Gecko) Safari/417.9.3"
  151.      * "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ru-ru) AppleWebKit/418.8
  152.      *              (KHTML, like Gecko) Safari/419.3"
  153.      */

  154.     { "safari",
  155.       sizeof("Mozilla/5.0 (") - 1,
  156.       sizeof("Safari/") - 1,
  157.       "Safari/"},

  158.     /*
  159.      * "Mozilla/5.0 (compatible; Konqueror/3.1; Linux)"
  160.      * "Mozilla/5.0 (compatible; Konqueror/3.4; Linux) KHTML/3.4.2 (like Gecko)"
  161.      * "Mozilla/5.0 (compatible; Konqueror/3.5; FreeBSD) KHTML/3.5.1
  162.      *              (like Gecko)"
  163.      */

  164.     { "konqueror",
  165.       sizeof("Mozilla/5.0 (compatible; ") - 1,
  166.       sizeof("Konqueror/") - 1,
  167.       "Konqueror/"},

  168.     { "", 0, 0, "" }

  169. };


  170. static ngx_http_browser_variable_t  ngx_http_browsers[] = {
  171.     { ngx_string("msie"), ngx_http_msie_variable, 0 },
  172.     { ngx_string("modern_browser"), ngx_http_browser_variable,
  173.           NGX_HTTP_MODERN_BROWSER },
  174.     { ngx_string("ancient_browser"), ngx_http_browser_variable,
  175.           NGX_HTTP_ANCIENT_BROWSER },
  176.     { ngx_null_string, NULL, 0 }
  177. };


  178. static ngx_int_t
  179. ngx_http_browser_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
  180.     uintptr_t data)
  181. {
  182.     ngx_uint_t                rc;
  183.     ngx_http_browser_conf_t  *cf;

  184.     cf = ngx_http_get_module_loc_conf(r, ngx_http_browser_module);

  185.     rc = ngx_http_browser(r, cf);

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

  190.     if (data == NGX_HTTP_ANCIENT_BROWSER && rc == NGX_HTTP_ANCIENT_BROWSER) {
  191.         *v = *cf->ancient_browser_value;
  192.         return NGX_OK;
  193.     }

  194.     *v = ngx_http_variable_null_value;
  195.     return NGX_OK;
  196. }


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

  205.     if (r->headers_in.user_agent == NULL) {
  206.         if (cf->modern_unlisted_browsers) {
  207.             return NGX_HTTP_MODERN_BROWSER;
  208.         }

  209.         return NGX_HTTP_ANCIENT_BROWSER;
  210.     }

  211.     ua = r->headers_in.user_agent->value.data;
  212.     len = r->headers_in.user_agent->value.len;
  213.     last = ua + len;

  214.     if (cf->modern_browsers) {
  215.         modern = cf->modern_browsers->elts;

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

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

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

  222.             if (name == NULL) {
  223.                 continue;
  224.             }

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

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

  228.             if (name >= last) {
  229.                 continue;
  230.             }

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

  233.             version = 0;
  234.             ver = 0;
  235.             scale = 1000000;

  236.             while (name < last) {

  237.                 c = *name++;

  238.                 if (c >= '0' && c <= '9') {
  239.                     ver = ver * 10 + (c - '0');
  240.                     continue;
  241.                 }

  242.                 if (c == '.') {
  243.                     version += ver * scale;

  244.                     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  245.                                    "version: \"%ui\" \"%ui\"",
  246.                                    modern[i].version, version);

  247.                     if (version > modern[i].version) {
  248.                         return NGX_HTTP_MODERN_BROWSER;
  249.                     }

  250.                     ver = 0;
  251.                     scale /= 100;
  252.                     continue;
  253.                 }

  254.                 break;
  255.             }

  256.             version += ver * scale;

  257.             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  258.                            "version: \"%ui\" \"%ui\"",
  259.                            modern[i].version, version);

  260.             if (version >= modern[i].version) {
  261.                 return NGX_HTTP_MODERN_BROWSER;
  262.             }

  263.             return NGX_HTTP_ANCIENT_BROWSER;
  264.         }

  265.         if (!cf->modern_unlisted_browsers) {
  266.             return NGX_HTTP_ANCIENT_BROWSER;
  267.         }
  268.     }

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

  277.     if (cf->ancient_browsers) {
  278.         ancient = cf->ancient_browsers->elts;

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

  287.     if (cf->modern_unlisted_browsers) {
  288.         return NGX_HTTP_MODERN_BROWSER;
  289.     }

  290.     return NGX_HTTP_ANCIENT_BROWSER;
  291. }


  292. static ngx_int_t
  293. ngx_http_msie_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
  294.     uintptr_t data)
  295. {
  296.     if (r->headers_in.msie) {
  297.         *v = ngx_http_variable_true_value;
  298.         return NGX_OK;
  299.     }

  300.     *v = ngx_http_variable_null_value;
  301.     return NGX_OK;
  302. }


  303. static ngx_int_t
  304. ngx_http_browser_add_variable(ngx_conf_t *cf)
  305. {
  306.     ngx_http_browser_variable_t   *var;
  307.     ngx_http_variable_t           *v;

  308.     for (var = ngx_http_browsers; var->name.len; var++) {

  309.         v = ngx_http_add_variable(cf, &var->name, NGX_HTTP_VAR_CHANGEABLE);
  310.         if (v == NULL) {
  311.             return NGX_ERROR;
  312.         }

  313.         v->get_handler = var->handler;
  314.         v->data = var->data;
  315.     }

  316.     return NGX_OK;
  317. }


  318. static void *
  319. ngx_http_browser_create_conf(ngx_conf_t *cf)
  320. {
  321.     ngx_http_browser_conf_t  *conf;

  322.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_browser_conf_t));
  323.     if (conf == NULL) {
  324.         return NULL;
  325.     }

  326.     /*
  327.      * set by ngx_pcalloc():
  328.      *
  329.      *     conf->modern_browsers = NULL;
  330.      *     conf->ancient_browsers = NULL;
  331.      *     conf->modern_browser_value = NULL;
  332.      *     conf->ancient_browser_value = NULL;
  333.      *
  334.      *     conf->modern_unlisted_browsers = 0;
  335.      *     conf->netscape4 = 0;
  336.      */

  337.     return conf;
  338. }


  339. static char *
  340. ngx_http_browser_merge_conf(ngx_conf_t *cf, void *parent, void *child)
  341. {
  342.     ngx_http_browser_conf_t *prev = parent;
  343.     ngx_http_browser_conf_t *conf = child;

  344.     ngx_uint_t                  i, n;
  345.     ngx_http_modern_browser_t  *browsers, *opera;

  346.     /*
  347.      * At the merge the skip field is used to store the browser slot,
  348.      * it will be used in sorting and then will overwritten
  349.      * with a real skip value.  The zero value means Opera.
  350.      */

  351.     if (conf->modern_browsers == NULL && conf->modern_unlisted_browsers == 0) {
  352.         conf->modern_browsers = prev->modern_browsers;
  353.         conf->modern_unlisted_browsers = prev->modern_unlisted_browsers;

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

  356.         for (i = 0; i < conf->modern_browsers->nelts; i++) {
  357.             if (browsers[i].skip == 0) {
  358.                 goto found;
  359.             }
  360.         }

  361.         /*
  362.          * Opera may contain MSIE string, so if Opera was not enumerated
  363.          * as modern browsers, then add it and set a unreachable version
  364.          */

  365.         opera = ngx_array_push(conf->modern_browsers);
  366.         if (opera == NULL) {
  367.             return NGX_CONF_ERROR;
  368.         }

  369.         opera->skip = 0;
  370.         opera->version = 4001000000U;

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

  372. found:

  373.         ngx_qsort(browsers, (size_t) conf->modern_browsers->nelts,
  374.                   sizeof(ngx_http_modern_browser_t),
  375.                   ngx_http_modern_browser_sort);

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

  378.              browsers[i].skip = ngx_http_modern_browser_masks[n].skip;
  379.              browsers[i].add = ngx_http_modern_browser_masks[n].add;
  380.              (void) ngx_cpystrn(browsers[i].name,
  381.                                 ngx_http_modern_browser_masks[n].name, 12);
  382.         }
  383.     }

  384.     if (conf->ancient_browsers == NULL && conf->netscape4 == 0) {
  385.         conf->ancient_browsers = prev->ancient_browsers;
  386.         conf->netscape4 = prev->netscape4;
  387.     }

  388.     if (conf->modern_browser_value == NULL) {
  389.         conf->modern_browser_value = prev->modern_browser_value;
  390.     }

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

  394.     if (conf->ancient_browser_value == NULL) {
  395.         conf->ancient_browser_value = prev->ancient_browser_value;
  396.     }

  397.     if (conf->ancient_browser_value == NULL) {
  398.         conf->ancient_browser_value = &ngx_http_variable_true_value;
  399.     }

  400.     return NGX_CONF_OK;
  401. }


  402. static int ngx_libc_cdecl
  403. ngx_http_modern_browser_sort(const void *one, const void *two)
  404. {
  405.     ngx_http_modern_browser_t *first = (ngx_http_modern_browser_t *) one;
  406.     ngx_http_modern_browser_t *second = (ngx_http_modern_browser_t *) two;

  407.     return (first->skip - second->skip);
  408. }


  409. static char *
  410. ngx_http_modern_browser(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  411. {
  412.     ngx_http_browser_conf_t *bcf = conf;

  413.     u_char                           c;
  414.     ngx_str_t                       *value;
  415.     ngx_uint_t                       i, n, version, ver, scale;
  416.     ngx_http_modern_browser_t       *browser;
  417.     ngx_http_modern_browser_mask_t  *mask;

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

  419.     if (cf->args->nelts == 2) {
  420.         if (ngx_strcmp(value[1].data, "unlisted") == 0) {
  421.             bcf->modern_unlisted_browsers = 1;
  422.             return NGX_CONF_OK;
  423.         }

  424.         return NGX_CONF_ERROR;
  425.     }

  426.     if (bcf->modern_browsers == NULL) {
  427.         bcf->modern_browsers = ngx_array_create(cf->pool, 5,
  428.                                             sizeof(ngx_http_modern_browser_t));
  429.         if (bcf->modern_browsers == NULL) {
  430.             return NGX_CONF_ERROR;
  431.         }
  432.     }

  433.     browser = ngx_array_push(bcf->modern_browsers);
  434.     if (browser == NULL) {
  435.         return NGX_CONF_ERROR;
  436.     }

  437.     mask = ngx_http_modern_browser_masks;

  438.     for (n = 0; mask[n].browser[0] != '\0'; n++) {
  439.         if (ngx_strcasecmp(mask[n].browser, value[1].data) == 0) {
  440.             goto found;
  441.         }
  442.     }

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

  445.     return NGX_CONF_ERROR;

  446. found:

  447.     /*
  448.      * at this stage the skip field is used to store the browser slot,
  449.      * it will be used in sorting in merge stage and then will overwritten
  450.      * with a real value
  451.      */

  452.     browser->skip = n;

  453.     version = 0;
  454.     ver = 0;
  455.     scale = 1000000;

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

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

  458.         if (c >= '0' && c <= '9') {
  459.             ver = ver * 10 + (c - '0');
  460.             continue;
  461.         }

  462.         if (c == '.') {
  463.             version += ver * scale;
  464.             ver = 0;
  465.             scale /= 100;
  466.             continue;
  467.         }

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

  470.         return NGX_CONF_ERROR;
  471.     }

  472.     version += ver * scale;

  473.     browser->version = version;

  474.     return NGX_CONF_OK;
  475. }


  476. static char *
  477. ngx_http_ancient_browser(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  478. {
  479.     ngx_http_browser_conf_t *bcf = conf;

  480.     ngx_str_t   *value, *browser;
  481.     ngx_uint_t   i;

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

  483.     for (i = 1; i < cf->args->nelts; i++) {
  484.         if (ngx_strcmp(value[i].data, "netscape4") == 0) {
  485.             bcf->netscape4 = 1;
  486.             continue;
  487.         }

  488.         if (bcf->ancient_browsers == NULL) {
  489.             bcf->ancient_browsers = ngx_array_create(cf->pool, 4,
  490.                                                      sizeof(ngx_str_t));
  491.             if (bcf->ancient_browsers == NULL) {
  492.                 return NGX_CONF_ERROR;
  493.             }
  494.         }

  495.         browser = ngx_array_push(bcf->ancient_browsers);
  496.         if (browser == NULL) {
  497.             return NGX_CONF_ERROR;
  498.         }

  499.         *browser = value[i];
  500.     }

  501.     return NGX_CONF_OK;
  502. }


  503. static char *
  504. ngx_http_modern_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  505. {
  506.     ngx_http_browser_conf_t *bcf = conf;

  507.     ngx_str_t  *value;

  508.     bcf->modern_browser_value = ngx_palloc(cf->pool,
  509.                                            sizeof(ngx_http_variable_value_t));
  510.     if (bcf->modern_browser_value == NULL) {
  511.         return NGX_CONF_ERROR;
  512.     }

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

  514.     bcf->modern_browser_value->len = value[1].len;
  515.     bcf->modern_browser_value->valid = 1;
  516.     bcf->modern_browser_value->no_cacheable = 0;
  517.     bcf->modern_browser_value->not_found = 0;
  518.     bcf->modern_browser_value->data = value[1].data;

  519.     return NGX_CONF_OK;
  520. }


  521. static char *
  522. ngx_http_ancient_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  523. {
  524.     ngx_http_browser_conf_t *bcf = conf;

  525.     ngx_str_t  *value;

  526.     bcf->ancient_browser_value = ngx_palloc(cf->pool,
  527.                                             sizeof(ngx_http_variable_value_t));
  528.     if (bcf->ancient_browser_value == NULL) {
  529.         return NGX_CONF_ERROR;
  530.     }

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

  532.     bcf->ancient_browser_value->len = value[1].len;
  533.     bcf->ancient_browser_value->valid = 1;
  534.     bcf->ancient_browser_value->no_cacheable = 0;
  535.     bcf->ancient_browser_value->not_found = 0;
  536.     bcf->ancient_browser_value->data = value[1].data;

  537.     return NGX_CONF_OK;
  538. }