- next is 1.4.56
[lighttpd.git] / src / mod_usertrack.c
blob932c75e7069a71d0c3c7bfa566408694bd976f93
1 #include "first.h"
3 #include "base.h"
4 #include "log.h"
5 #include "buffer.h"
6 #include "rand.h"
7 #include "http_header.h"
9 #include "plugin.h"
11 #include <stdlib.h>
12 #include <string.h>
14 #include "md5.h"
16 /* plugin config for all request/connections */
18 typedef struct {
19 buffer *cookie_name;
20 buffer *cookie_attrs;
21 buffer *cookie_domain;
22 unsigned int cookie_max_age;
23 } plugin_config;
25 typedef struct {
26 PLUGIN_DATA;
28 plugin_config **config_storage;
30 plugin_config conf;
31 } plugin_data;
33 /* init the plugin data */
34 INIT_FUNC(mod_usertrack_init) {
35 plugin_data *p;
37 p = calloc(1, sizeof(*p));
39 return p;
42 /* detroy the plugin data */
43 FREE_FUNC(mod_usertrack_free) {
44 plugin_data *p = p_d;
46 UNUSED(srv);
48 if (!p) return HANDLER_GO_ON;
50 if (p->config_storage) {
51 size_t i;
52 for (i = 0; i < srv->config_context->used; i++) {
53 plugin_config *s = p->config_storage[i];
55 if (NULL == s) continue;
57 buffer_free(s->cookie_name);
58 buffer_free(s->cookie_attrs);
59 buffer_free(s->cookie_domain);
61 free(s);
63 free(p->config_storage);
66 free(p);
68 return HANDLER_GO_ON;
71 /* handle plugin config and check values */
73 SETDEFAULTS_FUNC(mod_usertrack_set_defaults) {
74 plugin_data *p = p_d;
75 size_t i = 0;
77 config_values_t cv[] = {
78 { "usertrack.cookie-name", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
79 { "usertrack.cookie-max-age", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
80 { "usertrack.cookie-domain", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
81 { "usertrack.cookie-attrs", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
83 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
86 if (!p) return HANDLER_ERROR;
88 p->config_storage = calloc(srv->config_context->used, sizeof(plugin_config *));
90 for (i = 0; i < srv->config_context->used; i++) {
91 data_config const* config = (data_config const*)srv->config_context->data[i];
92 plugin_config *s;
94 s = calloc(1, sizeof(plugin_config));
95 s->cookie_name = buffer_init();
96 s->cookie_attrs = buffer_init();
97 s->cookie_domain = buffer_init();
98 s->cookie_max_age = 0;
100 cv[0].destination = s->cookie_name;
101 cv[1].destination = &(s->cookie_max_age);
102 cv[2].destination = s->cookie_domain;
103 cv[3].destination = s->cookie_attrs;
105 p->config_storage[i] = s;
107 if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
108 return HANDLER_ERROR;
111 if (buffer_string_is_empty(s->cookie_name)) {
112 buffer_copy_string_len(s->cookie_name, CONST_STR_LEN("TRACKID"));
113 } else {
114 size_t j, len = buffer_string_length(s->cookie_name);
115 for (j = 0; j < len; j++) {
116 char c = s->cookie_name->ptr[j] | 32;
117 if (c < 'a' || c > 'z') {
118 log_error_write(srv, __FILE__, __LINE__, "sb",
119 "invalid character in usertrack.cookie-name:",
120 s->cookie_name);
122 return HANDLER_ERROR;
127 if (!buffer_string_is_empty(s->cookie_domain)) {
128 size_t j, len = buffer_string_length(s->cookie_domain);
129 for (j = 0; j < len; j++) {
130 char c = s->cookie_domain->ptr[j];
131 if (c <= 32 || c >= 127 || c == '"' || c == '\\') {
132 log_error_write(srv, __FILE__, __LINE__, "sb",
133 "invalid character in usertrack.cookie-domain:",
134 s->cookie_domain);
136 return HANDLER_ERROR;
142 return HANDLER_GO_ON;
145 #define PATCH(x) \
146 p->conf.x = s->x;
147 static int mod_usertrack_patch_connection(server *srv, connection *con, plugin_data *p) {
148 size_t i, j;
149 plugin_config *s = p->config_storage[0];
151 PATCH(cookie_name);
152 PATCH(cookie_attrs);
153 PATCH(cookie_domain);
154 PATCH(cookie_max_age);
156 /* skip the first, the global context */
157 for (i = 1; i < srv->config_context->used; i++) {
158 data_config *dc = (data_config *)srv->config_context->data[i];
159 s = p->config_storage[i];
161 /* condition didn't match */
162 if (!config_check_cond(srv, con, dc)) continue;
164 /* merge config */
165 for (j = 0; j < dc->value->used; j++) {
166 data_unset *du = dc->value->data[j];
168 if (buffer_is_equal_string(du->key, CONST_STR_LEN("usertrack.cookie-name"))) {
169 PATCH(cookie_name);
170 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("usertrack.cookie-attrs"))) {
171 PATCH(cookie_attrs);
172 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("usertrack.cookie-max-age"))) {
173 PATCH(cookie_max_age);
174 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("usertrack.cookie-domain"))) {
175 PATCH(cookie_domain);
180 return 0;
182 #undef PATCH
184 URIHANDLER_FUNC(mod_usertrack_uri_handler) {
185 plugin_data *p = p_d;
186 buffer *cookie;
187 buffer *b;
188 unsigned char h[16];
189 li_MD5_CTX Md5Ctx;
190 char hh[LI_ITOSTRING_LENGTH];
192 if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON;
194 mod_usertrack_patch_connection(srv, con, p);
196 if (NULL != (b = http_header_request_get(con, HTTP_HEADER_COOKIE, CONST_STR_LEN("Cookie")))) {
197 char *g;
198 /* we have a cookie, does it contain a valid name ? */
200 /* parse the cookie
202 * check for cookiename + (WS | '=')
206 if (NULL != (g = strstr(b->ptr, p->conf.cookie_name->ptr))) {
207 char *nc;
209 /* skip WS */
210 for (nc = g + buffer_string_length(p->conf.cookie_name); *nc == ' ' || *nc == '\t'; nc++);
212 if (*nc == '=') {
213 /* ok, found the key of our own cookie */
215 if (strlen(nc) > 32) {
216 /* i'm lazy */
217 return HANDLER_GO_ON;
223 /* set a cookie */
224 cookie = srv->tmp_buf;
225 buffer_copy_buffer(cookie, p->conf.cookie_name);
226 buffer_append_string_len(cookie, CONST_STR_LEN("="));
229 /* taken from mod_auth.c */
231 /* generate shared-secret */
232 li_MD5_Init(&Md5Ctx);
233 li_MD5_Update(&Md5Ctx, CONST_BUF_LEN(con->uri.path));
234 li_MD5_Update(&Md5Ctx, CONST_STR_LEN("+"));
236 li_itostrn(hh, sizeof(hh), srv->cur_ts);
237 li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh));
238 li_itostrn(hh, sizeof(hh), li_rand_pseudo());
239 li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh));
241 li_MD5_Final(h, &Md5Ctx);
243 buffer_append_string_encoded_hex_lc(cookie, (char *)h, 16);
245 /* usertrack.cookie-attrs, if set, replaces all other attrs */
246 if (!buffer_string_is_empty(p->conf.cookie_attrs)) {
247 buffer_append_string_buffer(cookie, p->conf.cookie_attrs);
248 http_header_response_insert(con, HTTP_HEADER_SET_COOKIE, CONST_STR_LEN("Set-Cookie"), CONST_BUF_LEN(cookie));
249 return HANDLER_GO_ON;
252 buffer_append_string_len(cookie, CONST_STR_LEN("; Path=/"));
253 buffer_append_string_len(cookie, CONST_STR_LEN("; Version=1"));
255 if (!buffer_string_is_empty(p->conf.cookie_domain)) {
256 buffer_append_string_len(cookie, CONST_STR_LEN("; Domain="));
257 buffer_append_string_encoded(cookie, CONST_BUF_LEN(p->conf.cookie_domain), ENCODING_REL_URI);
260 if (p->conf.cookie_max_age) {
261 buffer_append_string_len(cookie, CONST_STR_LEN("; max-age="));
262 buffer_append_int(cookie, p->conf.cookie_max_age);
265 http_header_response_insert(con, HTTP_HEADER_SET_COOKIE, CONST_STR_LEN("Set-Cookie"), CONST_BUF_LEN(cookie));
267 return HANDLER_GO_ON;
270 /* this function is called at dlopen() time and inits the callbacks */
272 int mod_usertrack_plugin_init(plugin *p);
273 int mod_usertrack_plugin_init(plugin *p) {
274 p->version = LIGHTTPD_VERSION_ID;
275 p->name = buffer_init_string("usertrack");
277 p->init = mod_usertrack_init;
278 p->handle_uri_clean = mod_usertrack_uri_handler;
279 p->set_defaults = mod_usertrack_set_defaults;
280 p->cleanup = mod_usertrack_free;
282 p->data = NULL;
284 return 0;