OSDN Git Service

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