OSDN Git Service

* Added New Features.
[modchxj/mod_chxj.git] / src / chxj_cookie.c
1 /*
2  * Copyright (C) 2005-2009 Atsushi Konno All rights reserved.
3  * Copyright (C) 2005 QSDN,Inc. 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 <time.h>
18
19 #include "mod_chxj.h"
20 #include "chxj_cookie.h"
21 #include "chxj_url_encode.h"
22 #include "chxj_apply_convrule.h"
23 #include "chxj_str_util.h"
24
25 #include "ap_release.h"
26
27 #include "apu.h"
28 #include "apr_dbm.h"
29 #include "apr_uuid.h"
30 #include "apr_md5.h"
31 #include "apr_base64.h"
32 #include "apr_uri.h"
33 #include "apr_time.h"
34 #include "apr_date.h"
35
36 #if defined(USE_MYSQL_COOKIE)
37 #  include "chxj_mysql.h"
38 #endif
39 #if defined(USE_MEMCACHE_COOKIE)
40 #  include "chxj_memcache.h"
41 #endif
42 #include "chxj_dbm.h"
43
44 #define DUMP_HEADERS(X) do { \
45   int __ii; \
46   apr_array_header_t  *__headers = (apr_array_header_t*)apr_table_elts((X)); \
47   apr_table_entry_t   *__hentryp = (apr_table_entry_t*)__headers->elts; \
48   for (__ii=0; __ii<__headers->nelts; __ii++) { \
49     DBG(r, "key:[%s] val:[%s]", __hentryp[__ii].key, __hentryp[__ii].val); \
50   } \
51 } while (0) 
52
53
54
55 static char* s_get_hostname_from_url(request_rec* r, char* value);
56 static char* s_cut_until_end_hostname(request_rec*, char* value);
57 static int valid_domain(request_rec *r, const char *value);
58 static int valid_path(request_rec *r, const char *value);
59 static int valid_expires(request_rec *r, const char *value);
60 static int valid_secure(request_rec *r, const char *value);
61 static int check_valid_cookie_attribute(request_rec *r, const char *pair);
62 static int check_valid_cookie_attribute_expires_only(request_rec *r, const char *value);
63
64 apr_proc_mutex_t *global_cookie_mutex;
65
66 static char *
67 alloc_cookie_id(request_rec *r)
68 {
69   char                *cookie_id;
70   char                *uuid_string;
71   unsigned char       *md5_value;
72   apr_uuid_t          uuid;
73   apr_status_t        retval;
74
75   apr_uuid_get(&uuid);
76   uuid_string = apr_palloc(r->pool, APR_UUID_FORMATTED_LENGTH + 1);
77   memset(uuid_string, 0, APR_UUID_FORMATTED_LENGTH + 1);
78   apr_uuid_format(uuid_string, &uuid);;
79
80   md5_value = (unsigned char*)apr_palloc(r->pool, APR_MD5_DIGESTSIZE + 1);
81   memset(md5_value, 0, APR_MD5_DIGESTSIZE + 1);
82
83   retval = apr_md5(md5_value, 
84                    (const char*)uuid_string, 
85                    APR_UUID_FORMATTED_LENGTH);
86   if (retval != APR_SUCCESS) {
87     ERR(r, "md5 failed.");
88     return NULL;
89   }
90
91   cookie_id = apr_palloc(r->pool, apr_base64_encode_len(APR_MD5_DIGESTSIZE)+1);
92   memset(cookie_id, 0, APR_MD5_DIGESTSIZE+1);
93   apr_base64_encode(cookie_id, (char*)md5_value, APR_MD5_DIGESTSIZE);
94
95   DBG(r, "cookie_id=[%s]", cookie_id);
96
97   cookie_id = chxj_url_encode(r->pool,cookie_id);
98
99   DBG(r, "cookie_id=[%s]", cookie_id);
100   return cookie_id;
101 }
102
103
104 /*
105  *
106  */
107 cookie_t *
108 chxj_save_cookie(request_rec *r)
109 {
110   int                 ii;
111   apr_array_header_t  *headers;
112   apr_table_entry_t   *hentryp;
113   apr_array_header_t  *err_headers;
114   apr_table_entry_t   *err_hentryp;
115   char                *old_cookie_id;
116   char                *store_string;
117   mod_chxj_config     *dconf;
118   chxjconvrule_entry  *entryp;
119   apr_table_t         *new_cookie_table;
120   int                 has_cookie = 0;
121   cookie_t            *cookie;
122   cookie_t            *old_cookie;
123   char                *refer_string;
124   apr_uri_t           parsed_uri;
125   int                 has_refer;
126   apr_pool_t          *pool;
127
128
129   DBG(r, "start chxj_save_cookie()");
130
131   apr_pool_create(&pool, r->pool);
132
133   cookie = (cookie_t*)apr_palloc(pool, sizeof(cookie_t));
134   cookie->cookie_id = NULL;
135
136   has_cookie = 0;
137   has_refer = 0;
138
139   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
140   entryp = chxj_apply_convrule(r, dconf->convrules);
141   if (! entryp) {
142     DBG(r, "end chxj_save_cookie() no pattern");
143     return NULL;
144   }
145   if (! (entryp->action & CONVRULE_COOKIE_ON_BIT)) {
146     DBG(r, "end chxj_save_cookie() CookieOff");
147     return NULL;
148   }
149
150
151
152   headers = (apr_array_header_t*)apr_table_elts(r->headers_out);
153   hentryp = (apr_table_entry_t*)headers->elts;
154   err_headers = (apr_array_header_t*)apr_table_elts(r->err_headers_out);
155   err_hentryp = (apr_table_entry_t*)err_headers->elts;
156
157
158   new_cookie_table = apr_table_make(pool, 0);
159
160   for (ii=0; ii<headers->nelts; ii++) {
161     if (strcasecmp(hentryp[ii].key, "Set-Cookie") == 0) {
162       DBG(r, "REQ[%X] cookie=[%s:%s]", (unsigned int)(apr_size_t)r, hentryp[ii].key, hentryp[ii].val);
163
164       char* key;
165       char* val;
166       char* buff;
167
168       char *pair = apr_psprintf(pool, "%s:%s", hentryp[ii].key, hentryp[ii].val);
169       if (check_valid_cookie_attribute_expires_only(r, pair)) {
170         buff = apr_pstrdup(pool, hentryp[ii].val);
171         val = strchr(buff, '=');
172         if (val) {
173           key = buff;
174           *val++ = 0;
175           apr_table_set(new_cookie_table, apr_pstrdup(pool, key), apr_pstrdup(pool, val));
176           if (strcasecmp(REFERER_COOKIE_KEY, key) == 0) has_refer++;
177         }
178         has_cookie = 1;
179       }
180     }
181   }
182   for (ii=0; ii<err_headers->nelts; ii++) {
183     if (strcasecmp(err_hentryp[ii].key, "Set-Cookie") == 0) {
184       DBG(r, "REQ[%X] cookie=[%s:%s]", (unsigned int)(apr_size_t)r, err_hentryp[ii].key, err_hentryp[ii].val);
185
186       char* key;
187       char* val;
188       char* buff;
189
190       char *pair = apr_psprintf(pool, "%s:%s", err_hentryp[ii].key, err_hentryp[ii].val);
191       if (check_valid_cookie_attribute_expires_only(r, pair)) {
192         buff = apr_pstrdup(pool, err_hentryp[ii].val);
193         val = strchr(buff, '=');
194         if (val) {
195           key = buff;
196           *val++ = 0;
197           apr_table_set(new_cookie_table, apr_pstrdup(pool, key), apr_pstrdup(pool, val));
198           if (strcasecmp(REFERER_COOKIE_KEY, key) == 0) has_refer++;
199         }
200         has_cookie = 1;
201       }
202     }
203   }
204   apr_table_unset(r->headers_out, "Set-Cookie");
205   apr_table_unset(r->err_headers_out, "Set-Cookie");
206
207   if (! has_refer) {
208     apr_uri_parse(pool,r->uri, &parsed_uri);
209     refer_string = apr_psprintf(pool, 
210                                 "%s://%s%s", 
211                                 chxj_run_http_scheme(r),
212                                 r->hostname,
213                                 apr_uri_unparse(pool,
214                                                 &parsed_uri,
215                                                 APR_URI_UNP_OMITSITEPART));
216     if (r->args && strlen(r->args)) {
217       refer_string = apr_pstrcat(pool, refer_string, "?", r->args, NULL);
218     }
219     apr_table_setn(new_cookie_table, REFERER_COOKIE_KEY, refer_string);
220     DBG(r, "ADD REFER[%s]", refer_string);
221     has_cookie++;
222   }
223
224
225   /*
226    * check input parameters
227    */
228   old_cookie_id = (char*)apr_table_get(r->headers_in, "CHXJ_COOKIE_ID");
229   if (old_cookie_id) {
230     old_cookie = chxj_load_cookie(r, old_cookie_id); 
231     if (old_cookie && old_cookie->cookie_headers) {
232       hentryp = (apr_table_entry_t*)old_cookie->cookie_headers->elts;
233       for (ii=0; ii<old_cookie->cookie_headers->nelts; ii++) {
234         if (hentryp && apr_table_get(new_cookie_table, hentryp[ii].key) == NULL) {
235           apr_table_add(new_cookie_table, apr_pstrdup(pool, hentryp[ii].key), apr_pstrdup(pool, hentryp[ii].val));
236         }
237       }
238       if (has_cookie) {
239         chxj_delete_cookie(r,        old_cookie_id);
240         chxj_delete_cookie_expire(r, old_cookie_id);
241       }
242     }
243   }
244
245
246
247   if (! has_cookie) {
248     DBG(r, "REQ[%X] end chxj_save_cookie() (no cookie)", (unsigned int)(apr_size_t)r);
249     return NULL;
250   }
251
252   /*
253    * create val
254    */
255   cookie->cookie_headers = (apr_array_header_t*)apr_table_elts(new_cookie_table);
256   hentryp = (apr_table_entry_t*)cookie->cookie_headers->elts;
257   apr_size_t store_string_len = 0;
258   for (ii=0; ii<cookie->cookie_headers->nelts; ii++) {
259     if (ii) store_string_len++;
260     store_string_len += (strlen(hentryp[ii].key) + strlen(hentryp[ii].val) + 1);
261   }
262   store_string = apr_palloc(pool, store_string_len + 1);
263   memset(store_string, 0, store_string_len + 1);
264   apr_size_t npos = 0;
265
266   for (ii=0; ii<cookie->cookie_headers->nelts; ii++) {
267     if (ii) store_string[npos++] = '\n';
268     memcpy(&store_string[npos], hentryp[ii].key, strlen(hentryp[ii].key));
269     npos += strlen(hentryp[ii].key);
270     store_string[npos++] = '=';
271     memcpy(&store_string[npos], hentryp[ii].val, strlen(hentryp[ii].val));
272     npos += strlen(hentryp[ii].val);
273   }
274
275   if (old_cookie_id && IS_COOKIE_LAZY(dconf)) {
276     DBG(r, "REQ[%X] LAZY COOKIE save",(unsigned int)(apr_size_t)r);
277     cookie->cookie_id = apr_pstrdup(r->pool, old_cookie_id);
278   }
279   else if (old_cookie_id && apr_table_get(r->headers_in, "X-Chxj-Cookie-No-Update")) {
280     DBG(r, "REQ[%X] NO UPDATE MODE",(unsigned int)(apr_size_t)r);
281     cookie->cookie_id = apr_pstrdup(r->pool, old_cookie_id);
282   }
283   else {
284     if (old_cookie_id && apr_table_get(r->headers_in, "X-Chxj-Forward")) {
285       DBG(r, "REQ[%X] NO LAZY COOKIE(X-Chxj-Forward)  save",(unsigned int)(apr_size_t)r);
286       cookie->cookie_id = apr_pstrdup(r->pool, old_cookie_id);
287     }
288     else {
289       DBG(r, "REQ[%X] NO LAZY COOKIE save",(unsigned int)(apr_size_t)r);
290       cookie->cookie_id = alloc_cookie_id(r);
291     }
292   }
293
294   DBG(r, "REQ[%X] TYPE:[%d]", (unsigned int)(apr_size_t)r, dconf->cookie_store_type);
295
296   {
297     int done_proc = 0;
298 #if defined(USE_MYSQL_COOKIE)
299     if (IS_COOKIE_STORE_MYSQL(dconf->cookie_store_type)) {
300       if (! chxj_save_cookie_mysql(r, dconf, cookie->cookie_id, store_string)) {
301         ERR(r, "%s:%d faild: chxj_save_cookie_mysql() cookie_id:[%s]", APLOG_MARK,cookie->cookie_id);
302         cookie = NULL;
303         goto on_error;
304       }
305       done_proc = 1;
306     }
307 #endif
308 #if defined(USE_MEMCACHE_COOKIE)
309     if (IS_COOKIE_STORE_MEMCACHE(dconf->cookie_store_type)) {
310       if (! chxj_save_cookie_memcache(r, dconf, cookie->cookie_id, store_string)) {
311         ERR(r, "%s:%d failed: chxj_save_cookie_memcache() cookie_id:[%s]", APLOG_MARK, cookie->cookie_id);
312         cookie = NULL;
313         goto on_error;
314       }
315       done_proc = 1;
316     }
317 #endif
318     if (IS_COOKIE_STORE_DBM(dconf->cookie_store_type) || ! done_proc) {
319       if (! chxj_save_cookie_dbm(r, dconf, cookie->cookie_id, store_string)) {
320         ERR(r, "%s:%d failed: chxj_save_cookie_dbm() cookie_id:[%s]", APLOG_MARK, cookie->cookie_id);
321         cookie = NULL;
322         goto on_error;
323       }
324     }
325   }
326   apr_table_unset(r->headers_out, "Set-Cookie");
327   apr_table_unset(r->err_headers_out, "Set-Cookie");
328
329   if (cookie) {
330     chxj_save_cookie_expire(r, cookie);
331   }
332
333 on_error:
334   DBG(r, "end chxj_save_cookie()");
335   return cookie;
336 }
337
338
339
340 /*
341  *
342  */
343 cookie_t *
344 chxj_update_cookie(request_rec *r, cookie_t *old_cookie)
345 {
346   int                 ii;
347   apr_array_header_t  *headers;
348   apr_table_entry_t   *hentryp;
349   char                *store_string;
350   mod_chxj_config     *dconf;
351   chxjconvrule_entry  *entryp;
352   cookie_t            *cookie;
353
354
355   DBG(r, "start chxj_update_cookie()");
356   if (!old_cookie || ! old_cookie->cookie_headers || ! old_cookie->cookie_id) {
357     DBG(r, "end chxj_update_cookie() (old_cookie is null)");
358     return  NULL;
359   }
360
361   cookie = (cookie_t *)apr_palloc(r->pool, sizeof(cookie_t));
362   cookie->cookie_id = NULL;
363
364   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
365   entryp = chxj_apply_convrule(r, dconf->convrules);
366   if (! entryp) {
367     DBG(r, "end chxj_update_cookie() no pattern");
368     return NULL;
369   }
370   if (! (entryp->action & CONVRULE_COOKIE_ON_BIT)) {
371     DBG(r, "end chxj_update_cookie() CookieOff");
372     return NULL;
373   }
374
375   headers = (apr_array_header_t*)apr_table_elts(r->headers_out);
376   hentryp = (apr_table_entry_t*)headers->elts;
377
378   chxj_delete_cookie(r,        old_cookie->cookie_id);
379   chxj_delete_cookie_expire(r, old_cookie->cookie_id);
380
381   if (IS_COOKIE_LAZY(dconf)) {
382     DBG(r, "LAZY MODE");
383     cookie->cookie_id = apr_pstrdup(r->pool, old_cookie->cookie_id);
384   }
385   else {
386     DBG(r, "NO LAZY MODE");
387     cookie->cookie_id = alloc_cookie_id(r);
388   }
389
390   cookie->cookie_headers = old_cookie->cookie_headers;
391   store_string = apr_palloc(r->pool, 1);
392   store_string[0] = 0;
393   hentryp = (apr_table_entry_t*)cookie->cookie_headers->elts;
394
395   for (ii=0; ii<cookie->cookie_headers->nelts; ii++) {
396     if (ii) store_string = apr_pstrcat(r->pool,
397                                store_string, 
398                                "\n",
399                                NULL);
400
401     DBG(r, "OLD COOKIE VALUE=[%s][%s]", hentryp[ii].key, hentryp[ii].val);
402     store_string = apr_pstrcat(r->pool, 
403                                store_string, 
404                                hentryp[ii].key, 
405                                "=",
406                                hentryp[ii].val, 
407                                NULL);
408   }
409
410   {
411     int done_proc = 0;
412 #if defined(USE_MYSQL_COOKIE)
413     if (IS_COOKIE_STORE_MYSQL(dconf->cookie_store_type)) {
414       if (!chxj_update_cookie_mysql(r, dconf, cookie->cookie_id, store_string)) {
415         ERR(r, "failed: chxj_update_cookie_mysql() cookie_id:[%s]", cookie->cookie_id);
416         goto on_error;
417       }
418       done_proc = 1;
419     }
420 #endif
421
422 #if defined(USE_MEMCACHE_COOKIE)
423     if (IS_COOKIE_STORE_MEMCACHE(dconf->cookie_store_type)) {
424       if (! chxj_update_cookie_memcache(r, dconf, cookie->cookie_id, store_string)) {
425         ERR(r, "failed: chxj_update_cookie_memcache() cookie_id:[%s]", cookie->cookie_id);
426         goto on_error;
427       }
428       done_proc = 1;
429     }
430 #endif
431     if (!done_proc || IS_COOKIE_STORE_DBM(dconf->cookie_store_type)) {
432       if (! chxj_update_cookie_dbm(r, dconf, cookie->cookie_id, store_string)) {
433         ERR(r, "failed: chxj_update_cookie_dbm() cookie_id:[%s]", cookie->cookie_id);
434         goto on_error;
435       }
436     }
437   }
438
439   chxj_save_cookie_expire(r, cookie);
440
441   apr_table_setn(r->headers_in, "CHXJ_COOKIE_ID", cookie->cookie_id);
442
443
444 on_error:
445   DBG(r, "end   chxj_update_cookie()");
446   return cookie;
447 }
448
449
450 /*
451  *
452  * @return loaded data.
453  */
454 cookie_t *
455 chxj_load_cookie(request_rec *r, char *cookie_id)
456 {
457   mod_chxj_config         *dconf;
458   chxjconvrule_entry      *entryp;
459   cookie_t                *cookie;
460   apr_table_t             *load_cookie_table;
461   char                    *load_string = NULL;
462   char                    *pstat;
463   char                    *key;
464   char                    *val;
465   char                    *pair;
466   char                    *header_cookie;
467
468   DBG(r, "start chxj_load_cookie() cookie_id=[%s]", cookie_id);
469   chxj_cookie_expire_gc(r);
470
471   cookie = (cookie_t*)apr_palloc(r->pool, sizeof(cookie_t));
472   cookie->cookie_headers = NULL;
473   cookie->cookie_id = chxj_url_decode(r->pool, apr_pstrdup(r->pool, cookie_id));
474   cookie->cookie_id = chxj_url_encode(r->pool, cookie->cookie_id);
475
476
477   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
478   entryp = chxj_apply_convrule(r, dconf->convrules);
479   if (! entryp) {
480     DBG(r, "end chxj_load_cookie() no pattern");
481     goto on_error0;
482   }
483   if (! (entryp->action & CONVRULE_COOKIE_ON_BIT)) {
484     DBG(r, "end chxj_load_cookie() CookieOff");
485     goto on_error0;
486   }
487   load_cookie_table = apr_table_make(r->pool, 0);
488
489   {
490     int done_proc = 0;
491 #if defined(USE_MYSQL_COOKIE)
492     if (IS_COOKIE_STORE_MYSQL(dconf->cookie_store_type)) {
493       if (! (load_string = chxj_load_cookie_mysql(r, dconf, cookie->cookie_id))) {
494         ERR(r, "%s:%d failed: chxj_load_cookie_mysql() cookie_id:[%s]", APLOG_MARK, cookie_id);
495         goto on_error0;
496       }
497       done_proc = 1;
498     }
499 #endif
500 #if defined(USE_MEMCACHE_COOKIE)
501     if (IS_COOKIE_STORE_MEMCACHE(dconf->cookie_store_type)) {
502       if (! (load_string = chxj_load_cookie_memcache(r, dconf, cookie->cookie_id))) {
503         ERR(r, "%s:%d failed: chxj_load_cookie_memcache() cookie_id:[%s]", APLOG_MARK,cookie_id);
504         goto on_error0;
505       }
506       done_proc = 1;
507     }
508 #endif
509     if (!done_proc || IS_COOKIE_STORE_DBM(dconf->cookie_store_type)) {
510       if (! (load_string = chxj_load_cookie_dbm(r, dconf, cookie->cookie_id))) {
511         ERR(r, "%s:%d failed: chxj_load_cookie_dbm() cookie_id:[%s]", APLOG_MARK,cookie_id);
512         goto on_error0;
513       }
514     }
515   }
516
517   if (load_string) {
518     DBG(r, "load_string=[%s]", load_string);
519     header_cookie = apr_palloc(r->pool, 1);
520     header_cookie[0] = 0;
521     for (;;) {
522       char *tmp_sem;
523       char *tmp_pair;
524       pair = apr_strtok(load_string, "\n", &pstat);  
525       load_string = NULL;
526       if (!pair) break;
527
528       DBG(r, "Cookie:[%s]", pair);
529
530       tmp_pair = apr_pstrdup(r->pool, pair);
531       val = strchr(tmp_pair, '=');
532       if (val) {
533         key = tmp_pair;
534         *val++ = 0;
535         apr_table_add(load_cookie_table, key, val);
536         DBG(r, "ADD key:[%s] val:[%s]", key, val);
537       }
538       tmp_pair = apr_pstrdup(r->pool, pair);
539       tmp_sem = strchr(tmp_pair, ';'); 
540       if (tmp_sem)
541         *tmp_sem = '\0';
542
543       if (check_valid_cookie_attribute(r, pair)) {
544         if (strlen(header_cookie)) 
545           header_cookie = apr_pstrcat(r->pool, header_cookie, ";", NULL);
546   
547         header_cookie = apr_pstrcat(r->pool, header_cookie, tmp_pair, NULL);
548       }
549     }
550     if (strlen(header_cookie)) {
551       DBG(r, "ADD COOKIE to REQUEST HEADER:[%s]", header_cookie);
552       apr_table_add(r->headers_in, "Cookie", header_cookie);
553     }
554   
555     cookie->cookie_headers = (apr_array_header_t*)apr_table_elts(load_cookie_table);
556
557     if (apr_table_get(r->headers_in, "referer") == NULL) {
558       apr_table_setn(r->headers_in, 
559                      "referer",
560                      apr_table_get(load_cookie_table, REFERER_COOKIE_KEY));
561     }
562   
563     /*
564      * save cookie_id to request header.
565      */
566     apr_table_setn(r->headers_in, "CHXJ_COOKIE_ID", cookie->cookie_id);
567   }
568
569   DBG(r, "end   chxj_load_cookie()");
570   return cookie;
571
572
573 on_error0:
574
575   DBG(r, "end   chxj_load_cookie()");
576   return NULL;
577 }
578
579 static int
580 check_valid_cookie_attribute(request_rec *r, const char *value)
581 {
582   char *pstat;
583   char *pair;
584   char *first_pair;
585   char *domain_pair;
586   char *path_pair;
587   char *expire_pair;
588   char *secure_pair;
589   char *p;
590
591   DBG(r, "start check_valid_cookie_attribute() value:[%s]", value);
592
593   domain_pair = path_pair = expire_pair = secure_pair = NULL;
594   p = apr_pstrdup(r->pool, value);
595
596   /* pass first pair */
597   first_pair = apr_strtok(p, ";", &pstat);  
598
599   for (;;) {
600     pair = apr_strtok(NULL, ";", &pstat);
601     if (! pair) break;
602     pair = qs_trim_string(r->pool, pair);
603     if (STRNCASEEQ('d','D',"domain", pair, sizeof("domain")-1)) {
604       domain_pair = apr_pstrdup(r->pool, pair);
605     }
606     else if (STRNCASEEQ('p','P',"path", pair, sizeof("path")-1)) {
607       path_pair = apr_pstrdup(r->pool, pair);
608     }
609     else if (STRNCASEEQ('e','E',"expires", pair, sizeof("expires")-1)) {
610       expire_pair = apr_pstrdup(r->pool, pair);
611     }
612     else if (STRNCASEEQ('s','S',"secure", pair, sizeof("secure")-1)) {
613       secure_pair = apr_pstrdup(r->pool, pair);
614     }
615   }
616
617   if (domain_pair) {
618     if (!valid_domain(r, domain_pair)) {
619       DBG(r, "invalid domain. domain_pair:[%s]", domain_pair);
620       return CHXJ_FALSE;
621     }
622   }
623   if (path_pair) {
624     if (!valid_path(r, path_pair)) {
625       DBG(r, "invalid path. path_pair:[%s]", path_pair);
626       return CHXJ_FALSE;
627     }
628   }
629   if (expire_pair) {
630     if (!valid_expires(r, expire_pair)) {
631       DBG(r, "invalid expire. expire_pair:[%s]", expire_pair);
632       return CHXJ_FALSE;
633     }
634   }
635   if (secure_pair) {
636     if (!valid_secure(r, secure_pair)) {
637       DBG(r, "invalid secure. secure_pair:[%s]", secure_pair);
638       return CHXJ_FALSE;
639     }
640   }
641   DBG(r, "end check_valid_cookie_attribute() value:[%s]", value);
642   return CHXJ_TRUE;
643 }
644
645
646 static int
647 check_valid_cookie_attribute_expires_only(request_rec *r, const char *value)
648 {
649   char *pstat;
650   char *pair;
651   char *first_pair;
652   char *expire_pair = NULL;
653   char *p;
654
655   DBG(r, "REQ[%X] start check_valid_cookie_attribute_expires_only() value:[%s]", (unsigned int)(apr_size_t)r, value);
656
657   expire_pair = NULL;
658   p = apr_pstrdup(r->pool, value);
659
660   /* pass first pair */
661   first_pair = apr_strtok(p, ";", &pstat);
662
663   for (;;) {
664     pair = apr_strtok(NULL, ";", &pstat);
665     if (! pair) break;
666     pair = qs_trim_string(r->pool, pair);
667     if (STRNCASEEQ('e','E',"expires", pair, sizeof("expires")-1)) {
668       expire_pair = apr_pstrdup(r->pool, pair);
669     }
670   }
671
672   if (expire_pair) {
673     if (!valid_expires(r, expire_pair)) {
674       DBG(r, "REQ[%X] invalid expire. expire_pair:[%s]", (unsigned int)(apr_size_t)r, expire_pair);
675       return CHXJ_FALSE;
676     }
677   }
678   DBG(r, "REQ[%X] end check_valid_cookie_attribute_expires_only() value:[%s]", (unsigned int)(apr_size_t)r, value);
679   return CHXJ_TRUE;
680 }
681
682
683 static int
684 valid_domain(request_rec *r, const char *value)
685 {
686   int  len;
687   char *name;
688   char *val;
689   char *pstat;
690   char *p = apr_pstrdup(r->pool, value);
691   const char *host = apr_table_get(r->headers_in, HTTP_HOST);
692
693   DBG(r, "start valid_domain() value:[%s]", value);
694   DBG(r, "host:[%s]", host);
695   if (!host)
696     return CHXJ_TRUE;
697
698   name = apr_strtok(p,"=", &pstat);
699   name = qs_trim_string(r->pool, name);
700   val = apr_strtok(NULL, "=", &pstat);
701   val = qs_trim_string(r->pool, val);
702   len = strlen(host);
703   if (len) {
704     if (chxj_strcasenrcmp(r->pool, host, val, strlen(val))) {
705       DBG(r, "not match domain. host domain:[%s] vs value:[%s]", host, val);
706       return CHXJ_FALSE;
707     }
708   }
709   DBG(r, "end valid_domain() value:[%s]", value);
710   return CHXJ_TRUE;
711 }
712
713
714 static int
715 valid_path(request_rec *r, const char *value)
716 {
717   char *p = apr_pstrdup(r->pool, value);
718   char *uri;
719   char *tmp;
720   char *name;
721   char *val;
722   char *pstat;
723
724   DBG(r, "start valid_path() unparsed_uri:[%s] value:[%s]", r->unparsed_uri, value);
725   if (chxj_starts_with(r->unparsed_uri, "http://")) {
726     uri = strchr(&r->unparsed_uri[sizeof("http://")], '/');
727     if (uri != NULL) {
728       uri = apr_pstrdup(r->pool, uri);
729     }
730   }
731   else if (chxj_starts_with(r->unparsed_uri, "https://")) {
732     uri = strchr(&r->unparsed_uri[sizeof("https://")], '/');
733     if (uri != NULL) {
734       uri = apr_pstrdup(r->pool, uri);
735     }
736   }
737   else if (chxj_starts_with(r->unparsed_uri, "/")) {
738     uri = apr_pstrdup(r->pool, r->unparsed_uri);
739   }
740   else {
741     uri = apr_pstrdup(r->pool, "/");
742   }
743   
744   if ((tmp = strchr(uri, '?'))) {
745     *tmp = '\0';
746   }
747   DBG(r, "uri=[%s]", uri);
748   name = apr_strtok(p, "=", &pstat);
749   val = apr_strtok(NULL, "=", &pstat);
750   name = qs_trim_string(r->pool, name);
751   val = qs_trim_string(r->pool, val);
752   DBG(r, "name=[%s] val=[%s]", name, val);
753   
754   DBG(r, "val:[%s] vs uri:[%s]", val, uri);
755   if (! chxj_starts_with(uri, val)) {
756     DBG(r, "end valid_path() unparsed_uri:[%s] value:[%s] (false)", r->unparsed_uri, value);
757     return CHXJ_FALSE;
758   }
759   DBG(r, "end valid_path() unparsed_uri:[%s] value:[%s] (true)", r->unparsed_uri, value);
760   return CHXJ_TRUE;
761 }
762
763
764 static int
765 valid_expires(request_rec *r, const char *value)
766 {
767   char       *name;
768   char       *val;
769   char       *p = apr_pstrdup(r->pool, value);
770   char       *pstat;
771   apr_time_t expires;
772   apr_time_t now;
773
774   DBG(r, "start valid_expire() value:[%s]", value);
775   name = apr_strtok(p, "=", &pstat);
776   val  = apr_strtok(NULL, "=", &pstat);
777   DBG(r, "name=[%s] val=[%s]", name, val);
778   now = apr_time_now();
779   expires = chxj_parse_cookie_expires(val);
780   if (expires < now) {
781     DBG(r, "end valid_expire() value:[%s] (expired)", value);
782     return CHXJ_FALSE;
783   }
784   
785   DBG(r, "end valid_expire() value:[%s] (non expired)", value);
786   return CHXJ_TRUE;
787 }
788
789
790 static int
791 valid_secure(request_rec *r, const char *value)
792 {
793   const char *scheme;
794   DBG(r, "start valid_secure() value:[%s]", value);
795   scheme = chxj_apache_run_http_scheme(r);
796   if (strcasecmp("https", scheme)) {
797     DBG(r, "end valid_secure() value:[%s] (non secure)", value);
798     return CHXJ_FALSE;
799   }
800   DBG(r, "end valid_secure() value:[%s] (secure)", value);
801   return CHXJ_TRUE;
802 }
803
804
805 char *
806 chxj_add_cookie_parameter(request_rec *r, char *value, cookie_t *cookie)
807 {
808   char *qs;
809   char *dst;
810   char *name = "";
811
812   DBG(r, "start chxj_add_cookie_parameter() cookie_id=[%s]", (cookie) ? cookie->cookie_id : NULL);
813
814   dst = apr_pstrdup(r->pool, value);
815
816   if (!cookie)
817     goto on_error;
818
819   if (!cookie->cookie_id)
820     goto on_error;
821
822   if (chxj_cookie_check_host(r, value) != 0) {
823     DBG(r, "end chxj_add_cookie_parameter()(check host)");
824     goto on_error;
825   }
826
827   qs = strchr(dst, '#');
828   if (qs) {
829     name = apr_pstrdup(r->pool, qs);
830     *qs = 0;
831   }
832
833   qs = strchr(dst, '?');
834   if (qs) {
835     dst = apr_psprintf(r->pool, "%s&%s=%s%s", dst, CHXJ_COOKIE_PARAM, cookie->cookie_id, name);
836   }
837   else {
838     dst = apr_psprintf(r->pool, "%s?%s=%s%s", dst, CHXJ_COOKIE_PARAM, cookie->cookie_id, name);
839   }
840
841   DBG(r, "end   chxj_add_cookie_parameter() dst=[%s]", dst);
842
843   return dst;
844
845 on_error:
846   DBG(r, "end   chxj_add_cookie_parameter() (on_error)");
847   return dst;
848 }
849
850
851 char *
852 chxj_add_cookie_no_update_parameter(request_rec *r, char *value)
853 {
854   char *qs;
855   char *dst;
856   char *name = "";
857
858   DBG(r, "REQ[%X] start chxj_add_cookie_no_update_parameter()", (unsigned int)(apr_size_t)r);
859
860   if (! value || ! *value) {
861     DBG(r, "REQ[%X] end chxj_add_cookie_parameter()(void value)", (unsigned int)(apr_size_t)r);
862     return apr_pstrdup(r->pool, "");
863   }
864
865   dst = apr_pstrdup(r->pool, value);
866
867   if (chxj_cookie_check_host(r, value) != 0) {
868     DBG(r, "REQ[%X] end chxj_add_cookie_parameter()(check host)", (unsigned int)(apr_size_t)r);
869     goto on_error;
870   }
871
872   qs = strchr(dst, '#');
873   if (qs) {
874     name = apr_pstrdup(r->pool, qs);
875     *qs = 0;
876   }
877   dst = apr_psprintf(r->pool, "%s%c%s=true%s", dst, (strchr(dst,'?')) ? '&' : '?',CHXJ_COOKIE_NOUPDATE_PARAM, name);
878   DBG(r, "REQ[%X] end   chxj_add_cookie_no_update_parameter() dst=[%s]", (unsigned int)(apr_size_t)r, dst);
879   return dst;
880
881 on_error:
882   DBG(r, "REQ[%X] end   chxj_add_cookie_no_update_parameter() (on_error)", (unsigned int)(apr_size_t)r);
883   return dst;
884 }
885
886
887 int
888 chxj_cookie_check_host(request_rec *r, char *value) 
889 {
890   char *hostnm;
891   mod_chxj_config *dconf;
892
893   DBG(r, "REQ[%X] start chxj_cookie_check_host()", (unsigned int)(apr_size_t)r);
894   DBG(r, "REQ[%X] hostname=[%s] vs Location:[%s]", (unsigned int)(apr_size_t)r, r->hostname, value);
895
896   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
897
898   hostnm = s_get_hostname_from_url(r, value);
899   if (hostnm) {
900     if (dconf->allowed_cookie_domain) {
901       DBG(r, "REQ[%X] allowed_domain[%s] vs Location:[%s]", (unsigned int)(apr_size_t)r, dconf->allowed_cookie_domain, value);
902       if (chxj_strcasenrcmp(r->pool, hostnm, dconf->allowed_cookie_domain, strlen(dconf->allowed_cookie_domain))) {
903         DBG(r, "REQ[%X] end chxj_cookie_check_host() (false/allowed_domain)", (unsigned int)(apr_size_t)r);
904         return 1;
905       }
906       else {
907         DBG(r, "REQ[%X] end chxj_cookie_check_host() (true/allowed_domain)", (unsigned int)(apr_size_t)r);
908         return 0;
909       }
910     }
911     else {
912       if (strcasecmp(hostnm, r->hostname) == 0) {
913         DBG(r, "REQ[%X] end chxj_cookie_check_host() (true)", (unsigned int)(apr_size_t)r);
914         return 0;
915       }
916       else {
917         DBG(r, "REQ[%X] end chxj_cookie_check_host() (false)", (unsigned int)(apr_size_t)r);
918         return 1;
919       }
920     }
921   }
922   DBG(r, "REQ[%X] end chxj_cookie_check_host() (true)", (unsigned int)(apr_size_t)r);
923   return 0;
924 }
925
926
927 static char *
928 s_get_hostname_from_url(request_rec *r, char *value)
929 {
930   if (!value) 
931     return NULL; 
932
933   if (strncasecmp(value, "http://",  7) == 0 )
934     return s_cut_until_end_hostname(r, &value[7]);
935
936   if (strncasecmp(value, "https://", 8) == 0)
937     return s_cut_until_end_hostname(r, &value[8]);
938
939   return NULL;
940 }
941
942
943 static char *
944 s_cut_until_end_hostname(request_rec *r, char *value)
945 {
946   char *sp;
947   char *hostnm;
948
949   hostnm = sp = apr_pstrdup(r->pool, value);
950   for (;*sp; sp++) {
951     if (*sp == '/'|| *sp == '?' || *sp == ':') {
952       *sp = '\0';
953       break;
954     }
955   }
956   return hostnm;
957 }
958
959
960
961 void
962 chxj_delete_cookie(request_rec *r, const char *cookie_id)
963 {
964   int done_proc = 0;
965   mod_chxj_config *dconf;
966
967   DBG(r, "start chxj_delete_cookie()");
968   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
969
970 #if defined(USE_MYSQL_COOKIE)
971   if (IS_COOKIE_STORE_MYSQL(dconf->cookie_store_type)) {
972     if (! chxj_delete_cookie_mysql(r, dconf, cookie_id)) {
973       ERR(r, "failed: chxj_delete_cookie_mysql() cookie_id:[%s]", cookie_id);
974       DBG(r, "end   chxj_delete_cookie()");
975       return;
976     }
977     done_proc = 1;
978   }
979 #endif
980 #if defined(USE_MEMCACHE_COOKIE)
981   if (IS_COOKIE_STORE_MEMCACHE(dconf->cookie_store_type)) {
982     if (! chxj_delete_cookie_memcache(r, dconf, cookie_id)) {
983       ERR(r, "failed: chxj_delete_cookie_memcache() cookie_id:[%s]", cookie_id);
984       DBG(r, "end   chxj_delete_cookie()");
985       return;
986     }
987     done_proc = 1;
988   }
989 #endif
990   if (!done_proc || IS_COOKIE_STORE_DBM(dconf->cookie_store_type)) {
991     if (! chxj_delete_cookie_dbm(r, dconf, cookie_id)) {
992       ERR(r, "failed: chxj_delete_cookie_dbm() cookie_id:[%s]", cookie_id);
993       DBG(r, "end   chxj_delete_cookie()");
994       return;
995     }
996   }
997
998   DBG(r, "end   chxj_delete_cookie()");
999 }
1000
1001
1002 /*
1003  *
1004  */
1005 void
1006 chxj_save_cookie_expire(request_rec *r, cookie_t *cookie)
1007 {
1008   int done_proc = 0;
1009   mod_chxj_config         *dconf;
1010
1011   DBG(r, "start chxj_save_cookie_expire()");
1012   if (!cookie) {
1013     DBG(r, "cookie is NULL");
1014     return;
1015   }
1016   if (!cookie->cookie_id) {
1017     DBG(r, "cookie->cookie_id is NULL");
1018     return;
1019   }
1020
1021   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
1022   if (!dconf) {
1023     DBG(r, "dconf is NULL");
1024     return;
1025   }
1026
1027 #if defined(USE_MYSQL_COOKIE)
1028   if (IS_COOKIE_STORE_MYSQL(dconf->cookie_store_type)) {
1029     if (! chxj_save_cookie_expire_mysql(r, dconf, cookie->cookie_id)) {
1030       ERR(r, "failed: chxj_save_cookie_expire_mysql() cookie_id:[%s]", cookie->cookie_id);
1031       DBG(r, "end   chxj_save_cookie_expire()");
1032       return;
1033     }
1034     done_proc = 1;
1035   }
1036 #endif
1037 #if defined(USE_MEMCACHE_COOKIE)
1038   if (IS_COOKIE_STORE_MEMCACHE(dconf->cookie_store_type)) {
1039     if (! chxj_save_cookie_expire_memcache(r, dconf, cookie->cookie_id)) {
1040       ERR(r, "failed: chxj_save_cookie_expire_memcache() cookie_id:[%s]", cookie->cookie_id);
1041       DBG(r, "end   chxj_save_cookie_expire()");
1042       return;
1043     }
1044     done_proc = 1;
1045   }
1046 #endif
1047   if (!done_proc || IS_COOKIE_STORE_DBM(dconf->cookie_store_type)) {
1048     if (! chxj_save_cookie_expire_dbm(r, dconf, cookie->cookie_id)) {
1049       ERR(r, "failed: chxj_save_cookie_expire_dbm() cookie_id:[%s]", cookie->cookie_id);
1050       DBG(r, "end   chxj_save_cookie_expire()");
1051       return;
1052     }
1053   }
1054
1055   DBG(r, "end   chxj_save_cookie_expire()");
1056 }
1057
1058
1059 void
1060 chxj_delete_cookie_expire(request_rec *r, char *cookie_id)
1061 {
1062   int done_proc = 0;
1063   mod_chxj_config *dconf;
1064
1065   DBG(r, "start chxj_delete_cookie_expire()");
1066
1067   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
1068
1069 #if defined(USE_MYSQL_COOKIE)
1070   if (IS_COOKIE_STORE_MYSQL(dconf->cookie_store_type)) {
1071     if (! chxj_delete_cookie_expire_mysql(r, dconf, cookie_id)) {
1072       ERR(r, "failed: chxj_delete_cookie_expire_mysql() cookie_id:[%s]", cookie_id);
1073       return;
1074     }
1075     done_proc = 1;
1076   }
1077 #endif
1078 #if defined(USE_MEMCACHE_COOKIE)
1079   if (IS_COOKIE_STORE_MEMCACHE(dconf->cookie_store_type)) {
1080     if (!chxj_delete_cookie_expire_memcache(r, dconf, cookie_id)) {
1081       ERR(r, "failed: chxj_delete_cookie_expire_memcache() cookie_id:[%s]", cookie_id);
1082       return;
1083     } 
1084     done_proc = 1;
1085   }
1086 #endif
1087   if (!done_proc || IS_COOKIE_STORE_DBM(dconf->cookie_store_type)) {
1088     if (!chxj_delete_cookie_expire_dbm(r, dconf, cookie_id)) {
1089       ERR(r, "failed: chxj_delete_cookie_expire_dbm() cookie_id:[%s]", cookie_id);
1090       return;
1091     } 
1092   }
1093
1094   DBG(r, "end   chxj_delete_cookie_expire()");
1095 }
1096
1097
1098 void
1099 chxj_cookie_expire_gc(request_rec *r)
1100 {
1101   mod_chxj_config   *dconf;
1102   int done_proc = 0;
1103
1104   DBG(r, "start chxj_cookie_expire_gc()");
1105
1106   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
1107 #if defined(USE_MYSQL_COOKIE)
1108   if (IS_COOKIE_STORE_MYSQL(dconf->cookie_store_type)) {
1109     if (! chxj_cookie_expire_gc_mysql(r, dconf)) {
1110       ERR(r, "%s:%d end chxj_cookie_expire_gc(): failed: chxj_cookie_expire_gc_mysql()", APLOG_MARK);
1111       return;
1112     }
1113     done_proc = 1;
1114   } 
1115 #endif
1116 #if defined(USE_MEMCACHE_COOKIE)
1117   if (IS_COOKIE_STORE_MEMCACHE(dconf->cookie_store_type)) {
1118     if (! chxj_cookie_expire_gc_memcache(r, dconf)) {
1119       ERR(r, "%s:%d end chxj_cookie_expire_gc(): failed: chxj_cookie_expire_gc_memcache()", APLOG_MARK);
1120       return;
1121     }
1122     done_proc = 1;
1123   } 
1124 #endif
1125   if (!done_proc) {
1126     if (! chxj_cookie_expire_gc_dbm(r, dconf)) {
1127       ERR(r, "%s:%d end chxj_cookie_expire_gc(): failed: chxj_cookie_expire_gc_dbm()", APLOG_MARK);
1128       return;
1129     }
1130   }
1131   DBG(r, "end   chxj_cookie_expire_gc()");
1132 }
1133
1134 apr_time_t
1135 chxj_parse_cookie_expires(const char *s)
1136 {
1137   if (!s) return (apr_time_t)0;
1138   return apr_date_parse_rfc(s);
1139 }
1140
1141
1142 cookie_lock_t *
1143 __chxj_cookie_lock(request_rec *r, const char *filename, int line)
1144 {
1145   mod_chxj_config *dconf;
1146   apr_status_t rv;
1147   int done_proc = 0;
1148   cookie_lock_t *ret = NULL;
1149
1150   DBG(r, "start chxj_cookie_lock() call from %s:%d", filename, line);
1151   if ((rv = apr_proc_mutex_lock(global_cookie_mutex)) != APR_SUCCESS) {
1152     char errstr[255];
1153     ERR(r, "%s:%d apr_proc_mutex_lock failure.(%d:%s)", APLOG_MARK, rv, apr_strerror(rv, errstr, 255));
1154     return NULL;
1155   }
1156   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
1157 #if defined(USE_MYSQL_COOKIE)
1158   if (IS_COOKIE_STORE_MYSQL(dconf->cookie_store_type)) {
1159     if (! chxj_cookie_lock_mysql(r, dconf)) {
1160       ERR(r, "%s:%d end chxj_cookie_lock(): failed: chxj_cookie_lock_mysql()", APLOG_MARK);
1161       return NULL;
1162     }
1163     done_proc = 1;
1164     ret = apr_palloc(r->pool, sizeof(*ret));
1165     memset(ret, 0, sizeof(*ret));
1166   }
1167 #endif
1168 #if defined(USE_MEMCACHE_COOKIE)
1169   if (IS_COOKIE_STORE_MEMCACHE(dconf->cookie_store_type)) {
1170     if (! chxj_cookie_lock_memcache(r, dconf)) {
1171       ERR(r, "%s:%d end chxj_cookie_lock(): failed: chxj_cookie_lock_memcache()", APLOG_MARK);
1172       return NULL;
1173     }
1174     done_proc = 1;
1175     ret = apr_palloc(r->pool, sizeof(*ret));
1176     memset(ret, 0, sizeof(*ret));
1177   }
1178 #endif
1179   if (!done_proc) {
1180     if (!(ret = chxj_cookie_lock_dbm(r, dconf))) {
1181       ERR(r, "%s:%d end chxj_cookie_lock(): failed: chxj_cookie_lock_dbm()", APLOG_MARK);
1182       DBG(r, "end chxj_cookie_lock() call from %s:%d", filename, line);
1183       return NULL;
1184     }
1185   }
1186   DBG(r, "REQ:[%X] end chxj_cookie_lock() call from %s:%d", (unsigned int)(apr_size_t)r, filename, line);
1187   return ret;
1188 }
1189
1190
1191 int
1192 __chxj_cookie_unlock(request_rec *r, cookie_lock_t *lock, const char *filename, int line)
1193 {
1194   mod_chxj_config *dconf;
1195   int done_proc = 0;
1196   apr_status_t rv;
1197   int rtn = 1;
1198
1199   DBG(r, "start chxj_cookie_unlock() call from %s:%d", filename, line);
1200
1201   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
1202 #if defined(USE_MYSQL_COOKIE)
1203   if (IS_COOKIE_STORE_MYSQL(dconf->cookie_store_type)) {
1204     if (! chxj_cookie_unlock_mysql(r, dconf)) {
1205       ERR(r, "failed: chxj_cookie_unlock_mysql()");
1206       rtn = 0;
1207       goto end_chxj_cookie_unlock;
1208     }
1209     done_proc = 1;
1210   }
1211 #endif
1212 #if defined(USE_MEMCACHE_COOKIE)
1213   if (IS_COOKIE_STORE_MEMCACHE(dconf->cookie_store_type)) {
1214     if (! chxj_cookie_unlock_memcache(r, dconf)) {
1215       ERR(r, "failed: chxj_cookie_unlock_memcache()");
1216       rtn = 0;
1217       goto end_chxj_cookie_unlock;
1218     }
1219     done_proc = 1;
1220   }
1221 #endif
1222   if (!done_proc) {
1223     if (! chxj_cookie_unlock_dbm(r, lock, dconf)) {
1224       ERR(r, "failed: chxj_cookie_unlock_dbm()");
1225       rtn = 0;
1226       goto end_chxj_cookie_unlock;
1227     }
1228   }
1229 end_chxj_cookie_unlock:
1230   if ((rv = apr_proc_mutex_unlock(global_cookie_mutex)) != APR_SUCCESS) {
1231     char errstr[255];
1232     ERR(r, "%s:%d apr_proc_mutex_unlock failure.(%d:%s)", APLOG_MARK, rv, apr_strerror(rv, errstr, 255));
1233     DBG(r, "end chxj_cookie_unlock() call from %s:%d", filename, line);
1234     return 0;
1235   }
1236   DBG(r, "end chxj_cookie_unlock() call from %s:%d", filename, line);
1237
1238   return rtn;
1239 }
1240 /*
1241  * vim:ts=2 et
1242  */