OSDN Git Service

* change writting.
[modchxj/mod_chxj.git] / src / chxj_cookie.c
1 /*
2  * Copyright (C) 2005 QSDN,Inc. All rights reserved.
3  * Copyright (C) 2005 Atsushi Konno All rights reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 #include "mod_chxj.h"
18 #include "chxj_cookie.h"
19 #include "chxj_url_encode.h"
20 #include "chxj_apply_convrule.h"
21
22 #include "apu.h"
23 #include "apr_dbm.h"
24 #include "apr_uuid.h"
25 #include "apr_md5.h"
26 #include "apr_base64.h"
27
28 static char* s_get_hostname_from_url(request_rec* r, char* value);
29 static char* s_cut_until_end_hostname(request_rec*, char* value);
30
31 /*
32  *
33  */
34 cookie_t*
35 chxj_save_cookie(request_rec* r)
36 {
37   int                 ii;
38   apr_array_header_t* headers;
39   apr_table_entry_t*  hentryp;
40   apr_status_t        retval;
41   apr_datum_t         dbmkey;
42   apr_datum_t         dbmval;
43   apr_dbm_t*          f;
44   apr_uuid_t          uuid;
45   char*               uuid_string;
46   unsigned char*      md5_value;
47   char*               old_cookie_id;
48   char*               store_string;
49   mod_chxj_config*    dconf;
50   chxjconvrule_entry* entryp;
51   apr_file_t*         file;
52   apr_table_t*        new_cookie_table;
53   int                 has_cookie = 0;
54   cookie_t*           cookie;
55   cookie_t*           old_cookie;
56
57
58   DBG(r, "start chxj_save_cookie()");
59
60   cookie = (cookie_t*)apr_palloc(r->pool, sizeof(cookie_t));
61   cookie->cookie_id = NULL;
62
63   has_cookie = 0;
64
65   dconf = ap_get_module_config(r->per_dir_config, &chxj_module);
66   entryp = chxj_apply_convrule(r, dconf->convrules);
67   if (! entryp) {
68     DBG(r, "end chxj_save_cookie() no pattern");
69     return NULL;
70   }
71   if (! (entryp->action & CONVRULE_COOKIE_ON_BIT)) {
72     DBG(r, "end chxj_save_cookie() CookieOff");
73     return NULL;
74   }
75
76
77   headers = (apr_array_header_t*)apr_table_elts(r->headers_out);
78   hentryp = (apr_table_entry_t*)headers->elts;
79
80
81   new_cookie_table = apr_table_make(r->pool, 0);
82
83   for (ii=0; ii<headers->nelts; ii++) {
84     if (strcasecmp(hentryp[ii].key, "Set-Cookie") == 0) {
85       DBG(r, "=====================================");
86       DBG2(r, "cookie=[%s:%s]", hentryp[ii].key, hentryp[ii].val);
87
88       char* key;
89       char* val;
90       char* buff;
91
92       buff = apr_pstrdup(r->pool, hentryp[ii].val);
93       val = strchr(buff, '=');
94       if (val) {
95         key = buff;
96         *val++ = 0;
97         apr_table_add(new_cookie_table, key, val);
98       }
99
100       has_cookie = 1;
101       DBG(r, "=====================================");
102     }
103   }
104
105   /*
106    * check input parameters
107    */
108   old_cookie_id = (char*)apr_table_get(r->headers_in, "CHXJ_COOKIE_ID");
109   if (old_cookie_id) {
110     old_cookie = chxj_load_cookie(r, old_cookie_id); 
111     if (old_cookie && old_cookie->cookie_headers) {
112       hentryp = (apr_table_entry_t*)old_cookie->cookie_headers->elts;
113       for (ii=0; ii<old_cookie->cookie_headers->nelts; ii++) {
114         if (hentryp[ii].key && apr_table_get(new_cookie_table, hentryp[ii].key) == NULL) {
115           apr_table_setn(new_cookie_table, hentryp[ii].key, hentryp[ii].val);
116           has_cookie = 1;
117         }
118       }
119       chxj_delete_cookie(r, old_cookie_id);
120     }
121   }
122
123   if (! has_cookie) {
124     DBG(r, "no cookie");
125     return NULL;
126   }
127
128   file = chxj_cookie_db_lock(r);
129   if (! file) {
130     ERR(r, "mod_chxj: Can't lock cookie db");
131     return NULL;
132   }
133
134   DBG(r, " ");
135
136   retval = apr_dbm_open_ex(&f, 
137                            "default", 
138                            "/tmp/cookie.db", 
139                            APR_DBM_RWCREATE, 
140                            APR_OS_DEFAULT, 
141                            r->pool);
142   if (retval != APR_SUCCESS) {
143     ERR2(r, "could not open dbm (type %s) auth file: %s", "default", "/tmp/cookie.db");
144     chxj_cookie_db_unlock(r, file);
145     return NULL;
146   }
147
148   apr_uuid_get(&uuid);
149   uuid_string = apr_palloc(r->pool, APR_UUID_FORMATTED_LENGTH + 1);
150   memset(uuid_string, 0, APR_UUID_FORMATTED_LENGTH + 1);
151   apr_uuid_format(uuid_string, &uuid);;
152
153   md5_value = (unsigned char*)apr_palloc(r->pool, APR_MD5_DIGESTSIZE + 1);
154   memset(md5_value, 0, APR_MD5_DIGESTSIZE + 1);
155   retval = apr_md5(md5_value, 
156           (const char*)uuid_string, (apr_size_t)APR_UUID_FORMATTED_LENGTH);
157   if (retval != APR_SUCCESS) {
158     ERR(r, "md5 failed.");
159     goto on_error;
160   }
161
162   cookie->cookie_id = apr_palloc(r->pool, apr_base64_encode_len(APR_MD5_DIGESTSIZE)+1);
163   memset(cookie->cookie_id, 0, APR_MD5_DIGESTSIZE+1);
164   apr_base64_encode(cookie->cookie_id, (char*)md5_value, APR_MD5_DIGESTSIZE);
165
166   DBG1(r, "cookie->cookie_id=[%s]", cookie->cookie_id);
167
168   cookie->cookie_id = chxj_url_encode(r,cookie->cookie_id);
169
170   DBG1(r, "cookie->cookie_id=[%s]", cookie->cookie_id);
171
172   /*
173    * create key
174    */
175
176   dbmkey.dptr  = cookie->cookie_id;
177   dbmkey.dsize = strlen(cookie->cookie_id);
178
179   /*
180    * create val
181    */
182   cookie->cookie_headers = (apr_array_header_t*)apr_table_elts(new_cookie_table);
183   store_string = apr_palloc(r->pool, 1);
184   store_string[0] = 0;
185   hentryp = (apr_table_entry_t*)cookie->cookie_headers->elts;
186
187   for (ii=0; ii<cookie->cookie_headers->nelts; ii++) {
188     if (ii) store_string = apr_pstrcat(r->pool,
189                                store_string, 
190                                "\n",
191                                NULL);
192
193     store_string = apr_pstrcat(r->pool, 
194                                store_string, 
195                                hentryp[ii].key, 
196                                "=",
197                                hentryp[ii].val, 
198                                NULL);
199   }
200   dbmval.dptr  = store_string;
201   dbmval.dsize = strlen(store_string);
202
203   /*
204    * store to db
205    */
206   retval = apr_dbm_store(f, dbmkey, dbmval);
207   if (retval != APR_SUCCESS) {
208     ERR1(r, "Cannot store SSL session to DBM file `%s'","/tmp/cookie.db");
209     goto on_error;
210   }
211
212
213 on_error:
214   apr_dbm_close(f);
215   chxj_cookie_db_unlock(r, file);
216
217   DBG(r, "end   chxj_save_cookie()");
218   return cookie;
219 }
220
221
222 /*
223  *
224  * @return loaded data.
225  */
226 cookie_t*
227 chxj_load_cookie(request_rec* r, char* cookie_id)
228 {
229   apr_status_t            retval;
230   apr_datum_t             dbmkey;
231   apr_datum_t             dbmval;
232   apr_dbm_t*              f;
233   mod_chxj_global_config* gconf;
234   mod_chxj_config*        dconf;
235   chxjconvrule_entry*     entryp;
236   apr_file_t*             file;
237   cookie_t*               cookie;
238   apr_table_t*            load_cookie_table;
239   char*                   load_string;
240   char*                   pstat;
241   char*                   key;
242   char*                   val;
243   char*                   pair;
244
245   DBG1(r, "start chxj_load_cookie() cookie_id=[%s]", cookie_id);
246
247   cookie = (cookie_t*)apr_palloc(r->pool, sizeof(cookie_t));
248   cookie->cookie_headers = NULL;
249   cookie->cookie_id = apr_pstrdup(r->pool, cookie_id);
250
251   gconf = ap_get_module_config(r->server->module_config, &chxj_module);
252   dconf = ap_get_module_config(r->per_dir_config, &chxj_module);
253   entryp = chxj_apply_convrule(r, dconf->convrules);
254   if (! entryp) {
255     DBG(r, "end chxj_load_cookie() no pattern");
256     goto on_error0;
257   }
258   if (! (entryp->action & CONVRULE_COOKIE_ON_BIT)) {
259     DBG(r, "end chxj_load_cookie() CookieOff");
260     goto on_error0;
261   }
262
263
264   file = chxj_cookie_db_lock(r);
265   if (! file) {
266     ERR(r, "mod_chxj: Can't lock cookie db");
267     goto on_error0;
268   }
269
270   retval = apr_dbm_open_ex(&f, 
271                            "default", 
272                            "/tmp/cookie.db", 
273                            APR_DBM_RWCREATE, 
274                            APR_OS_DEFAULT, 
275                            r->pool);
276   if (retval != APR_SUCCESS) {
277     ERR2(r, "could not open dbm (type %s) auth file: %s", "default", "/tmp/cookie.db");
278     goto on_error1;
279   }
280
281   /*
282    * create key
283    */
284   dbmkey.dptr  = apr_pstrdup(r->pool, cookie->cookie_id);
285   dbmkey.dsize = strlen(dbmkey.dptr);
286   if (apr_dbm_exists(f, dbmkey)) {
287   
288     retval = apr_dbm_fetch(f, dbmkey, &dbmval);
289     if (retval != APR_SUCCESS) {
290       ERR2(r, "could not fetch dbm (type %s) auth file: %s", "default", "/tmp/cookie.db");
291       goto on_error2;
292     }
293     load_cookie_table = apr_table_make(r->pool, 0);
294     load_string = apr_palloc(r->pool, dbmval.dsize+1);
295
296     memset(load_string, 0, dbmval.dsize+1);
297     memcpy(load_string, dbmval.dptr, dbmval.dsize);
298     for (;;) {
299       pair = apr_strtok(load_string, "\n", &pstat);  
300       load_string = NULL;
301       if (!pair) break;
302
303       DBG1(r, "Cookie:[%s]", pair);
304       char* tmp_pair;
305
306       tmp_pair = apr_pstrdup(r->pool, pair);
307       val = strchr(tmp_pair, '=');
308       if (val) {
309         key = tmp_pair;
310         *val++ = 0;
311         apr_table_add(load_cookie_table, key, val);
312       }
313       apr_table_setn(r->headers_in, "Cookie", pair);
314     }
315   
316     cookie->cookie_headers = (apr_array_header_t*)apr_table_elts(load_cookie_table);
317   
318     /*
319      * save cookie_id to request header.
320      */
321     apr_table_setn(r->headers_in, "CHXJ_COOKIE_ID", cookie->cookie_id);
322   }
323   apr_dbm_close(f);
324   chxj_cookie_db_unlock(r, file);
325   DBG(r, "end   chxj_load_cookie()");
326
327   return cookie;
328
329
330 on_error2:
331   apr_dbm_close(f);
332
333 on_error1:
334   chxj_cookie_db_unlock(r, file);
335
336 on_error0:
337
338   DBG(r, "end   chxj_load_cookie()");
339   return NULL;
340 }
341
342
343 char*
344 chxj_add_cookie_parameter(request_rec* r, char* value, cookie_t* cookie)
345 {
346   char* qs;
347   char* dst;
348
349   DBG1(r, "start chxj_add_cookie_parameter() cookie_id=[%s]", (cookie) ? cookie->cookie_id : NULL);
350
351   dst = value;
352
353   if (!cookie)
354     goto on_error;
355
356   if (!cookie->cookie_id)
357     goto on_error;
358
359   if (chxj_cookie_check_host(r, value) != 0) {
360     DBG(r, "end chxj_add_cookie_parameter()(check host)");
361     goto on_error;
362   }
363
364
365   qs = strchr(dst, '?');
366   if (qs) {
367     dst = apr_psprintf(r->pool, "%s&%s=%s", dst, CHXJ_COOKIE_PARAM, cookie->cookie_id);
368   }
369   else {
370     dst = apr_psprintf(r->pool, "%s?%s=%s", dst, CHXJ_COOKIE_PARAM, cookie->cookie_id);
371   }
372
373   DBG1(r, "end   chxj_add_cookie_parameter() dst=[%s]", dst);
374
375   return dst;
376
377 on_error:
378   DBG(r, "end   chxj_add_cookie_parameter() (on_error)");
379   return dst;
380 }
381
382
383 int
384 chxj_cookie_check_host(request_rec* r, char* value) 
385 {
386   char* hostnm;
387
388   DBG1(r, "hostname=[%s]", r->hostname);
389
390   hostnm = s_get_hostname_from_url(r, value);
391   if (hostnm) {
392     if (strcasecmp(hostnm, r->hostname) == 0)
393       return 0;
394     else
395       return 1;
396   }
397   return 0;
398 }
399
400
401 static char*
402 s_get_hostname_from_url(request_rec* r, char* value)
403 {
404   if (!value) 
405     return NULL; 
406
407   if (strncasecmp(value, "http://",  7) == 0 )
408     return s_cut_until_end_hostname(r, &value[7]);
409
410   if (strncasecmp(value, "https://", 8) == 0)
411     return s_cut_until_end_hostname(r, &value[8]);
412
413   return NULL;
414 }
415
416
417 static char* 
418 s_cut_until_end_hostname(request_rec* r, char* value)
419 {
420   char* sp;
421   char* hostnm;
422
423   hostnm = sp = apr_pstrdup(r->pool, value);
424   for (;*sp; sp++) {
425     if (*sp == '/'|| *sp == '?') {
426       *sp = '\0';
427       break;
428     }
429   }
430   return hostnm;
431 }
432
433 apr_file_t*
434 chxj_cookie_db_lock(request_rec* r)
435 {
436   apr_file_t* file;
437   apr_status_t rv;
438
439   rv = apr_file_open(&file, "/tmp/cookie_db.lock", APR_CREATE|APR_WRITE, APR_OS_DEFAULT, r->pool);
440   if (rv != APR_SUCCESS) {
441     ERR(r, "cookie lock file open failed.");
442     return NULL;
443   }
444
445   rv = apr_file_lock(file,APR_FLOCK_EXCLUSIVE);
446   if (rv != APR_SUCCESS) {
447     ERR(r, "cookie lock file open failed.");
448     apr_file_close(file);
449     return NULL;
450   }
451
452   return file;
453 }
454
455 void
456 chxj_cookie_db_unlock(request_rec* r, apr_file_t* file)
457 {
458   apr_status_t rv;
459
460   rv = apr_file_unlock(file);
461   if (rv != APR_SUCCESS) {
462     ERR(r, "cookie lock file open failed.");
463     return;
464   }
465
466   apr_file_close(file);
467 }
468
469 void
470 chxj_delete_cookie(request_rec* r, char* cookie_id)
471 {
472   apr_status_t        retval;
473   apr_datum_t dbmkey;
474   apr_dbm_t*          f;
475   apr_file_t*       file;
476
477   DBG(r, "start chxj_delete_cookie()");
478
479   file = chxj_cookie_db_lock(r);
480   if (file == NULL) {
481     ERR(r, "mod_chxj: Can't lock cookie db");
482     goto on_error0;
483   }
484
485   retval = apr_dbm_open_ex(&f,
486                            "default",
487                            "/tmp/cookie.db",
488                            APR_DBM_RWCREATE,
489                            APR_OS_DEFAULT,
490                            r->pool);
491   if (retval != APR_SUCCESS) {
492     ERR2(r, "could not open dbm (type %s) auth file: %s", "default", "/tmp/cookie.db");
493     goto on_error1;
494   }
495   /*
496    * create key
497    */
498   dbmkey.dptr  = apr_pstrdup(r->pool, cookie_id);
499   dbmkey.dsize = strlen(dbmkey.dptr);
500   if (apr_dbm_exists(f, dbmkey)) {
501     apr_dbm_delete(f, dbmkey);
502   }
503   apr_dbm_close(f);
504   chxj_cookie_db_unlock(r, file);
505
506   DBG(r, "end   chxj_delete_cookie()");
507
508   return;
509
510 on_error1:
511   chxj_cookie_db_unlock(r, file);
512
513 on_error0:
514   return;
515
516 }
517 /*
518  * vim:ts=2 et
519  */