Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 304 lines (247 sloc) 7.099 kb
d17e28e @hollow add mod_visitor
authored
1 #include "apr.h"
2 #include "apr_strings.h"
3
4 #define APR_WANT_STRFUNC
5 #include "apr_want.h"
6
7 #include "httpd.h"
8 #include "http_config.h"
9 #include "http_core.h"
10 #include "http_log.h"
11 #include "http_request.h"
12
13 module AP_MODULE_DECLARE_DATA visitor_module;
14
15 typedef struct {
16 int enabled;
17 char *domain;
18 apr_int64_t vexpiry;
19 apr_int64_t sexpiry;
20 } visitor_dir_rec;
21
22 static uint8_t visitor_image[] = {
23 // GIF header
24 0x47, 0x49, 0x46,
25 // version (89a)
26 0x38, 0x39, 0x61,
27 // image size (1x1 px)
28 0x01, 0x00, 0x01, 0x00,
29 // flags + color index
30 0x90, 0x00, 0x00,
31 // color palette
32 0xff, 0xff, 0xff,
33 // image block
34 0x00, 0x00, 0x00,
35 0x2c, 0x00, 0x00,
36 0x00, 0x00, 0x01,
37 0x00, 0x01, 0x00,
38 0x00, 0x02, 0x02,
39 0x04, 0x01, 0x00,
40 // trailer
41 0x3b
42 };
43
44 static
45 void set_cookie(request_rec *r,
46 const char *name, const char *val,
47 apr_time_t expiry)
48 {
49 /* calculate expiry time in GMT */
50 apr_time_exp_t tms;
51 apr_time_exp_gmt(&tms,
52 r->request_time + apr_time_from_sec(expiry));
53
54 /* generate cookie header */
55 char *new_cookie;
56 new_cookie = apr_psprintf(r->pool,
57 "%s=%s; path=/",
58 name, val);
59
60 if (expiry > 0) {
61 new_cookie = apr_psprintf(r->pool,
62 "%s; expires=%s, "
63 "%.2d-%s-%.2d %.2d:%.2d:%.2d GMT",
64 new_cookie, apr_day_snames[tms.tm_wday],
65 tms.tm_mday, apr_month_snames[tms.tm_mon],
66 tms.tm_year % 100,
67 tms.tm_hour, tms.tm_min, tms.tm_sec);
68 }
69
70 /* append domain name if configured */
71 visitor_dir_rec *dcfg = ap_get_module_config(r->per_dir_config, &visitor_module);
72
73 if (dcfg->domain != NULL) {
74 new_cookie = apr_pstrcat(r->pool,
75 new_cookie, "; domain=",
76 dcfg->domain, NULL);
77 }
78
79 /* add cookie header */
80 apr_table_addn(r->headers_out, "Set-Cookie", new_cookie);
81 }
82
83 static
84 const char *get_cookie(request_rec *r, const char *name)
85 {
86 const char *cookies;
87 cookies = apr_table_get(r->headers_in, "Cookie");
88
89 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
90 "searching cookie '%s' in '%s'", name, cookies);
91
92 if (cookies == NULL)
93 return NULL;
94
95 const char *re_str = apr_pstrcat(r->pool,
96 "^", name, "=([^;,]+)|[;,][ \t]*", name, "=([^;,]+)", NULL);
97
98 ap_regex_t *re_bin = ap_pregcomp(r->pool, re_str, AP_REG_EXTENDED);
99 ap_assert(re_bin != NULL);
100
101 char *val = NULL;
102 ap_regmatch_t regm[3];
103
104 if (!ap_regexec(re_bin, cookies, 3, regm, 0)) {
105 if (regm[1].rm_so != -1)
106 val = apr_pstrndup(r->pool,
107 cookies + regm[1].rm_so,
108 regm[1].rm_eo - regm[1].rm_so);
109 if (regm[2].rm_so != -1)
110 val = apr_pstrndup(r->pool,
111 cookies + regm[2].rm_so,
112 regm[2].rm_eo - regm[2].rm_so);
113 }
114
115 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
116 "found cookie: '%s'", val);
117
118 return val;
119 }
120
121 static
122 const char *make_cookie(request_rec *r, const char *name, apr_time_t expiry)
123 {
124 const char *id;
125
126 if ((id = apr_table_get(r->subprocess_env, "UNIQUE_ID")))
127 set_cookie(r, name, id, expiry);
128
129 return id;
130 }
131
132 static
133 void spot_cookies(request_rec *r)
134 {
135 visitor_dir_rec *dcfg = ap_get_module_config(r->per_dir_config,
136 &visitor_module);
137
138 /* do not run in subrequests */
139 if (r->main)
140 return;
141
142 const char *a = get_cookie(r, "__vta");
143 const char *b = get_cookie(r, "__vtb");
144 const char *c = get_cookie(r, "__vtc");
145
146 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
b1ecfe3 @hollow fix expiry config, add license and readme
authored
147 "got visitor cookies: a=%s; b=%s; c=%s", a, b, c);
d17e28e @hollow add mod_visitor
authored
148
149 if (a == NULL) {
b1ecfe3 @hollow fix expiry config, add license and readme
authored
150 a = make_cookie(r, "__vta", dcfg->vexpiry);
151 b = make_cookie(r, "__vtb", dcfg->sexpiry);
d17e28e @hollow add mod_visitor
authored
152 c = make_cookie(r, "__vtc", 0);
153 } else if (b == NULL || c == NULL) {
b1ecfe3 @hollow fix expiry config, add license and readme
authored
154 b = make_cookie(r, "__vtb", dcfg->sexpiry);
d17e28e @hollow add mod_visitor
authored
155 c = make_cookie(r, "__vtc", 0);
156 }
157
158 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
b1ecfe3 @hollow fix expiry config, add license and readme
authored
159 "new visitor cookies: a=%s; b=%s; c=%s", a, b, c);
d17e28e @hollow add mod_visitor
authored
160
161 if (a && b)
162 apr_table_setn(r->notes, "visitor-cookie",
163 apr_pstrcat(r->pool, a, ";", b, NULL));
164
165 return;
166 }
167
168 static
169 int visitor_cookie_handler(request_rec *r)
170 {
171 if (strcmp(r->handler, "visitor-cookie"))
172 return DECLINED;
173
174 r->allowed |= (AP_METHOD_BIT << M_GET);
175 if (r->method_number != M_GET)
176 return DECLINED;
177
178 /* check and set cookies */
179 spot_cookies(r);
180
181 /* send images */
182 ap_set_content_type(r, "image/gif");
183 ap_set_content_length(r, sizeof(visitor_image));
184 ap_rwrite(visitor_image, sizeof(visitor_image), r);
185
186 return OK;
187 }
188
189 static void *visitor_create_dir_config(apr_pool_t *p, char *d)
190 {
191 visitor_dir_rec *dcfg = apr_pcalloc(p, sizeof(visitor_dir_rec));
192
193 dcfg->enabled = 0;
194 dcfg->domain = NULL;
195 dcfg->vexpiry = 2*365*24*60*60; /* aprox. 2 years */
196 dcfg->sexpiry = 30*60; /* 30 minutes */
197
198 return dcfg;
199 }
200
201 static const char *cmd_visitor_tracking(cmd_parms *cmd, void *mconfig, int arg)
202 {
203 visitor_dir_rec *dcfg = mconfig;
204 dcfg->enabled = arg;
205 return NULL;
206 }
207
208 static
209 const char *cmd_visitor_domain(cmd_parms *cmd, void *mconfig, const char *name)
210 {
211 visitor_dir_rec *dcfg = mconfig;
212
213 if (strlen(name) == 0)
214 return "VisitorDomain values may not be null";
215 if (name[0] != '.')
216 return "VisitorDomain values must begin with a dot";
217 if (ap_strchr_c(&name[1], '.') == NULL)
218 return "VisitorDomain values must contain at least one embedded dot";
219
220 dcfg->domain = apr_pstrdup(cmd->pool, name);
221 return NULL;
222 }
223
224 static
225 const char *cmd_visitor_expiry(cmd_parms *cmd, void *mconfig, const char *seconds)
226 {
227 visitor_dir_rec *dcfg = mconfig;
228
229 if (strlen(seconds) == 0)
230 return "VisitorExpiry values may not be null";
231
232 char *end;
233 apr_int64_t expiry = apr_strtoi64(seconds, &end, 10);
234
235 if (end - seconds < strlen(seconds) || expiry < 0)
236 return "VisitorExpiry value is not a positive number";
237
238 dcfg->vexpiry = expiry;
239 return NULL;
240 }
241
242 static
243 const char *cmd_session_expiry(cmd_parms *cmd, void *mconfig, const char *seconds)
244 {
245 visitor_dir_rec *dcfg = mconfig;
246
247 if (strlen(seconds) == 0)
248 return "SessionExpiry values may not be null";
249
250 char *end;
251 apr_int64_t expiry = apr_strtoi64(seconds, &end, 10);
252
253 if (end - seconds < strlen(seconds) || expiry < 0)
254 return "SessionExpiry value is not a positive number";
255
256 dcfg->sexpiry = expiry;
257 return NULL;
258 }
259
260 static
261 const command_rec visitor_cmds[] = {
262 AP_INIT_FLAG(
263 "VisitorTracking",
264 cmd_visitor_tracking,
265 NULL,
266 OR_FILEINFO,
267 "whether or not to enable visitor tracking"),
268 AP_INIT_TAKE1(
269 "VisitorDomain",
270 cmd_visitor_domain,
271 NULL,
272 OR_FILEINFO,
273 "domain to which mod_visitor cookies apply"),
274 AP_INIT_TAKE1(
275 "VisitorExpiry",
276 cmd_visitor_expiry,
277 NULL,
278 OR_FILEINFO,
279 "time after which visitor cookie expires in seconds"),
280 AP_INIT_TAKE1(
281 "SessionExpiry",
282 cmd_session_expiry,
283 NULL,
284 OR_FILEINFO,
285 "time after which session cookie expires in seconds"),
286 { NULL }
287 };
288
289 static
290 void visitor_register_hooks(apr_pool_t *p)
291 {
292 ap_hook_handler(visitor_cookie_handler, NULL, NULL, APR_HOOK_FIRST);
293 }
294
295 module AP_MODULE_DECLARE_DATA visitor_module = {
296 STANDARD20_MODULE_STUFF,
297 visitor_create_dir_config, /* per-directory config creater */
298 NULL, /* dir config merger */
299 NULL, /* server config creator */
300 NULL, /* server config merger */
301 visitor_cmds, /* command table */
302 visitor_register_hooks /* set up other request processing hooks */
303 };
Something went wrong with that request. Please try again.