1
2/*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
5 */
6
7
8#include <ngx_config.h>
9#include <ngx_core.h>
10#include <ngx_http.h>
11
12
13#define NGX_HTTP_USERID_OFF   0
14#define NGX_HTTP_USERID_LOG   1
15#define NGX_HTTP_USERID_V1    2
16#define NGX_HTTP_USERID_ON    3
17
18/* 31 Dec 2037 23:55:55 GMT */
19#define NGX_HTTP_USERID_MAX_EXPIRES  2145916555
20
21
22typedef struct {
23    ngx_uint_t  enable;
24
25    ngx_int_t   service;
26
27    ngx_str_t   name;
28    ngx_str_t   domain;
29    ngx_str_t   path;
30    ngx_str_t   p3p;
31
32    time_t      expires;
33
34    u_char      mark;
35} ngx_http_userid_conf_t;
36
37
38typedef struct {
39    uint32_t    uid_got[4];
40    uint32_t    uid_set[4];
41    ngx_str_t   cookie;
42    ngx_uint_t  reset;
43} ngx_http_userid_ctx_t;
44
45
46static ngx_http_userid_ctx_t *ngx_http_userid_get_uid(ngx_http_request_t *r,
47    ngx_http_userid_conf_t *conf);
48static ngx_int_t ngx_http_userid_variable(ngx_http_request_t *r,
49    ngx_http_variable_value_t *v, ngx_str_t *name, uint32_t *uid);
50static ngx_int_t ngx_http_userid_set_uid(ngx_http_request_t *r,
51    ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf);
52static ngx_int_t ngx_http_userid_create_uid(ngx_http_request_t *r,
53    ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf);
54
55static ngx_int_t ngx_http_userid_add_variables(ngx_conf_t *cf);
56static ngx_int_t ngx_http_userid_init(ngx_conf_t *cf);
57static void *ngx_http_userid_create_conf(ngx_conf_t *cf);
58static char *ngx_http_userid_merge_conf(ngx_conf_t *cf, void *parent,
59    void *child);
60static char *ngx_http_userid_domain(ngx_conf_t *cf, void *post, void *data);
61static char *ngx_http_userid_path(ngx_conf_t *cf, void *post, void *data);
62static char *ngx_http_userid_expires(ngx_conf_t *cf, ngx_command_t *cmd,
63    void *conf);
64static char *ngx_http_userid_p3p(ngx_conf_t *cf, void *post, void *data);
65static char *ngx_http_userid_mark(ngx_conf_t *cf, ngx_command_t *cmd,
66    void *conf);
67static ngx_int_t ngx_http_userid_init_worker(ngx_cycle_t *cycle);
68
69
70
71static uint32_t  start_value;
72static uint32_t  sequencer_v1 = 1;
73static uint32_t  sequencer_v2 = 0x03030302;
74
75
76static u_char expires[] = "; expires=Thu, 31-Dec-37 23:55:55 GMT";
77
78
79static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
80
81
82static ngx_conf_enum_t  ngx_http_userid_state[] = {
83    { ngx_string("off"), NGX_HTTP_USERID_OFF },
84    { ngx_string("log"), NGX_HTTP_USERID_LOG },
85    { ngx_string("v1"), NGX_HTTP_USERID_V1 },
86    { ngx_string("on"), NGX_HTTP_USERID_ON },
87    { ngx_null_string, 0 }
88};
89
90
91static ngx_conf_post_handler_pt  ngx_http_userid_domain_p =
92    ngx_http_userid_domain;
93static ngx_conf_post_handler_pt  ngx_http_userid_path_p = ngx_http_userid_path;
94static ngx_conf_post_handler_pt  ngx_http_userid_p3p_p = ngx_http_userid_p3p;
95
96
97static ngx_command_t  ngx_http_userid_commands[] = {
98
99    { ngx_string("userid"),
100      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
101      ngx_conf_set_enum_slot,
102      NGX_HTTP_LOC_CONF_OFFSET,
103      offsetof(ngx_http_userid_conf_t, enable),
104      ngx_http_userid_state },
105
106    { ngx_string("userid_service"),
107      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
108      ngx_conf_set_num_slot,
109      NGX_HTTP_LOC_CONF_OFFSET,
110      offsetof(ngx_http_userid_conf_t, service),
111      NULL },
112
113    { ngx_string("userid_name"),
114      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
115      ngx_conf_set_str_slot,
116      NGX_HTTP_LOC_CONF_OFFSET,
117      offsetof(ngx_http_userid_conf_t, name),
118      NULL },
119
120    { ngx_string("userid_domain"),
121      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
122      ngx_conf_set_str_slot,
123      NGX_HTTP_LOC_CONF_OFFSET,
124      offsetof(ngx_http_userid_conf_t, domain),
125      &ngx_http_userid_domain_p },
126
127    { ngx_string("userid_path"),
128      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
129      ngx_conf_set_str_slot,
130      NGX_HTTP_LOC_CONF_OFFSET,
131      offsetof(ngx_http_userid_conf_t, path),
132      &ngx_http_userid_path_p },
133
134    { ngx_string("userid_expires"),
135      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
136      ngx_http_userid_expires,
137      NGX_HTTP_LOC_CONF_OFFSET,
138      0,
139      NULL },
140
141    { ngx_string("userid_p3p"),
142      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
143      ngx_conf_set_str_slot,
144      NGX_HTTP_LOC_CONF_OFFSET,
145      offsetof(ngx_http_userid_conf_t, p3p),
146      &ngx_http_userid_p3p_p },
147
148    { ngx_string("userid_mark"),
149      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
150      ngx_http_userid_mark,
151      NGX_HTTP_LOC_CONF_OFFSET,
152      0,
153      NULL },
154
155      ngx_null_command
156};
157
158
159static ngx_http_module_t  ngx_http_userid_filter_module_ctx = {
160    ngx_http_userid_add_variables,         /* preconfiguration */
161    ngx_http_userid_init,                  /* postconfiguration */
162
163    NULL,                                  /* create main configuration */
164    NULL,                                  /* init main configuration */
165
166    NULL,                                  /* create server configuration */
167    NULL,                                  /* merge server configuration */
168
169    ngx_http_userid_create_conf,           /* create location configuration */
170    ngx_http_userid_merge_conf             /* merge location configuration */
171};
172
173
174ngx_module_t  ngx_http_userid_filter_module = {
175    NGX_MODULE_V1,
176    &ngx_http_userid_filter_module_ctx,    /* module context */
177    ngx_http_userid_commands,              /* module directives */
178    NGX_HTTP_MODULE,                       /* module type */
179    NULL,                                  /* init master */
180    NULL,                                  /* init module */
181    ngx_http_userid_init_worker,           /* init process */
182    NULL,                                  /* init thread */
183    NULL,                                  /* exit thread */
184    NULL,                                  /* exit process */
185    NULL,                                  /* exit master */
186    NGX_MODULE_V1_PADDING
187};
188
189
190static ngx_str_t   ngx_http_userid_got = ngx_string("uid_got");
191static ngx_str_t   ngx_http_userid_set = ngx_string("uid_set");
192static ngx_str_t   ngx_http_userid_reset = ngx_string("uid_reset");
193static ngx_uint_t  ngx_http_userid_reset_index;
194
195
196static ngx_int_t
197ngx_http_userid_filter(ngx_http_request_t *r)
198{
199    ngx_http_userid_ctx_t   *ctx;
200    ngx_http_userid_conf_t  *conf;
201
202    if (r != r->main) {
203        return ngx_http_next_header_filter(r);
204    }
205
206    conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_module);
207
208    if (conf->enable < NGX_HTTP_USERID_V1) {
209        return ngx_http_next_header_filter(r);
210    }
211
212    ctx = ngx_http_userid_get_uid(r, conf);
213
214    if (ctx == NULL) {
215        return NGX_ERROR;
216    }
217
218    if (ngx_http_userid_set_uid(r, ctx, conf) == NGX_OK) {
219        return ngx_http_next_header_filter(r);
220    }
221
222    return NGX_ERROR;
223}
224
225
226static ngx_int_t
227ngx_http_userid_got_variable(ngx_http_request_t *r,
228    ngx_http_variable_value_t *v, uintptr_t data)
229{
230    ngx_http_userid_ctx_t   *ctx;
231    ngx_http_userid_conf_t  *conf;
232
233    conf = ngx_http_get_module_loc_conf(r->main, ngx_http_userid_filter_module);
234
235    if (conf->enable == NGX_HTTP_USERID_OFF) {
236        v->not_found = 1;
237        return NGX_OK;
238    }
239
240    ctx = ngx_http_userid_get_uid(r->main, conf);
241
242    if (ctx == NULL) {
243        return NGX_ERROR;
244    }
245
246    if (ctx->uid_got[3] != 0) {
247        return ngx_http_userid_variable(r->main, v, &conf->name, ctx->uid_got);
248    }
249
250    v->not_found = 1;
251
252    return NGX_OK;
253}
254
255
256static ngx_int_t
257ngx_http_userid_set_variable(ngx_http_request_t *r,
258    ngx_http_variable_value_t *v, uintptr_t data)
259{
260    ngx_http_userid_ctx_t   *ctx;
261    ngx_http_userid_conf_t  *conf;
262
263    conf = ngx_http_get_module_loc_conf(r->main, ngx_http_userid_filter_module);
264
265    if (conf->enable < NGX_HTTP_USERID_V1) {
266        v->not_found = 1;
267        return NGX_OK;
268    }
269
270    ctx = ngx_http_userid_get_uid(r->main, conf);
271
272    if (ctx == NULL) {
273        return NGX_ERROR;
274    }
275
276    if (ngx_http_userid_create_uid(r->main, ctx, conf) != NGX_OK) {
277        return NGX_ERROR;
278    }
279
280    if (ctx->uid_set[3] == 0) {
281        v->not_found = 1;
282        return NGX_OK;
283    }
284
285    return ngx_http_userid_variable(r->main, v, &conf->name, ctx->uid_set);
286}
287
288
289static ngx_http_userid_ctx_t *
290ngx_http_userid_get_uid(ngx_http_request_t *r, ngx_http_userid_conf_t *conf)
291{
292    ngx_int_t                n;
293    ngx_str_t                src, dst;
294    ngx_table_elt_t        **cookies;
295    ngx_http_userid_ctx_t   *ctx;
296
297    ctx = ngx_http_get_module_ctx(r, ngx_http_userid_filter_module);
298
299    if (ctx) {
300        return ctx;
301    }
302
303    if (ctx == NULL) {
304        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_userid_ctx_t));
305        if (ctx == NULL) {
306            return NULL;
307        }
308
309        ngx_http_set_ctx(r, ctx, ngx_http_userid_filter_module);
310    }
311
312    n = ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &conf->name,
313                                          &ctx->cookie);
314    if (n == NGX_DECLINED) {
315        return ctx;
316    }
317
318    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
319                   "uid cookie: \"%V\"", &ctx->cookie);
320
321    if (ctx->cookie.len < 22) {
322        cookies = r->headers_in.cookies.elts;
323        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
324                      "client sent too short userid cookie \"%V\"",
325                      &cookies[n]->value);
326        return ctx;
327    }
328
329    src = ctx->cookie;
330
331    /*
332     * we have to limit the encoded string to 22 characters because
333     *  1) cookie may be marked by "userid_mark",
334     *  2) and there are already the millions cookies with a garbage
335     *     instead of the correct base64 trail "=="
336     */
337
338    src.len = 22;
339
340    dst.data = (u_char *) ctx->uid_got;
341
342    if (ngx_decode_base64(&dst, &src) == NGX_ERROR) {
343        cookies = r->headers_in.cookies.elts;
344        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
345                      "client sent invalid userid cookie \"%V\"",
346                      &cookies[n]->value);
347        return ctx;
348    }
349
350    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
351                   "uid: %08XD%08XD%08XD%08XD",
352                   ctx->uid_got[0], ctx->uid_got[1],
353                   ctx->uid_got[2], ctx->uid_got[3]);
354
355    return ctx;
356}
357
358
359static ngx_int_t
360ngx_http_userid_set_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx,
361    ngx_http_userid_conf_t *conf)
362{
363    u_char           *cookie, *p;
364    size_t            len;
365    ngx_str_t         src, dst;
366    ngx_table_elt_t  *set_cookie, *p3p;
367
368    if (ngx_http_userid_create_uid(r, ctx, conf) != NGX_OK) {
369        return NGX_ERROR;
370    }
371
372    if (ctx->uid_set[3] == 0) {
373        return NGX_OK;
374    }
375
376    len = conf->name.len + 1 + ngx_base64_encoded_length(16) + conf->path.len;
377
378    if (conf->expires) {
379        len += sizeof(expires) - 1 + 2;
380    }
381
382    if (conf->domain.len) {
383        len += conf->domain.len;
384    }
385
386    cookie = ngx_pnalloc(r->pool, len);
387    if (cookie == NULL) {
388        return NGX_ERROR;
389    }
390
391    p = ngx_copy(cookie, conf->name.data, conf->name.len);
392    *p++ = '=';
393
394    if (ctx->uid_got[3] == 0 || ctx->reset) {
395        src.len = 16;
396        src.data = (u_char *) ctx->uid_set;
397        dst.data = p;
398
399        ngx_encode_base64(&dst, &src);
400
401        p += dst.len;
402
403        if (conf->mark) {
404            *(p - 2) = conf->mark;
405        }
406
407    } else {
408        p = ngx_cpymem(p, ctx->cookie.data, 22);
409        *p++ = conf->mark;
410        *p++ = '=';
411    }
412
413    if (conf->expires == NGX_HTTP_USERID_MAX_EXPIRES) {
414        p = ngx_cpymem(p, expires, sizeof(expires) - 1);
415
416    } else if (conf->expires) {
417        p = ngx_cpymem(p, expires, sizeof("; expires=") - 1);
418        p = ngx_http_cookie_time(p, ngx_time() + conf->expires);
419    }
420
421    p = ngx_copy(p, conf->domain.data, conf->domain.len);
422
423    p = ngx_copy(p, conf->path.data, conf->path.len);
424
425    set_cookie = ngx_list_push(&r->headers_out.headers);
426    if (set_cookie == NULL) {
427        return NGX_ERROR;
428    }
429
430    set_cookie->hash = 1;
431    ngx_str_set(&set_cookie->key, "Set-Cookie");
432    set_cookie->value.len = p - cookie;
433    set_cookie->value.data = cookie;
434
435    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
436                   "uid cookie: \"%V\"", &set_cookie->value);
437
438    if (conf->p3p.len == 0) {
439        return NGX_OK;
440    }
441
442    p3p = ngx_list_push(&r->headers_out.headers);
443    if (p3p == NULL) {
444        return NGX_ERROR;
445    }
446
447    p3p->hash = 1;
448    ngx_str_set(&p3p->key, "P3P");
449    p3p->value = conf->p3p;
450
451    return NGX_OK;
452}
453
454
455static ngx_int_t
456ngx_http_userid_create_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx,
457    ngx_http_userid_conf_t *conf)
458{
459    ngx_connection_t           *c;
460    struct sockaddr_in         *sin;
461    ngx_http_variable_value_t  *vv;
462#if (NGX_HAVE_INET6)
463    u_char                     *p;
464    struct sockaddr_in6        *sin6;
465#endif
466
467    if (ctx->uid_set[3] != 0) {
468        return NGX_OK;
469    }
470
471    if (ctx->uid_got[3] != 0) {
472
473        vv = ngx_http_get_indexed_variable(r, ngx_http_userid_reset_index);
474
475        if (vv->len == 0 || (vv->len == 1 && vv->data[0] == '0')) {
476
477            if (conf->mark == '\0'
478                || (ctx->cookie.len > 23
479                    && ctx->cookie.data[22] == conf->mark
480                    && ctx->cookie.data[23] == '='))
481            {
482                return NGX_OK;
483            }
484
485            ctx->uid_set[0] = ctx->uid_got[0];
486            ctx->uid_set[1] = ctx->uid_got[1];
487            ctx->uid_set[2] = ctx->uid_got[2];
488            ctx->uid_set[3] = ctx->uid_got[3];
489
490            return NGX_OK;
491
492        } else {
493            ctx->reset = 1;
494
495            if (vv->len == 3 && ngx_strncmp(vv->data, "log", 3) == 0) {
496                ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
497                        "userid cookie \"%V=%08XD%08XD%08XD%08XD\" was reset",
498                        &conf->name, ctx->uid_got[0], ctx->uid_got[1],
499                        ctx->uid_got[2], ctx->uid_got[3]);
500            }
501        }
502    }
503
504    /*
505     * TODO: in the threaded mode the sequencers should be in TLS and their
506     * ranges should be divided between threads
507     */
508
509    if (conf->enable == NGX_HTTP_USERID_V1) {
510        if (conf->service == NGX_CONF_UNSET) {
511            ctx->uid_set[0] = 0;
512        } else {
513            ctx->uid_set[0] = conf->service;
514        }
515        ctx->uid_set[1] = (uint32_t) ngx_time();
516        ctx->uid_set[2] = start_value;
517        ctx->uid_set[3] = sequencer_v1;
518        sequencer_v1 += 0x100;
519
520    } else {
521        if (conf->service == NGX_CONF_UNSET) {
522
523            c = r->connection;
524
525            if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
526                return NGX_ERROR;
527            }
528
529            switch (c->local_sockaddr->sa_family) {
530
531#if (NGX_HAVE_INET6)
532            case AF_INET6:
533                sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
534
535                p = (u_char *) &ctx->uid_set[0];
536
537                *p++ = sin6->sin6_addr.s6_addr[12];
538                *p++ = sin6->sin6_addr.s6_addr[13];
539                *p++ = sin6->sin6_addr.s6_addr[14];
540                *p = sin6->sin6_addr.s6_addr[15];
541
542                break;
543#endif
544            default: /* AF_INET */
545                sin = (struct sockaddr_in *) c->local_sockaddr;
546                ctx->uid_set[0] = sin->sin_addr.s_addr;
547                break;
548            }
549
550        } else {
551            ctx->uid_set[0] = htonl(conf->service);
552        }
553
554        ctx->uid_set[1] = htonl((uint32_t) ngx_time());
555        ctx->uid_set[2] = htonl(start_value);
556        ctx->uid_set[3] = htonl(sequencer_v2);
557        sequencer_v2 += 0x100;
558        if (sequencer_v2 < 0x03030302) {
559            sequencer_v2 = 0x03030302;
560        }
561    }
562
563    return NGX_OK;
564}
565
566
567static ngx_int_t
568ngx_http_userid_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
569    ngx_str_t *name, uint32_t *uid)
570{
571    v->len = name->len + sizeof("=00001111222233334444555566667777") - 1;
572    v->data = ngx_pnalloc(r->pool, v->len);
573    if (v->data == NULL) {
574        return NGX_ERROR;
575    }
576
577    v->valid = 1;
578    v->no_cacheable = 0;
579    v->not_found = 0;
580
581    ngx_sprintf(v->data, "%V=%08XD%08XD%08XD%08XD",
582                name, uid[0], uid[1], uid[2], uid[3]);
583
584    return NGX_OK;
585}
586
587
588static ngx_int_t
589ngx_http_userid_reset_variable(ngx_http_request_t *r,
590    ngx_http_variable_value_t *v, uintptr_t data)
591{
592    *v = ngx_http_variable_null_value;
593
594    return NGX_OK;
595}
596
597
598static ngx_int_t
599ngx_http_userid_add_variables(ngx_conf_t *cf)
600{
601    ngx_int_t             n;
602    ngx_http_variable_t  *var;
603
604    var = ngx_http_add_variable(cf, &ngx_http_userid_got, 0);
605    if (var == NULL) {
606        return NGX_ERROR;
607    }
608
609    var->get_handler = ngx_http_userid_got_variable;
610
611    var = ngx_http_add_variable(cf, &ngx_http_userid_set, 0);
612    if (var == NULL) {
613        return NGX_ERROR;
614    }
615
616    var->get_handler = ngx_http_userid_set_variable;
617
618    var = ngx_http_add_variable(cf, &ngx_http_userid_reset,
619                                NGX_HTTP_VAR_CHANGEABLE);
620    if (var == NULL) {
621        return NGX_ERROR;
622    }
623
624    var->get_handler = ngx_http_userid_reset_variable;
625
626    n = ngx_http_get_variable_index(cf, &ngx_http_userid_reset);
627    if (n == NGX_ERROR) {
628        return NGX_ERROR;
629    }
630
631    ngx_http_userid_reset_index = n;
632
633    return NGX_OK;
634}
635
636
637static void *
638ngx_http_userid_create_conf(ngx_conf_t *cf)
639{
640    ngx_http_userid_conf_t  *conf;
641
642    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_userid_conf_t));
643    if (conf == NULL) {
644        return NULL;
645    }
646
647    /*
648     * set by ngx_pcalloc():
649     *
650     *     conf->name = { 0, NULL };
651     *     conf->domain = { 0, NULL };
652     *     conf->path = { 0, NULL };
653     *     conf->p3p = { 0, NULL };
654     */
655
656    conf->enable = NGX_CONF_UNSET_UINT;
657    conf->service = NGX_CONF_UNSET;
658    conf->expires = NGX_CONF_UNSET;
659    conf->mark = (u_char) '\xFF';
660
661    return conf;
662}
663
664
665static char *
666ngx_http_userid_merge_conf(ngx_conf_t *cf, void *parent, void *child)
667{
668    ngx_http_userid_conf_t *prev = parent;
669    ngx_http_userid_conf_t *conf = child;
670
671    ngx_conf_merge_uint_value(conf->enable, prev->enable,
672                              NGX_HTTP_USERID_OFF);
673
674    ngx_conf_merge_str_value(conf->name, prev->name, "uid");
675    ngx_conf_merge_str_value(conf->domain, prev->domain, "");
676    ngx_conf_merge_str_value(conf->path, prev->path, "; path=/");
677    ngx_conf_merge_str_value(conf->p3p, prev->p3p, "");
678
679    ngx_conf_merge_value(conf->service, prev->service, NGX_CONF_UNSET);
680    ngx_conf_merge_sec_value(conf->expires, prev->expires, 0);
681
682    if (conf->mark == (u_char) '\xFF') {
683        if (prev->mark == (u_char) '\xFF') {
684            conf->mark = '\0';
685        } else {
686            conf->mark = prev->mark;
687        }
688    }
689
690    return NGX_CONF_OK;
691}
692
693
694static ngx_int_t
695ngx_http_userid_init(ngx_conf_t *cf)
696{
697    ngx_http_next_header_filter = ngx_http_top_header_filter;
698    ngx_http_top_header_filter = ngx_http_userid_filter;
699
700    return NGX_OK;
701}
702
703
704static char *
705ngx_http_userid_domain(ngx_conf_t *cf, void *post, void *data)
706{
707    ngx_str_t  *domain = data;
708
709    u_char  *p, *new;
710
711    if (ngx_strcmp(domain->data, "none") == 0) {
712        ngx_str_set(domain, "");
713        return NGX_CONF_OK;
714    }
715
716    new = ngx_pnalloc(cf->pool, sizeof("; domain=") - 1 + domain->len);
717    if (new == NULL) {
718        return NGX_CONF_ERROR;
719    }
720
721    p = ngx_cpymem(new, "; domain=", sizeof("; domain=") - 1);
722    ngx_memcpy(p, domain->data, domain->len);
723
724    domain->len += sizeof("; domain=") - 1;
725    domain->data = new;
726
727    return NGX_CONF_OK;
728}
729
730
731static char *
732ngx_http_userid_path(ngx_conf_t *cf, void *post, void *data)
733{
734    ngx_str_t  *path = data;
735
736    u_char  *p, *new;
737
738    new = ngx_pnalloc(cf->pool, sizeof("; path=") - 1 + path->len);
739    if (new == NULL) {
740        return NGX_CONF_ERROR;
741    }
742
743    p = ngx_cpymem(new, "; path=", sizeof("; path=") - 1);
744    ngx_memcpy(p, path->data, path->len);
745
746    path->len += sizeof("; path=") - 1;
747    path->data = new;
748
749    return NGX_CONF_OK;
750}
751
752
753static char *
754ngx_http_userid_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
755{
756    ngx_http_userid_conf_t *ucf = conf;
757
758    ngx_str_t  *value;
759
760    if (ucf->expires != NGX_CONF_UNSET) {
761        return "is duplicate";
762    }
763
764    value = cf->args->elts;
765
766    if (ngx_strcmp(value[1].data, "max") == 0) {
767        ucf->expires = NGX_HTTP_USERID_MAX_EXPIRES;
768        return NGX_CONF_OK;
769    }
770
771    if (ngx_strcmp(value[1].data, "off") == 0) {
772        ucf->expires = 0;
773        return NGX_CONF_OK;
774    }
775
776    ucf->expires = ngx_parse_time(&value[1], 1);
777    if (ucf->expires == (time_t) NGX_ERROR) {
778        return "invalid value";
779    }
780
781    return NGX_CONF_OK;
782}
783
784
785static char *
786ngx_http_userid_p3p(ngx_conf_t *cf, void *post, void *data)
787{
788    ngx_str_t  *p3p = data;
789
790    if (ngx_strcmp(p3p->data, "none") == 0) {
791        ngx_str_set(p3p, "");
792    }
793
794    return NGX_CONF_OK;
795}
796
797
798static char *
799ngx_http_userid_mark(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
800{
801    ngx_http_userid_conf_t *ucf = conf;
802
803    ngx_str_t  *value;
804
805    if (ucf->mark != (u_char) '\xFF') {
806        return "is duplicate";
807    }
808
809    value = cf->args->elts;
810
811    if (ngx_strcmp(value[1].data, "off") == 0) {
812        ucf->mark = '\0';
813        return NGX_CONF_OK;
814    }
815
816    if (value[1].len != 1
817        || !((value[1].data[0] >= '0' && value[1].data[0] <= '9')
818              || (value[1].data[0] >= 'A' && value[1].data[0] <= 'Z')
819              || (value[1].data[0] >= 'a' && value[1].data[0] <= 'z')
820              || value[1].data[0] == '='))
821    {
822        return "value must be \"off\" or a single letter, digit or \"=\"";
823    }
824
825    ucf->mark = value[1].data[0];
826
827    return NGX_CONF_OK;
828}
829
830
831static ngx_int_t
832ngx_http_userid_init_worker(ngx_cycle_t *cycle)
833{
834    struct timeval  tp;
835
836    ngx_gettimeofday(&tp);
837
838    /* use the most significant usec part that fits to 16 bits */
839    start_value = (((uint32_t) tp.tv_usec / 20) << 16) | ngx_pid;
840
841    return NGX_OK;
842}
843