OSDN Git Service

Added CHXJ:IF function to Emoji Only Mode.
[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
892   DBG(r, "REQ[%X] start chxj_cookie_check_host()", (unsigned int)(apr_size_t)r);
893   DBG(r, "hostname=[%s] vs Location:[%s]", r->hostname, value);
894
895   hostnm = s_get_hostname_from_url(r, value);
896   if (hostnm) {
897     if (strcasecmp(hostnm, r->hostname) == 0) {
898       DBG(r, "REQ[%X] end chxj_cookie_check_host() (true)", (unsigned int)(apr_size_t)r);
899       return 0;
900     }
901     else {
902       DBG(r, "REQ[%X] end chxj_cookie_check_host() (false)", (unsigned int)(apr_size_t)r);
903       return 1;
904     }
905   }
906   DBG(r, "REQ[%X] end chxj_cookie_check_host() (true)", (unsigned int)(apr_size_t)r);
907   return 0;
908 }
909
910
911 static char *
912 s_get_hostname_from_url(request_rec *r, char *value)
913 {
914   if (!value) 
915     return NULL; 
916
917   if (strncasecmp(value, "http://",  7) == 0 )
918     return s_cut_until_end_hostname(r, &value[7]);
919
920   if (strncasecmp(value, "https://", 8) == 0)
921     return s_cut_until_end_hostname(r, &value[8]);
922
923   return NULL;
924 }
925
926
927 static char *
928 s_cut_until_end_hostname(request_rec *r, char *value)
929 {
930   char *sp;
931   char *hostnm;
932
933   hostnm = sp = apr_pstrdup(r->pool, value);
934   for (;*sp; sp++) {
935     if (*sp == '/'|| *sp == '?' || *sp == ':') {
936       *sp = '\0';
937       break;
938     }
939   }
940   return hostnm;
941 }
942
943
944
945 void
946 chxj_delete_cookie(request_rec *r, const char *cookie_id)
947 {
948   int done_proc = 0;
949   mod_chxj_config *dconf;
950
951   DBG(r, "start chxj_delete_cookie()");
952   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
953
954 #if defined(USE_MYSQL_COOKIE)
955   if (IS_COOKIE_STORE_MYSQL(dconf->cookie_store_type)) {
956     if (! chxj_delete_cookie_mysql(r, dconf, cookie_id)) {
957       ERR(r, "failed: chxj_delete_cookie_mysql() cookie_id:[%s]", cookie_id);
958       DBG(r, "end   chxj_delete_cookie()");
959       return;
960     }
961     done_proc = 1;
962   }
963 #endif
964 #if defined(USE_MEMCACHE_COOKIE)
965   if (IS_COOKIE_STORE_MEMCACHE(dconf->cookie_store_type)) {
966     if (! chxj_delete_cookie_memcache(r, dconf, cookie_id)) {
967       ERR(r, "failed: chxj_delete_cookie_memcache() cookie_id:[%s]", cookie_id);
968       DBG(r, "end   chxj_delete_cookie()");
969       return;
970     }
971     done_proc = 1;
972   }
973 #endif
974   if (!done_proc || IS_COOKIE_STORE_DBM(dconf->cookie_store_type)) {
975     if (! chxj_delete_cookie_dbm(r, dconf, cookie_id)) {
976       ERR(r, "failed: chxj_delete_cookie_dbm() cookie_id:[%s]", cookie_id);
977       DBG(r, "end   chxj_delete_cookie()");
978       return;
979     }
980   }
981
982   DBG(r, "end   chxj_delete_cookie()");
983 }
984
985
986 /*
987  *
988  */
989 void
990 chxj_save_cookie_expire(request_rec *r, cookie_t *cookie)
991 {
992   int done_proc = 0;
993   mod_chxj_config         *dconf;
994
995   DBG(r, "start chxj_save_cookie_expire()");
996   if (!cookie) {
997     DBG(r, "cookie is NULL");
998     return;
999   }
1000   if (!cookie->cookie_id) {
1001     DBG(r, "cookie->cookie_id is NULL");
1002     return;
1003   }
1004
1005   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
1006   if (!dconf) {
1007     DBG(r, "dconf is NULL");
1008     return;
1009   }
1010
1011 #if defined(USE_MYSQL_COOKIE)
1012   if (IS_COOKIE_STORE_MYSQL(dconf->cookie_store_type)) {
1013     if (! chxj_save_cookie_expire_mysql(r, dconf, cookie->cookie_id)) {
1014       ERR(r, "failed: chxj_save_cookie_expire_mysql() cookie_id:[%s]", cookie->cookie_id);
1015       DBG(r, "end   chxj_save_cookie_expire()");
1016       return;
1017     }
1018     done_proc = 1;
1019   }
1020 #endif
1021 #if defined(USE_MEMCACHE_COOKIE)
1022   if (IS_COOKIE_STORE_MEMCACHE(dconf->cookie_store_type)) {
1023     if (! chxj_save_cookie_expire_memcache(r, dconf, cookie->cookie_id)) {
1024       ERR(r, "failed: chxj_save_cookie_expire_memcache() cookie_id:[%s]", cookie->cookie_id);
1025       DBG(r, "end   chxj_save_cookie_expire()");
1026       return;
1027     }
1028     done_proc = 1;
1029   }
1030 #endif
1031   if (!done_proc || IS_COOKIE_STORE_DBM(dconf->cookie_store_type)) {
1032     if (! chxj_save_cookie_expire_dbm(r, dconf, cookie->cookie_id)) {
1033       ERR(r, "failed: chxj_save_cookie_expire_dbm() cookie_id:[%s]", cookie->cookie_id);
1034       DBG(r, "end   chxj_save_cookie_expire()");
1035       return;
1036     }
1037   }
1038
1039   DBG(r, "end   chxj_save_cookie_expire()");
1040 }
1041
1042
1043 void
1044 chxj_delete_cookie_expire(request_rec *r, char *cookie_id)
1045 {
1046   int done_proc = 0;
1047   mod_chxj_config *dconf;
1048
1049   DBG(r, "start chxj_delete_cookie_expire()");
1050
1051   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
1052
1053 #if defined(USE_MYSQL_COOKIE)
1054   if (IS_COOKIE_STORE_MYSQL(dconf->cookie_store_type)) {
1055     if (! chxj_delete_cookie_expire_mysql(r, dconf, cookie_id)) {
1056       ERR(r, "failed: chxj_delete_cookie_expire_mysql() cookie_id:[%s]", cookie_id);
1057       return;
1058     }
1059     done_proc = 1;
1060   }
1061 #endif
1062 #if defined(USE_MEMCACHE_COOKIE)
1063   if (IS_COOKIE_STORE_MEMCACHE(dconf->cookie_store_type)) {
1064     if (!chxj_delete_cookie_expire_memcache(r, dconf, cookie_id)) {
1065       ERR(r, "failed: chxj_delete_cookie_expire_memcache() cookie_id:[%s]", cookie_id);
1066       return;
1067     } 
1068     done_proc = 1;
1069   }
1070 #endif
1071   if (!done_proc || IS_COOKIE_STORE_DBM(dconf->cookie_store_type)) {
1072     if (!chxj_delete_cookie_expire_dbm(r, dconf, cookie_id)) {
1073       ERR(r, "failed: chxj_delete_cookie_expire_dbm() cookie_id:[%s]", cookie_id);
1074       return;
1075     } 
1076   }
1077
1078   DBG(r, "end   chxj_delete_cookie_expire()");
1079 }
1080
1081
1082 void
1083 chxj_cookie_expire_gc(request_rec *r)
1084 {
1085   mod_chxj_config   *dconf;
1086   int done_proc = 0;
1087
1088   DBG(r, "start chxj_cookie_expire_gc()");
1089
1090   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
1091 #if defined(USE_MYSQL_COOKIE)
1092   if (IS_COOKIE_STORE_MYSQL(dconf->cookie_store_type)) {
1093     if (! chxj_cookie_expire_gc_mysql(r, dconf)) {
1094       ERR(r, "%s:%d end chxj_cookie_expire_gc(): failed: chxj_cookie_expire_gc_mysql()", APLOG_MARK);
1095       return;
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_expire_gc_memcache(r, dconf)) {
1103       ERR(r, "%s:%d end chxj_cookie_expire_gc(): failed: chxj_cookie_expire_gc_memcache()", APLOG_MARK);
1104       return;
1105     }
1106     done_proc = 1;
1107   } 
1108 #endif
1109   if (!done_proc) {
1110     if (! chxj_cookie_expire_gc_dbm(r, dconf)) {
1111       ERR(r, "%s:%d end chxj_cookie_expire_gc(): failed: chxj_cookie_expire_gc_dbm()", APLOG_MARK);
1112       return;
1113     }
1114   }
1115   DBG(r, "end   chxj_cookie_expire_gc()");
1116 }
1117
1118 apr_time_t
1119 chxj_parse_cookie_expires(const char *s)
1120 {
1121   if (!s) return (apr_time_t)0;
1122   return apr_date_parse_rfc(s);
1123 }
1124
1125
1126 cookie_lock_t *
1127 __chxj_cookie_lock(request_rec *r, const char *filename, int line)
1128 {
1129   mod_chxj_config *dconf;
1130   apr_status_t rv;
1131   int done_proc = 0;
1132   cookie_lock_t *ret = NULL;
1133
1134   DBG(r, "start chxj_cookie_lock() call from %s:%d", filename, line);
1135   if ((rv = apr_proc_mutex_lock(global_cookie_mutex)) != APR_SUCCESS) {
1136     char errstr[255];
1137     ERR(r, "%s:%d apr_proc_mutex_lock failure.(%d:%s)", APLOG_MARK, rv, apr_strerror(rv, errstr, 255));
1138     return NULL;
1139   }
1140   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
1141 #if defined(USE_MYSQL_COOKIE)
1142   if (IS_COOKIE_STORE_MYSQL(dconf->cookie_store_type)) {
1143     if (! chxj_cookie_lock_mysql(r, dconf)) {
1144       ERR(r, "%s:%d end chxj_cookie_lock(): failed: chxj_cookie_lock_mysql()", APLOG_MARK);
1145       return NULL;
1146     }
1147     done_proc = 1;
1148     ret = apr_palloc(r->pool, sizeof(*ret));
1149     memset(ret, 0, sizeof(*ret));
1150   }
1151 #endif
1152 #if defined(USE_MEMCACHE_COOKIE)
1153   if (IS_COOKIE_STORE_MEMCACHE(dconf->cookie_store_type)) {
1154     if (! chxj_cookie_lock_memcache(r, dconf)) {
1155       ERR(r, "%s:%d end chxj_cookie_lock(): failed: chxj_cookie_lock_memcache()", APLOG_MARK);
1156       return NULL;
1157     }
1158     done_proc = 1;
1159     ret = apr_palloc(r->pool, sizeof(*ret));
1160     memset(ret, 0, sizeof(*ret));
1161   }
1162 #endif
1163   if (!done_proc) {
1164     if (!(ret = chxj_cookie_lock_dbm(r, dconf))) {
1165       ERR(r, "%s:%d end chxj_cookie_lock(): failed: chxj_cookie_lock_dbm()", APLOG_MARK);
1166       DBG(r, "end chxj_cookie_lock() call from %s:%d", filename, line);
1167       return NULL;
1168     }
1169   }
1170   DBG(r, "REQ:[%X] end chxj_cookie_lock() call from %s:%d", (unsigned int)(apr_size_t)r, filename, line);
1171   return ret;
1172 }
1173
1174
1175 int
1176 __chxj_cookie_unlock(request_rec *r, cookie_lock_t *lock, const char *filename, int line)
1177 {
1178   mod_chxj_config *dconf;
1179   int done_proc = 0;
1180   apr_status_t rv;
1181   int rtn = 1;
1182
1183   DBG(r, "start chxj_cookie_unlock() call from %s:%d", filename, line);
1184
1185   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
1186 #if defined(USE_MYSQL_COOKIE)
1187   if (IS_COOKIE_STORE_MYSQL(dconf->cookie_store_type)) {
1188     if (! chxj_cookie_unlock_mysql(r, dconf)) {
1189       ERR(r, "failed: chxj_cookie_unlock_mysql()");
1190       rtn = 0;
1191       goto end_chxj_cookie_unlock;
1192     }
1193     done_proc = 1;
1194   }
1195 #endif
1196 #if defined(USE_MEMCACHE_COOKIE)
1197   if (IS_COOKIE_STORE_MEMCACHE(dconf->cookie_store_type)) {
1198     if (! chxj_cookie_unlock_memcache(r, dconf)) {
1199       ERR(r, "failed: chxj_cookie_unlock_memcache()");
1200       rtn = 0;
1201       goto end_chxj_cookie_unlock;
1202     }
1203     done_proc = 1;
1204   }
1205 #endif
1206   if (!done_proc) {
1207     if (! chxj_cookie_unlock_dbm(r, lock, dconf)) {
1208       ERR(r, "failed: chxj_cookie_unlock_dbm()");
1209       rtn = 0;
1210       goto end_chxj_cookie_unlock;
1211     }
1212   }
1213 end_chxj_cookie_unlock:
1214   if ((rv = apr_proc_mutex_unlock(global_cookie_mutex)) != APR_SUCCESS) {
1215     char errstr[255];
1216     ERR(r, "%s:%d apr_proc_mutex_unlock failure.(%d:%s)", APLOG_MARK, rv, apr_strerror(rv, errstr, 255));
1217     DBG(r, "end chxj_cookie_unlock() call from %s:%d", filename, line);
1218     return 0;
1219   }
1220   DBG(r, "end chxj_cookie_unlock() call from %s:%d", filename, line);
1221
1222   return rtn;
1223 }
1224 /*
1225  * vim:ts=2 et
1226  */