OSDN Git Service

e6099464514a2a86bfb100dfcee3a05224ac35cc
[modchxj/mod_chxj.git] / src / chxj_mysql.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 #ifdef USE_MYSQL_COOKIE
18 #include "mod_chxj.h"
19 #include "chxj_cookie.h"
20 #include "chxj_url_encode.h"
21 #include "chxj_apply_convrule.h"
22
23 #include "ap_release.h"
24
25 #include "apu.h"
26 #include "apr_uuid.h"
27 #include "apr_md5.h"
28 #include "apr_base64.h"
29 #include "apr_uri.h"
30
31 #include <unistd.h>
32
33 /* for MySQL */
34 #include <mysql.h>
35 #include <errmsg.h>
36
37
38 #define CHXJ_MYSQL_RECONNECT_WAIT_TIME (1)
39 #define CHXJ_MYSQL_RECONNECT_COUNT (3)
40
41 typedef struct {
42   MYSQL * handle;
43   char host[255];
44   char username[255];
45   char database[255];
46   time_t last_used;
47   int reconnect;
48 } mysql_connection;
49
50 static mysql_connection connection = {NULL, "", "", "", 0, 0};
51
52
53 static apr_status_t
54 _mysql_cleanup(void *UNUSED(notused))
55 {
56   chxj_close_mysql_handle();
57   return APR_SUCCESS;
58 }
59
60 static apr_status_t
61 _mysql_cleanup_child(void *UNUSED(notused))
62 {
63   return APR_SUCCESS;
64 }
65
66
67 void
68 chxj_close_mysql_handle()
69 {
70   if (connection.handle) {
71     mysql_close(connection.handle);
72     connection.handle = NULL;
73   }
74 }
75
76 int
77 chxj_open_mysql_handle(request_rec *r, mod_chxj_config *m)
78 {
79   static MYSQL mysql_conn;
80   char query[MAX_STRING_LEN];
81
82   if (connection.handle && connection.reconnect == 0) {
83     if ((!m->mysql.host || (strcasecmp(m->mysql.host, "localhost") == 0)) && connection.host[0] == '\0'
84         &&  (!m->mysql.username || (strcmp(m->mysql.username, connection.username) == 0))) {
85
86       if (m->mysql.database && strcmp(m->mysql.database, connection.database) == 0) {
87         return 1;
88       }
89       else {
90         if (mysql_select_db(connection.handle,m->mysql.database) != 0) {
91           ERR(r, "MySQL ERROR: %s", mysql_error(connection.handle));
92           return 0;
93         }
94         else {
95           strcpy (connection.database, m->mysql.database);
96           return 1;
97         }
98       }
99     }
100     else {
101       chxj_close_mysql_handle();
102     }
103   }
104
105   connection.handle = mysql_init(&mysql_conn);
106   if (! connection.handle) {
107     ERR(r, "MySQL ERROR: %s", mysql_error(&mysql_conn));
108   }
109
110   if (!m->mysql.host || strcmp(m->mysql.host,"localhost") == 0) {
111     connection.host[0] = '\0';
112   } else {
113     strcpy(connection.host, m->mysql.host);
114   }
115
116   connection.handle = mysql_real_connect(&mysql_conn,connection.host,m->mysql.username,
117                                   m->mysql.password, NULL, m->mysql.port, m->mysql.socket_path, 0);
118   if (!connection.handle) {
119     ERR(r, "MySQL ERROR: %s. host:[%s] username:[%s] password:[%s] port:[%d] socket_path:[%s]", mysql_error(&mysql_conn), 
120        connection.host, 
121        m->mysql.username,
122        m->mysql.password,
123        m->mysql.port,
124        m->mysql.socket_path);
125     return 0;
126   }
127
128   if (!m->mysql.keep_alive) {
129     apr_pool_cleanup_register(r->pool, (void *)NULL, _mysql_cleanup, _mysql_cleanup_child);
130   }
131
132   if (m->mysql.username) {
133     strcpy(connection.username, m->mysql.username);
134   }
135   else {
136     connection.username[0] = '\0';
137   }
138
139   if (mysql_select_db(connection.handle,m->mysql.database) != 0) {
140     ERR(r, "MySQL ERROR: %s", mysql_error(connection.handle));
141     return 0;
142   }
143
144   strcpy (connection.database, m->mysql.database);
145   if (m->mysql.charset) {
146     apr_snprintf(query, sizeof(query)-1, "SET CHARACTER SET %s", m->mysql.charset);
147     if (mysql_query(connection.handle, query) != 0) {
148       ERR(r, "MySQL ERROR: %s: %s", mysql_error(connection.handle), r->uri);
149       return 0;
150     }
151   }
152
153   return 1;
154 }
155
156
157 int
158 chxj_mysql_exist_cookie_table(request_rec *r, mod_chxj_config *m)
159 {
160   MYSQL_RES *result;
161   char query[MAX_STRING_LEN];
162
163   apr_snprintf(query, sizeof(query)-1, "desc %s", m->mysql.tablename);
164   DBG(r, "start chxj_mysql_exist_cookie_table() query:[%s]", query);
165   do { 
166     if (!chxj_open_mysql_handle(r, m)) {
167       ERR(r, "failed chxj_open_mysql_handle() query:[%s]", query);
168       return 0;
169     }
170     connection.reconnect = 0;
171     if (mysql_query(connection.handle, query) != 0) {
172       if (mysql_errno(connection.handle) == CR_SERVER_GONE_ERROR) {
173         connection.reconnect = 1;
174         sleep(CHXJ_MYSQL_RECONNECT_WAIT_TIME);
175         continue;
176       }
177       WRN(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
178       return 0;
179     }
180   }
181   while(0);
182
183   result = mysql_store_result(connection.handle);
184   if (result) mysql_free_result(result);
185   
186   DBG(r, "end chxj_mysql_exist_cookie_table() query:[%s]", query);
187
188   return 1;
189 }
190
191
192 int
193 chxj_mysql_exist_cookie_table_expire(request_rec *r, mod_chxj_config *m)
194 {
195   MYSQL_RES *result;
196   char query[MAX_STRING_LEN];
197
198   apr_snprintf(query, sizeof(query)-1, "desc %s_expire", m->mysql.tablename);
199
200   DBG(r, "start chxj_mysql_exist_cookie_table_expire() query:[%s]", query);
201
202   do { 
203     if (!chxj_open_mysql_handle(r, m)) {
204       ERR(r, "failed chxj_open_mysql_handle() query:[%s]", query);
205       return 0;
206     }
207     connection.reconnect = 0;
208     if (mysql_query(connection.handle, query) != 0) {
209       if (mysql_errno(connection.handle) == CR_SERVER_GONE_ERROR) {
210         connection.reconnect = 1;
211         sleep(CHXJ_MYSQL_RECONNECT_WAIT_TIME);
212         continue;
213       }
214       WRN(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
215       return 0;
216     }
217   } while(0);
218
219   result = mysql_store_result(connection.handle);
220   if (result) mysql_free_result(result);
221
222   DBG(r, "end chxj_mysql_exist_cookie_table_expire() query:[%s]", query);
223
224   return 1;
225 }
226
227
228 int
229 chxj_mysql_create_cookie_table(request_rec *r, mod_chxj_config *m)
230 {
231   MYSQL_RES *result;
232   char query[MAX_STRING_LEN];
233
234   apr_snprintf(query, sizeof(query)-1, "CREATE TABLE %s  (cookie_id VARCHAR(%d) NOT NULL, data TEXT, PRIMARY KEY(cookie_id)) TYPE=InnoDB;",
235     m->mysql.tablename,
236     apr_base64_encode_len(APR_MD5_DIGESTSIZE) * 3);
237   DBG(r, "start chxj_mysql_create_cookie_table() query:[%s]", query);
238   do {
239     if (!chxj_open_mysql_handle(r, m)) {
240       ERR(r, "failed chxj_open_mysql_handle() query:[%s]", query);
241       return 0;
242     }
243     connection.reconnect = 0;
244     if (mysql_query(connection.handle, query) != 0) {
245       if (mysql_errno(connection.handle) == CR_SERVER_GONE_ERROR) {
246         connection.reconnect = 1;
247         sleep(CHXJ_MYSQL_RECONNECT_WAIT_TIME);
248         continue;
249       }
250       WRN(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
251       return 0;
252     }
253   }
254   while(0);
255
256   result = mysql_store_result(connection.handle);
257   if (result) mysql_free_result(result);
258   
259   DBG(r, "end chxj_mysql_create_cookie_table() query:[%s]", query);
260
261   return 1;
262 }
263
264 int
265 chxj_mysql_create_cookie_expire_table(request_rec *r, mod_chxj_config *m)
266 {
267   MYSQL_RES *result;
268   char query[MAX_STRING_LEN];
269
270   apr_snprintf(query, sizeof(query)-1, "CREATE TABLE %s_expire  (cookie_id VARCHAR(%d) NOT NULL, created_at DATETIME, PRIMARY KEY(cookie_id)) TYPE=InnoDB;",
271     m->mysql.tablename,
272     apr_base64_encode_len(APR_MD5_DIGESTSIZE) * 3);
273
274   DBG(r, "start chxj_mysql_create_cookie_expire_table() query:[%s]", query);
275
276   do {
277     if (!chxj_open_mysql_handle(r, m)) {
278       ERR(r, "failed chxj_open_mysql_handle() query:[%s]", query);
279       return 0;
280     }
281     connection.reconnect = 0;
282     if (mysql_query(connection.handle, query) != 0) {
283       if (mysql_errno(connection.handle) == CR_SERVER_GONE_ERROR) {
284         connection.reconnect = 1;
285         sleep(CHXJ_MYSQL_RECONNECT_WAIT_TIME);
286         continue;
287       }
288       WRN(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
289       return 0;
290     }
291   }
292   while(0);
293
294   result = mysql_store_result(connection.handle);
295   if (result) mysql_free_result(result);
296   
297   DBG(r, "end chxj_mysql_create_cookie_expire_table() query:[%s]", query);
298
299   return 1;
300 }
301
302
303 char *
304 chxj_mysql_get_cookie_from_cookie_id(request_rec *r, mod_chxj_config *m, const char *cookie_id)
305 {
306   MYSQL_RES *result;
307   char query[MAX_STRING_LEN];
308   char *retval = NULL;
309   apr_size_t clen = strlen(cookie_id);
310   char *sql_safe_cookie_id = apr_palloc(r->pool, clen*2+1);
311
312   mysql_escape_string(sql_safe_cookie_id,cookie_id,clen);
313
314   apr_snprintf(query, sizeof(query)-1, "SELECT data, length(data) FROM %s WHERE cookie_id = '%s' FOR UPDATE;", m->mysql.tablename, sql_safe_cookie_id);
315   DBG(r, "start chxj_mysql_get_cookie_from_cookie_id() query:[%s]", query);
316   do {
317     if (!chxj_open_mysql_handle(r, m)) {
318       ERR(r, "failed chxj_open_mysql_handle() query:[%s]", query);
319       return 0;
320     }
321     connection.reconnect = 0;
322     if (mysql_query(connection.handle, query) != 0) {
323       if (mysql_errno(connection.handle) == CR_SERVER_GONE_ERROR) {
324         connection.reconnect = 1;
325         sleep(CHXJ_MYSQL_RECONNECT_WAIT_TIME);
326         continue;
327       }
328       WRN(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
329       return NULL;
330     }
331   }
332   while(0);
333
334   result = mysql_store_result(connection.handle);
335   if (result && (mysql_num_rows(result) >= 1)) {
336     MYSQL_ROW data = mysql_fetch_row(result);
337     int len;
338     if (! data[0]) {
339       ERR(r, "MySQL cookie_id:[%s] has no valid cookie_id. %s", cookie_id, r->uri);
340       mysql_free_result(result);
341       return NULL;
342     }
343     len = atoi(data[1]);
344     retval = (char *) apr_palloc(r->pool, len + 1);
345     memcpy(retval, data[0], len);
346   }
347   if (result) mysql_free_result(result);
348   
349   DBG(r, "end chxj_mysql_get_cookie_from_cookie_id() query:[%s]", query);
350
351   return retval;
352 }
353
354
355 char *
356 chxj_mysql_get_cookie_expire_from_cookie_id(request_rec *r, mod_chxj_config *m, const char *cookie_id)
357 {
358   MYSQL_RES *result;
359   char query[MAX_STRING_LEN];
360   apr_size_t clen = strlen(cookie_id);
361   char *retval = NULL;
362   char *sql_safe_cookie_id = apr_palloc(r->pool, clen*2+1);
363
364   mysql_escape_string(sql_safe_cookie_id,cookie_id,clen);
365
366   apr_snprintf(query, sizeof(query)-1, "SELECT DATE_FORMAT(created_at, '%%Y%%m%%d%%H%%i%%s') FROM %s_expire WHERE cookie_id = '%s' FOR UPDATE;", 
367     m->mysql.tablename, sql_safe_cookie_id);
368
369   DBG(r, "start chxj_mysql_get_cookie_expire_from_cookie_id() query:[%s]", query);
370
371   do {
372     if (!chxj_open_mysql_handle(r, m)) {
373       ERR(r, "failed chxj_open_mysql_handle() query:[%s]", query);
374       return NULL;
375     }
376     connection.reconnect = 0;
377     if (mysql_query(connection.handle, query) != 0) {
378       if (mysql_errno(connection.handle) == CR_SERVER_GONE_ERROR) {
379         connection.reconnect = 1;
380         sleep(CHXJ_MYSQL_RECONNECT_WAIT_TIME);
381         continue;
382       }
383       WRN(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
384       return NULL;
385     }
386   }
387   while(0);
388
389   result = mysql_store_result(connection.handle);
390   if (result && (mysql_num_rows(result) >= 1)) {
391     MYSQL_ROW data = mysql_fetch_row(result);
392     if (! data[0]) {
393       ERR(r, "MySQL cookie_id:[%s] has no valid cookie_id. %s", cookie_id, r->uri);
394       mysql_free_result(result);
395       return NULL;
396     }
397     retval = (char *) apr_palloc(r->pool, 19 + 1);
398     memset(retval, 0, 19+1);
399     memcpy(retval, data[0], 19);
400   }
401   if (result) mysql_free_result(result);
402   
403   DBG(r, "end chxj_mysql_get_cookie_expire_from_cookie_id() query:[%s]", query);
404
405   return retval;
406 }
407
408
409 int
410 chxj_mysql_insert_or_update_cookie(request_rec *r, mod_chxj_config *m, const char *cookie_id, const char *data)
411 {
412   MYSQL_RES *result;
413   char query[MAX_STRING_LEN];
414   char *cid = ap_escape_logitem(r->pool, cookie_id);
415   char *cdt = ap_escape_logitem(r->pool, data);
416
417   DBG(r, "start chxj_mysql_insert_or_update_cookie()");
418   do {
419     if (!chxj_open_mysql_handle(r, m)) {
420       ERR(r, "failed chxj_open_mysql_handle()");
421       return 0;
422     }
423     connection.reconnect = 0;
424     apr_snprintf(query, sizeof(query)-1, "BEGIN;");
425     DBG(r, "query:[%s]", query);
426     if (mysql_query(connection.handle, query) != 0) {
427       if (mysql_errno(connection.handle) == CR_SERVER_GONE_ERROR) {
428         connection.reconnect = 1;
429         sleep(CHXJ_MYSQL_RECONNECT_WAIT_TIME);
430         continue;
431       }
432       ERR(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
433       return 0;
434     }
435   }
436   while(0);
437
438   apr_snprintf(query, sizeof(query)-1, "INSERT INTO %s (cookie_id, data) VALUES ('%s','%s');", m->mysql.tablename, cid, cdt);
439   DBG(r, "query:[%s]", query);
440   if (mysql_query(connection.handle, query) != 0) {
441     WRN(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
442     if (!chxj_mysql_get_cookie_from_cookie_id(r, m, cookie_id)) {
443       ERR(r, "failed chxj_mysql_get_cookie_from_cookie_id() cookie_id:[%s]", cookie_id);
444       return 0;
445     }
446     apr_snprintf(query, sizeof(query)-1, "UPDATE %s set data = '%s' WHERE cookie_id = '%s';", m->mysql.tablename, cdt, cid);
447     DBG(r, "query:[%s]", query);
448     if (mysql_query(connection.handle, query) != 0) {
449       ERR(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
450       chxj_mysql_rollback(r, m);
451       return 0;
452     }
453   }
454
455   apr_snprintf(query, sizeof(query)-1, "COMMIT;");
456   DBG(r, "query:[%s]", query);
457   if (mysql_query(connection.handle, query) != 0) {
458     ERR(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
459     chxj_mysql_rollback(r, m);
460     return 0;
461   }
462
463   result = mysql_store_result(connection.handle);
464   if (result) mysql_free_result(result);
465   
466   DBG(r, "end chxj_mysql_get_cookie_from_cookie_id()");
467
468   return 1;
469 }
470
471
472 int
473 chxj_mysql_insert_or_update_cookie_expire(request_rec *r, mod_chxj_config *m, const char *cookie_id)
474 {
475   MYSQL_RES *result;
476   char query[MAX_STRING_LEN];
477   char *cid = ap_escape_logitem(r->pool, cookie_id);
478
479   DBG(r, "start chxj_mysql_insert_or_update_cookie_expire()");
480
481   do {
482     if (!chxj_open_mysql_handle(r, m)) {
483       ERR(r, "failed chxj_open_mysql_handle()");
484       return 0;
485     }
486     connection.reconnect = 0; 
487     apr_snprintf(query, sizeof(query)-1, "BEGIN;");
488     DBG(r, "query:[%s]", query);
489     if (mysql_query(connection.handle, query) != 0) {
490       if (mysql_errno(connection.handle) == CR_SERVER_GONE_ERROR) {
491         connection.reconnect = 1;
492         sleep(CHXJ_MYSQL_RECONNECT_WAIT_TIME);
493         continue;
494       }
495       ERR(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
496       return 0;
497     }
498   }
499   while(0);
500
501   apr_snprintf(query, sizeof(query)-1, "INSERT INTO %s_expire (cookie_id, created_at) VALUES ('%s',localtime);", m->mysql.tablename, cid);
502   DBG(r, "query:[%s]", query);
503   if (mysql_query(connection.handle, query) != 0) {
504     WRN(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
505     if (!chxj_mysql_get_cookie_from_cookie_id(r, m, cookie_id)) {
506       ERR(r, "failed chxj_mysql_get_cookie_from_cookie_id() cookie_id:[%s]", cookie_id);
507       return 0;
508     }
509     apr_snprintf(query, sizeof(query)-1, "UPDATE %s_expire set created_at = localtime WHERE cookie_id = '%s';", m->mysql.tablename, cid);
510     DBG(r, "query:[%s]", query);
511     if (mysql_query(connection.handle, query) != 0) {
512       ERR(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
513       chxj_mysql_rollback(r, m);
514       return 0;
515     }
516   }
517
518   apr_snprintf(query, sizeof(query)-1, "COMMIT;");
519   DBG(r, "query:[%s]", query);
520   if (mysql_query(connection.handle, query) != 0) {
521     ERR(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
522     chxj_mysql_rollback(r, m);
523     return 0;
524   }
525
526   result = mysql_store_result(connection.handle);
527   if (result) mysql_free_result(result);
528   
529   DBG(r, "end chxj_mysql_insert_or_update_cookie_expire()");
530
531   return 1;
532 }
533
534
535 int
536 chxj_mysql_rollback(request_rec *r, mod_chxj_config *m)
537 {
538   char query[MAX_STRING_LEN];
539
540   DBG(r, "start chxj_mysql_rollback()");
541
542   apr_snprintf(query, sizeof(query)-1, "ROLLBACK;");
543
544   if (!chxj_open_mysql_handle(r, m)) {
545     return 1; /* TRUE */
546   }
547   if (mysql_query(connection.handle, query) != 0) {
548     ERR(r, "MySQL ERROR: %s: %s", mysql_error(connection.handle), r->uri);
549     return 0; /* FALSE */
550   }
551   DBG(r, "end chxj_mysql_rollback()");
552   return 1;
553 }
554
555 char *
556 chxj_mysql_load_cookie(request_rec *r, mod_chxj_config *m, const char *cookie_id)
557 {
558   MYSQL_RES *result;
559   char query[MAX_STRING_LEN];
560   apr_size_t clen = strlen(cookie_id);
561   char *retval = NULL;
562   char *sql_safe_cookie_id = apr_palloc(r->pool, clen*2+1);
563   
564   mysql_escape_string(sql_safe_cookie_id,cookie_id,clen);
565
566   apr_snprintf(query, sizeof(query)-1, "SELECT data, length(data) FROM %s WHERE cookie_id = '%s';", m->mysql.tablename, sql_safe_cookie_id);
567
568   DBG(r, "start chxj_mysql_load_cookie() query:[%s]", query);
569
570   do {
571     if (!chxj_open_mysql_handle(r, m)) {
572       ERR(r, "failed chxj_open_mysql_handle() query:[%s]", query);
573       return NULL;
574     }
575     connection.reconnect = 1;
576     if (mysql_query(connection.handle, query) != 0) {
577       if (mysql_errno(connection.handle) == CR_SERVER_GONE_ERROR) {
578         connection.reconnect = 1;
579         sleep(CHXJ_MYSQL_RECONNECT_WAIT_TIME);
580         continue;
581       }
582       WRN(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
583       return NULL;
584     }
585   }
586   while(0);
587
588   result = mysql_store_result(connection.handle);
589   if (result && (mysql_num_rows(result) >= 1)) {
590     MYSQL_ROW data = mysql_fetch_row(result);
591     int len;
592     if (! data[0]) {
593       ERR(r, "MySQL cookie_id:[%s] has no valid cookie_id. %s", cookie_id, r->uri);
594       mysql_free_result(result);
595       return NULL;
596     }
597     len = atoi(data[1]);
598     retval = (char *) apr_palloc(r->pool, len + 1);
599     memcpy(retval, data[0], len);
600   }
601   if (result) mysql_free_result(result);
602   
603   DBG(r, "end chxj_load_mysql_cookie() query:[%s]", query);
604
605   return retval;
606 }
607
608
609 char *
610 chxj_mysql_load_cookie_expire(request_rec *r, mod_chxj_config *m, const char *cookie_id)
611 {
612   MYSQL_RES *result;
613   char query[MAX_STRING_LEN];
614   char *retval = NULL;
615   apr_size_t clen = strlen(cookie_id);
616   char *sql_safe_cookie_id = apr_palloc(r->pool, clen*2+1);
617
618   mysql_escape_string(sql_safe_cookie_id,cookie_id,clen);
619
620   apr_snprintf(query, 
621                sizeof(query)-1, 
622                "SELECT DATE_FORMAT(created_at, '%%Y%%m%%d%%H%%i%%s') FROM %s_expire WHERE cookie_id = '%s';", 
623                m->mysql.tablename, 
624                sql_safe_cookie_id);
625
626   DBG(r, "start chxj_mysql_load_cookie_expire() query:[%s]", query);
627
628   do {
629     if (!chxj_open_mysql_handle(r, m)) {
630       ERR(r, "failed chxj_open_mysql_handle() query:[%s]", query);
631       return NULL;
632     }
633     connection.reconnect = 0;
634     if (mysql_query(connection.handle, query) != 0) {
635       if (mysql_errno(connection.handle) == CR_SERVER_GONE_ERROR) {
636         connection.reconnect = 1;
637         sleep(CHXJ_MYSQL_RECONNECT_WAIT_TIME);
638         continue;
639       }
640       WRN(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
641       return NULL;
642     }
643   }
644   while(0);
645
646   result = mysql_store_result(connection.handle);
647   if (result && (mysql_num_rows(result) >= 1)) {
648     MYSQL_ROW data = mysql_fetch_row(result);
649     if (! data[0]) {
650       ERR(r, "MySQL cookie_id:[%s] has no valid cookie_id. %s", cookie_id, r->uri);
651       mysql_free_result(result);
652       return NULL;
653     }
654     retval = (char *) apr_palloc(r->pool, 14 + 1);
655     memcpy(retval, data[0], 14);
656   }
657   if (result) mysql_free_result(result);
658   
659   DBG(r, "end chxj_mysql_load_cookie_expire() query:[%s]", query);
660
661   return retval;
662 }
663
664
665 int
666 chxj_mysql_delete_cookie(request_rec *r, mod_chxj_config *m, const char *cookie_id)
667 {
668   MYSQL_RES *result;
669   char query[MAX_STRING_LEN];
670   char *cid = ap_escape_logitem(r->pool, cookie_id);
671
672   DBG(r, "start chxj_mysql_delete_cookie() cookie_id:[%s]", cookie_id);
673
674   do {
675     if (!chxj_open_mysql_handle(r, m)) {
676       ERR(r, "failed chxj_open_mysql_handle()");
677       return 0;
678     }
679     connection.reconnect = 0;
680     apr_snprintf(query, sizeof(query)-1, "BEGIN;");
681     DBG(r, "query:[%s]", query);
682     if (mysql_query(connection.handle, query) != 0) {
683       if (mysql_errno(connection.handle) == CR_SERVER_GONE_ERROR) {
684         connection.reconnect = 1;
685         sleep(CHXJ_MYSQL_RECONNECT_WAIT_TIME);
686         continue;
687       }
688       ERR(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
689       return 0;
690     }
691   }
692   while(0);
693
694   if (!chxj_mysql_get_cookie_from_cookie_id(r, m, cookie_id)) {
695     ERR(r, "failed chxj_mysql_get_cookie_from_cookie_id() cookie_id:[%s]", cookie_id);
696     return 0;
697   }
698   apr_snprintf(query, sizeof(query)-1, "DELETE FROM %s WHERE cookie_id = '%s';", m->mysql.tablename, cid);
699   DBG(r, "query:[%s]", query);
700   if (mysql_query(connection.handle, query) != 0) {
701     ERR(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
702     chxj_mysql_rollback(r, m);
703     return 0;
704   }
705
706   apr_snprintf(query, sizeof(query)-1, "COMMIT;");
707   DBG(r, "query:[%s]", query);
708   if (mysql_query(connection.handle, query) != 0) {
709     ERR(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
710     chxj_mysql_rollback(r, m);
711     return 0;
712   }
713
714   result = mysql_store_result(connection.handle);
715   if (result) mysql_free_result(result);
716   
717   DBG(r, "end chxj_mysql_delete_cookie()");
718
719   return 1;
720 }
721
722
723 int
724 chxj_mysql_delete_cookie_expire(request_rec *r, mod_chxj_config *m, const char *cookie_id)
725 {
726   MYSQL_RES *result;
727   char query[MAX_STRING_LEN];
728   char *cid = ap_escape_logitem(r->pool, cookie_id);
729
730   DBG(r, "start chxj_mysql_delete_cookie_expire() cookie_id:[%s]", cookie_id);
731
732   do {
733     if (!chxj_open_mysql_handle(r, m)) {
734       ERR(r, "failed chxj_open_mysql_handle()");
735       return 0;
736     }
737     connection.reconnect = 0; 
738     apr_snprintf(query, sizeof(query)-1, "BEGIN;");
739     DBG(r, "query:[%s]", query);
740     if (mysql_query(connection.handle, query) != 0) {
741       if (mysql_errno(connection.handle) == CR_SERVER_GONE_ERROR) {
742         connection.reconnect = 1;
743         sleep(CHXJ_MYSQL_RECONNECT_WAIT_TIME);
744         continue;
745       }
746       ERR(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
747       return 0;
748     }
749   }
750   while(0);
751
752   if (!chxj_mysql_get_cookie_expire_from_cookie_id(r, m, cookie_id)) {
753     ERR(r, "failed chxj_mysql_get_cookie_expire_from_cookie_id() cookie_id:[%s]", cookie_id);
754     return 0;
755   }
756   apr_snprintf(query, sizeof(query)-1, "DELETE FROM %s_expire WHERE cookie_id = '%s';", m->mysql.tablename, cid);
757   DBG(r, "query:[%s]", query);
758   if (mysql_query(connection.handle, query) != 0) {
759     ERR(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
760     chxj_mysql_rollback(r, m);
761     return 0;
762   }
763
764   apr_snprintf(query, sizeof(query)-1, "COMMIT;");
765   DBG(r, "query:[%s]", query);
766   if (mysql_query(connection.handle, query) != 0) {
767     ERR(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
768     chxj_mysql_rollback(r, m);
769     return 0;
770   }
771
772   result = mysql_store_result(connection.handle);
773   if (result) mysql_free_result(result);
774   
775   DBG(r, "end chxj_mysql_delete_cookie_expire() cookie_id:[%s]", cookie_id);
776
777   return 1;
778 }
779
780 char *
781 chxj_mysql_get_timeout_localtime(request_rec *r, mod_chxj_config *m)
782 {
783   MYSQL_RES *result;
784   char query[MAX_STRING_LEN];
785   char *retval = NULL;
786
787   DBG(r, "start chxj_mysql_get_timeout_localtime()");
788   do {
789     if (!chxj_open_mysql_handle(r, m)) {
790       ERR(r, "failed chxj_open_mysql_handle()");
791       return 0;
792     }
793     connection.reconnect = 0;
794
795     apr_snprintf(query, sizeof(query)-1, "SELECT DATE_SUB(localtime, interval %ld second);",
796       (m->cookie_timeout == 0) ? DEFAULT_COOKIE_TIMEOUT : m->cookie_timeout);
797     DBG(r, "query:[%s]", query);
798     if (mysql_query(connection.handle, query) != 0) {
799       if (mysql_errno(connection.handle) == CR_SERVER_GONE_ERROR) {
800         connection.reconnect = 1;
801         sleep(CHXJ_MYSQL_RECONNECT_WAIT_TIME);
802         continue;
803       }
804       ERR(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
805       return NULL;
806     }
807   }
808   while(0);
809   result = mysql_store_result(connection.handle);
810   if (result && (mysql_num_rows(result) >= 1)) {
811     MYSQL_ROW data = mysql_fetch_row(result);
812     if (! data[0]) {
813       ERR(r, "MySQL ERROR: %s: %s", mysql_error(connection.handle), r->uri);
814       mysql_free_result(result);
815       return NULL;
816     }
817     retval = (char *) apr_palloc(r->pool, 19 + 1);
818     memset(retval, 0, 19+1);
819     memcpy(retval, data[0], 19);
820   }
821   if (result) mysql_free_result(result);
822    
823   DBG(r, "end chxj_mysql_get_timeout_localtime()");
824
825   return retval;
826 }
827
828 int
829 chxj_mysql_delete_expired_cookie(request_rec *r, mod_chxj_config *m)
830 {
831   MYSQL_RES *result;
832   char query[MAX_STRING_LEN];
833   char *timeout;
834   DBG(r, "start chxj_mysql_delete_expired_cookie()");
835
836   do {
837     if (!chxj_open_mysql_handle(r, m)) {
838       ERR(r, "failed chxj_open_mysql_handle()");
839       return 0;
840     }
841     connection.reconnect = 0;
842     apr_snprintf(query, sizeof(query)-1, "BEGIN;");
843     DBG(r, "query:[%s]", query);
844     if (mysql_query(connection.handle, query) != 0) {
845       if (mysql_errno(connection.handle) == CR_SERVER_GONE_ERROR) {
846         connection.reconnect = 1;
847         sleep(CHXJ_MYSQL_RECONNECT_WAIT_TIME);
848         continue;
849       }
850       ERR(r, "MySQL ERROR: %s: %s", mysql_error(connection.handle), r->uri);
851       return 0;
852     }
853   }
854   while (0);
855
856   timeout = chxj_mysql_get_timeout_localtime(r, m);
857   if (! timeout) {
858     ERR(r, "MySQL ERROR: %s: %s", mysql_error(connection.handle), r->uri);
859     return 0;
860   }
861
862   apr_snprintf(query, sizeof(query)-1, "SELECT * FROM %s_expire WHERE created_at <= '%s'", m->mysql.tablename, timeout);
863   DBG(r, "query:[%s]", query);
864   if (mysql_query(connection.handle, query) != 0) {
865     ERR(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
866     chxj_mysql_rollback(r, m);
867     return 0;
868   }
869   result = mysql_store_result(connection.handle);
870   if (result) mysql_free_result(result);
871   result = NULL;
872
873   /* delete from chxj_cookie */
874   apr_snprintf(query, sizeof(query)-1, "DELETE %s FROM %s, %s_expire WHERE %s_expire.created_at <= '%s' AND %s.cookie_id = %s_expire.cookie_id;", 
875      m->mysql.tablename, 
876      m->mysql.tablename,
877      m->mysql.tablename,
878      m->mysql.tablename,
879      timeout,
880      m->mysql.tablename,
881      m->mysql.tablename);
882   DBG(r, "query:[%s]", query);
883   if (mysql_query(connection.handle, query) != 0) {
884     ERR(r, "MySQL ERROR: %s: %s", mysql_error(connection.handle), r->uri);
885     chxj_mysql_rollback(r, m);
886     return 0;
887   }
888   result = mysql_store_result(connection.handle);
889   if (result) mysql_free_result(result);
890   result = NULL;
891
892   /* delete from chxj_cookie_expire */
893   apr_snprintf(query, sizeof(query)-1, "DELETE %s_expire FROM %s_expire WHERE %s_expire.created_at <= '%s';", 
894      m->mysql.tablename, 
895      m->mysql.tablename,
896      m->mysql.tablename,
897      timeout);
898   DBG(r, "query:[%s]", query);
899   if (mysql_query(connection.handle, query) != 0) {
900     ERR(r, "MySQL ERROR: %s: %s", mysql_error(connection.handle), r->uri);
901     chxj_mysql_rollback(r, m);
902     return 0;
903   }
904   result = mysql_store_result(connection.handle);
905   if (result) mysql_free_result(result);
906   result = NULL;
907
908   apr_snprintf(query, sizeof(query)-1, "COMMIT;");
909   DBG(r, "query:[%s]", query);
910   if (mysql_query(connection.handle, query) != 0) {
911     ERR(r, "MySQL WARN: %s: %s", mysql_error(connection.handle), r->uri);
912     chxj_mysql_rollback(r, m);
913     return 0;
914   }
915
916   result = mysql_store_result(connection.handle);
917   if (result) mysql_free_result(result);
918
919   DBG(r, "end chxj_mysql_delete_expired_cookie()");
920   return 1;
921 }
922
923
924 int
925 chxj_save_cookie_mysql(request_rec *r, mod_chxj_config *m, const char *cookie_id, const char *store_string)
926 {
927   if (! chxj_open_mysql_handle(r, m)) {
928     ERR(r, "Cannot open mysql connection");
929     return CHXJ_FALSE;
930   }
931
932   if (!chxj_mysql_exist_cookie_table(r, m)) {
933     DBG(r, "not found cookie table:[%s]", m->mysql.tablename);
934     if (!chxj_mysql_create_cookie_table(r, m)) {
935       ERR(r, "cannot create cookie table:[%s]", m->mysql.tablename);
936       return CHXJ_FALSE;
937     }
938   }
939   if (! chxj_mysql_insert_or_update_cookie(r, m, cookie_id, store_string)) {
940     ERR(r, "cannot store to cookie table:[%s]", m->mysql.tablename);
941     return CHXJ_FALSE;
942   }
943
944   /* *NEED NOT* close database. */
945   return CHXJ_TRUE;
946 }
947
948 int
949 chxj_update_cookie_mysql(request_rec *r, mod_chxj_config *m, const char *cookie_id, const char *store_string)
950 {
951   DBG(r, "start chxj_update_cookie_mysql() cookie_id:[%s]", cookie_id);
952   if (! chxj_open_mysql_handle(r, m)) {
953     ERR(r, "Cannot open mysql connection");
954     DBG(r, "end chxj_update_cookie_mysql() cookie_id:[%s]", cookie_id);
955     return CHXJ_FALSE;
956   }
957
958   if (!chxj_mysql_exist_cookie_table(r, m)) {
959     DBG(r, "not found cookie table:[%s]", m->mysql.tablename);
960     if (!chxj_mysql_create_cookie_table(r, m)) {
961       ERR(r, "cannot create cookie table:[%s]", m->mysql.tablename);
962       DBG(r, "end chxj_update_cookie_mysql() cookie_id:[%s]", cookie_id);
963       return CHXJ_FALSE;
964     }
965   }
966   if (! chxj_mysql_insert_or_update_cookie(r, m, cookie_id, store_string)) {
967     ERR(r, "cannot create cookie table:[%s]", m->mysql.tablename);
968     DBG(r, "end chxj_update_cookie_mysql() cookie_id:[%s]", cookie_id);
969     return CHXJ_FALSE;
970   }
971
972   /* *NEED NOT* close database. */
973   /* chxj_close_mysql_handle(); */
974   DBG(r, "end chxj_update_cookie_mysql() cookie_id:[%s]", cookie_id);
975   return CHXJ_TRUE;
976 }
977
978
979 char *
980 chxj_load_cookie_mysql(request_rec *r, mod_chxj_config *m, const char *cookie_id)
981 {
982   char *load_string;
983
984   DBG(r, "start chxj_load_cookie_mysql() cookie_id:[%s]", cookie_id);
985   if (! chxj_open_mysql_handle(r, m)) {
986     ERR(r, "Cannot open mysql connection");
987     DBG(r, "end   chxj_load_cookie_mysql() cookie_id:[%s]", cookie_id);
988     return NULL;
989   }
990
991   if (!chxj_mysql_exist_cookie_table(r, m)) {
992     DBG(r, "not found cookie table:[%s]", m->mysql.tablename);
993     if (!chxj_mysql_create_cookie_table(r, m)) {
994       ERR(r, "cannot create cookie table:[%s]", m->mysql.tablename);
995       DBG(r, "end   chxj_load_cookie_mysql() cookie_id:[%s]", cookie_id);
996       return NULL;
997     }
998   }
999   if (!(load_string = chxj_mysql_load_cookie(r, m, cookie_id))) {
1000     ERR(r, "not found cookie. cookie_id:[%s]", cookie_id);
1001     DBG(r, "end   chxj_load_cookie_mysql() cookie_id:[%s]", cookie_id);
1002     return NULL;
1003   }
1004
1005   /* *NEED NOT* close database. */
1006   /* chxj_close_mysql_handle(); */
1007   DBG(r, "end   chxj_load_cookie_mysql() cookie_id:[%s]", cookie_id);
1008   return load_string;
1009 }
1010
1011
1012 int
1013 chxj_delete_cookie_mysql(request_rec *r, mod_chxj_config *m, const char *cookie_id)
1014 {
1015   DBG(r, "start chxj_delete_cookie_mysql() cookie_id=[%s]", cookie_id);
1016   if (!chxj_mysql_delete_cookie(r, m, cookie_id)) {
1017     ERR(r, "failed: chxj_mysql_delete_cookie() cookie_id:[%s]", cookie_id);
1018     DBG(r, "end chxj_delete_cookie_mysql() cookie_id=[%s]", cookie_id);
1019     return CHXJ_FALSE;
1020   }
1021   DBG(r, "end chxj_delete_cookie_mysql() cookie_id=[%s]", cookie_id);
1022   return CHXJ_TRUE;
1023 }
1024
1025
1026 int
1027 chxj_save_cookie_expire_mysql(request_rec *r, mod_chxj_config *m, const char *cookie_id)
1028 {
1029   DBG(r, "start chxj_save_cookie_expire_mysql() cookie_id:[%s]", cookie_id);
1030   if (! chxj_open_mysql_handle(r, m)) {
1031     ERR(r, "Cannot open mysql connection");
1032     DBG(r, "end   chxj_save_cookie_expire_mysql()");
1033     return CHXJ_FALSE;
1034   }
1035
1036   if (!chxj_mysql_exist_cookie_table_expire(r, m)) {
1037     DBG(r, "not found cookie table:[%s_expire]", m->mysql.tablename);
1038     if (!chxj_mysql_create_cookie_expire_table(r, m)) {
1039       ERR(r, "cannot create cookie table:[%s_expire]", m->mysql.tablename);
1040       DBG(r, "end   chxj_save_cookie_expire_mysql()");
1041       return CHXJ_FALSE;
1042     }
1043   }
1044   if (! chxj_mysql_insert_or_update_cookie_expire(r, m, cookie_id)) {
1045     ERR(r, "cannot create cookie table:[%s_expire]", m->mysql.tablename);
1046     DBG(r, "end   chxj_save_cookie_expire_mysql()");
1047     return CHXJ_FALSE;
1048   }
1049
1050   /* *NEED NOT* close database. */
1051   /* chxj_close_mysql_handle(); */
1052
1053   DBG(r, "end   chxj_save_cookie_expire_mysql() cookie_id:[%s]", cookie_id);
1054   return CHXJ_TRUE;
1055 }
1056
1057
1058 int
1059 chxj_delete_cookie_expire_mysql(request_rec *r, mod_chxj_config *m, const char *cookie_id)
1060 {
1061   DBG(r, "start chxj_delete_cookie_expire_mysql() cookie_id:[%s]", cookie_id);
1062   if (!chxj_mysql_delete_cookie_expire(r, m, cookie_id)) {
1063     ERR(r, "failed: chxj_mysql_delete_cookie() cookie_id:[%s]", cookie_id);
1064     DBG(r, "end   chxj_delete_cookie_expire_mysql() cookie_id:[%s]", cookie_id);
1065     return CHXJ_FALSE;
1066   }
1067   DBG(r, "end   chxj_delete_cookie_expire_mysql() cookie_id:[%s]", cookie_id);
1068   return CHXJ_TRUE;
1069 }
1070
1071
1072 int
1073 chxj_cookie_expire_gc_mysql(request_rec *r, mod_chxj_config *m)
1074 {
1075   DBG(r, "start chxj_cookie_expire_gc_mysql()");
1076   if (!chxj_mysql_delete_expired_cookie(r, m)) {
1077     ERR(r, "failed: chxj_mysql_delete_expired_cookie()");
1078     DBG(r, "end   chxj_cookie_expire_gc_mysql()");
1079     return CHXJ_FALSE;
1080   }
1081   DBG(r, "end   chxj_cookie_expire_gc_mysql()");
1082   return CHXJ_TRUE;
1083 }
1084 #endif
1085 /*
1086  * vim:ts=2 et
1087  */