15 /* plugin config for all request/connections */
19 buffer
*cookie_domain
;
20 unsigned int cookie_max_age
;
26 plugin_config
**config_storage
;
31 /* init the plugin data */
32 INIT_FUNC(mod_usertrack_init
) {
35 p
= calloc(1, sizeof(*p
));
40 /* detroy the plugin data */
41 FREE_FUNC(mod_usertrack_free
) {
46 if (!p
) return HANDLER_GO_ON
;
48 if (p
->config_storage
) {
50 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
51 plugin_config
*s
= p
->config_storage
[i
];
53 if (NULL
== s
) continue;
55 buffer_free(s
->cookie_name
);
56 buffer_free(s
->cookie_domain
);
60 free(p
->config_storage
);
68 /* handle plugin config and check values */
70 SETDEFAULTS_FUNC(mod_usertrack_set_defaults
) {
74 config_values_t cv
[] = {
75 { "usertrack.cookie-name", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
}, /* 0 */
76 { "usertrack.cookie-max-age", NULL
, T_CONFIG_INT
, T_CONFIG_SCOPE_CONNECTION
}, /* 1 */
77 { "usertrack.cookie-domain", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
}, /* 2 */
79 { "usertrack.cookiename", NULL
, T_CONFIG_DEPRECATED
, T_CONFIG_SCOPE_CONNECTION
},
80 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
83 if (!p
) return HANDLER_ERROR
;
85 p
->config_storage
= calloc(1, srv
->config_context
->used
* sizeof(plugin_config
*));
87 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
88 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
91 s
= calloc(1, sizeof(plugin_config
));
92 s
->cookie_name
= buffer_init();
93 s
->cookie_domain
= buffer_init();
94 s
->cookie_max_age
= 0;
96 cv
[0].destination
= s
->cookie_name
;
97 cv
[1].destination
= &(s
->cookie_max_age
);
98 cv
[2].destination
= s
->cookie_domain
;
100 p
->config_storage
[i
] = s
;
102 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
103 return HANDLER_ERROR
;
106 if (buffer_string_is_empty(s
->cookie_name
)) {
107 buffer_copy_string_len(s
->cookie_name
, CONST_STR_LEN("TRACKID"));
109 size_t j
, len
= buffer_string_length(s
->cookie_name
);
110 for (j
= 0; j
< len
; j
++) {
111 char c
= s
->cookie_name
->ptr
[j
] | 32;
112 if (c
< 'a' || c
> 'z') {
113 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
114 "invalid character in usertrack.cookie-name:",
117 return HANDLER_ERROR
;
122 if (!buffer_string_is_empty(s
->cookie_domain
)) {
123 size_t j
, len
= buffer_string_length(s
->cookie_domain
);
124 for (j
= 0; j
< len
; j
++) {
125 char c
= s
->cookie_domain
->ptr
[j
];
126 if (c
<= 32 || c
>= 127 || c
== '"' || c
== '\\') {
127 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
128 "invalid character in usertrack.cookie-domain:",
131 return HANDLER_ERROR
;
137 return HANDLER_GO_ON
;
142 static int mod_usertrack_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
144 plugin_config
*s
= p
->config_storage
[0];
147 PATCH(cookie_domain
);
148 PATCH(cookie_max_age
);
150 /* skip the first, the global context */
151 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
152 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
153 s
= p
->config_storage
[i
];
155 /* condition didn't match */
156 if (!config_check_cond(srv
, con
, dc
)) continue;
159 for (j
= 0; j
< dc
->value
->used
; j
++) {
160 data_unset
*du
= dc
->value
->data
[j
];
162 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("usertrack.cookie-name"))) {
164 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("usertrack.cookie-max-age"))) {
165 PATCH(cookie_max_age
);
166 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("usertrack.cookie-domain"))) {
167 PATCH(cookie_domain
);
176 URIHANDLER_FUNC(mod_usertrack_uri_handler
) {
177 plugin_data
*p
= p_d
;
181 char hh
[LI_ITOSTRING_LENGTH
];
183 if (buffer_is_empty(con
->uri
.path
)) return HANDLER_GO_ON
;
185 mod_usertrack_patch_connection(srv
, con
, p
);
187 if (NULL
!= (ds
= (data_string
*)array_get_element(con
->request
.headers
, "Cookie"))) {
189 /* we have a cookie, does it contain a valid name ? */
193 * check for cookiename + (WS | '=')
197 if (NULL
!= (g
= strstr(ds
->value
->ptr
, p
->conf
.cookie_name
->ptr
))) {
201 for (nc
= g
+ buffer_string_length(p
->conf
.cookie_name
); *nc
== ' ' || *nc
== '\t'; nc
++);
204 /* ok, found the key of our own cookie */
206 if (strlen(nc
) > 32) {
208 return HANDLER_GO_ON
;
215 if (NULL
== (ds
= (data_string
*)array_get_unused_element(con
->response
.headers
, TYPE_STRING
))) {
216 ds
= data_response_init();
218 buffer_copy_string_len(ds
->key
, CONST_STR_LEN("Set-Cookie"));
219 buffer_copy_buffer(ds
->value
, p
->conf
.cookie_name
);
220 buffer_append_string_len(ds
->value
, CONST_STR_LEN("="));
223 /* taken from mod_auth.c */
225 /* generate shared-secret */
226 li_MD5_Init(&Md5Ctx
);
227 li_MD5_Update(&Md5Ctx
, CONST_BUF_LEN(con
->uri
.path
));
228 li_MD5_Update(&Md5Ctx
, CONST_STR_LEN("+"));
230 /* we assume sizeof(time_t) == 4 here, but if not it ain't a problem at all */
231 li_itostrn(hh
, sizeof(hh
), srv
->cur_ts
);
232 li_MD5_Update(&Md5Ctx
, (unsigned char *)hh
, strlen(hh
));
233 li_MD5_Update(&Md5Ctx
, (unsigned char *)srv
->entropy
, sizeof(srv
->entropy
));
234 li_itostrn(hh
, sizeof(hh
), rand());
235 li_MD5_Update(&Md5Ctx
, (unsigned char *)hh
, strlen(hh
));
237 li_MD5_Final(h
, &Md5Ctx
);
239 buffer_append_string_encoded(ds
->value
, (char *)h
, 16, ENCODING_HEX
);
240 buffer_append_string_len(ds
->value
, CONST_STR_LEN("; Path=/"));
241 buffer_append_string_len(ds
->value
, CONST_STR_LEN("; Version=1"));
243 if (!buffer_string_is_empty(p
->conf
.cookie_domain
)) {
244 buffer_append_string_len(ds
->value
, CONST_STR_LEN("; Domain="));
245 buffer_append_string_encoded(ds
->value
, CONST_BUF_LEN(p
->conf
.cookie_domain
), ENCODING_REL_URI
);
248 if (p
->conf
.cookie_max_age
) {
249 buffer_append_string_len(ds
->value
, CONST_STR_LEN("; max-age="));
250 buffer_append_int(ds
->value
, p
->conf
.cookie_max_age
);
253 array_insert_unique(con
->response
.headers
, (data_unset
*)ds
);
255 return HANDLER_GO_ON
;
258 /* this function is called at dlopen() time and inits the callbacks */
260 int mod_usertrack_plugin_init(plugin
*p
);
261 int mod_usertrack_plugin_init(plugin
*p
) {
262 p
->version
= LIGHTTPD_VERSION_ID
;
263 p
->name
= buffer_init_string("usertrack");
265 p
->init
= mod_usertrack_init
;
266 p
->handle_uri_clean
= mod_usertrack_uri_handler
;
267 p
->set_defaults
= mod_usertrack_set_defaults
;
268 p
->cleanup
= mod_usertrack_free
;