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 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
82 if (!p
) return HANDLER_ERROR
;
84 p
->config_storage
= calloc(1, srv
->config_context
->used
* sizeof(plugin_config
*));
86 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
87 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
90 s
= calloc(1, sizeof(plugin_config
));
91 s
->cookie_name
= buffer_init();
92 s
->cookie_domain
= buffer_init();
93 s
->cookie_max_age
= 0;
95 cv
[0].destination
= s
->cookie_name
;
96 cv
[1].destination
= &(s
->cookie_max_age
);
97 cv
[2].destination
= s
->cookie_domain
;
99 p
->config_storage
[i
] = s
;
101 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
102 return HANDLER_ERROR
;
105 if (buffer_string_is_empty(s
->cookie_name
)) {
106 buffer_copy_string_len(s
->cookie_name
, CONST_STR_LEN("TRACKID"));
108 size_t j
, len
= buffer_string_length(s
->cookie_name
);
109 for (j
= 0; j
< len
; j
++) {
110 char c
= s
->cookie_name
->ptr
[j
] | 32;
111 if (c
< 'a' || c
> 'z') {
112 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
113 "invalid character in usertrack.cookie-name:",
116 return HANDLER_ERROR
;
121 if (!buffer_string_is_empty(s
->cookie_domain
)) {
122 size_t j
, len
= buffer_string_length(s
->cookie_domain
);
123 for (j
= 0; j
< len
; j
++) {
124 char c
= s
->cookie_domain
->ptr
[j
];
125 if (c
<= 32 || c
>= 127 || c
== '"' || c
== '\\') {
126 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
127 "invalid character in usertrack.cookie-domain:",
130 return HANDLER_ERROR
;
136 return HANDLER_GO_ON
;
141 static int mod_usertrack_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
143 plugin_config
*s
= p
->config_storage
[0];
146 PATCH(cookie_domain
);
147 PATCH(cookie_max_age
);
149 /* skip the first, the global context */
150 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
151 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
152 s
= p
->config_storage
[i
];
154 /* condition didn't match */
155 if (!config_check_cond(srv
, con
, dc
)) continue;
158 for (j
= 0; j
< dc
->value
->used
; j
++) {
159 data_unset
*du
= dc
->value
->data
[j
];
161 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("usertrack.cookie-name"))) {
163 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("usertrack.cookie-max-age"))) {
164 PATCH(cookie_max_age
);
165 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("usertrack.cookie-domain"))) {
166 PATCH(cookie_domain
);
175 URIHANDLER_FUNC(mod_usertrack_uri_handler
) {
176 plugin_data
*p
= p_d
;
180 char hh
[LI_ITOSTRING_LENGTH
];
182 if (buffer_is_empty(con
->uri
.path
)) return HANDLER_GO_ON
;
184 mod_usertrack_patch_connection(srv
, con
, p
);
186 if (NULL
!= (ds
= (data_string
*)array_get_element(con
->request
.headers
, "Cookie"))) {
188 /* we have a cookie, does it contain a valid name ? */
192 * check for cookiename + (WS | '=')
196 if (NULL
!= (g
= strstr(ds
->value
->ptr
, p
->conf
.cookie_name
->ptr
))) {
200 for (nc
= g
+ buffer_string_length(p
->conf
.cookie_name
); *nc
== ' ' || *nc
== '\t'; nc
++);
203 /* ok, found the key of our own cookie */
205 if (strlen(nc
) > 32) {
207 return HANDLER_GO_ON
;
214 if (NULL
== (ds
= (data_string
*)array_get_unused_element(con
->response
.headers
, TYPE_STRING
))) {
215 ds
= data_response_init();
217 buffer_copy_string_len(ds
->key
, CONST_STR_LEN("Set-Cookie"));
218 buffer_copy_buffer(ds
->value
, p
->conf
.cookie_name
);
219 buffer_append_string_len(ds
->value
, CONST_STR_LEN("="));
222 /* taken from mod_auth.c */
224 /* generate shared-secret */
225 li_MD5_Init(&Md5Ctx
);
226 li_MD5_Update(&Md5Ctx
, CONST_BUF_LEN(con
->uri
.path
));
227 li_MD5_Update(&Md5Ctx
, CONST_STR_LEN("+"));
229 /* we assume sizeof(time_t) == 4 here, but if not it ain't a problem at all */
230 li_itostrn(hh
, sizeof(hh
), srv
->cur_ts
);
231 li_MD5_Update(&Md5Ctx
, (unsigned char *)hh
, strlen(hh
));
232 li_MD5_Update(&Md5Ctx
, (unsigned char *)srv
->entropy
, sizeof(srv
->entropy
));
233 li_itostrn(hh
, sizeof(hh
), rand());
234 li_MD5_Update(&Md5Ctx
, (unsigned char *)hh
, strlen(hh
));
236 li_MD5_Final(h
, &Md5Ctx
);
238 buffer_append_string_encoded(ds
->value
, (char *)h
, 16, ENCODING_HEX
);
239 buffer_append_string_len(ds
->value
, CONST_STR_LEN("; Path=/"));
240 buffer_append_string_len(ds
->value
, CONST_STR_LEN("; Version=1"));
242 if (!buffer_string_is_empty(p
->conf
.cookie_domain
)) {
243 buffer_append_string_len(ds
->value
, CONST_STR_LEN("; Domain="));
244 buffer_append_string_encoded(ds
->value
, CONST_BUF_LEN(p
->conf
.cookie_domain
), ENCODING_REL_URI
);
247 if (p
->conf
.cookie_max_age
) {
248 buffer_append_string_len(ds
->value
, CONST_STR_LEN("; max-age="));
249 buffer_append_int(ds
->value
, p
->conf
.cookie_max_age
);
252 array_insert_unique(con
->response
.headers
, (data_unset
*)ds
);
254 return HANDLER_GO_ON
;
257 /* this function is called at dlopen() time and inits the callbacks */
259 int mod_usertrack_plugin_init(plugin
*p
);
260 int mod_usertrack_plugin_init(plugin
*p
) {
261 p
->version
= LIGHTTPD_VERSION_ID
;
262 p
->name
= buffer_init_string("usertrack");
264 p
->init
= mod_usertrack_init
;
265 p
->handle_uri_clean
= mod_usertrack_uri_handler
;
266 p
->set_defaults
= mod_usertrack_set_defaults
;
267 p
->cleanup
= mod_usertrack_free
;