OSDN Git Service

*** empty log message ***
[modchxj/mod_chxj.git] / src / mod_chxj.c
1 /*
2  * Copyright (C) 2005 QSDN,Inc. All rights reserved.
3  * Copyright (C) 2005 Atsushi Konno All rights reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 #include <unistd.h>
18 #include <string.h>
19
20 #include "httpd.h"
21 #include "http_config.h"
22 #include "http_core.h"
23 #include "http_protocol.h"
24 #include "http_log.h"
25 #include "ap_config.h"
26 #include "apr_strings.h"
27 #include "util_filter.h"
28 #include "apr_buckets.h"
29 #include "apr_lib.h"
30 #include "apr_tables.h"
31 #include "apr_dso.h"
32 #include "apr_general.h"
33 #include "apr_pools.h"
34
35 #include "mod_chxj.h"
36 #include "chxj_encoding.h"
37 #include "qs_ignore_sp.h"
38 #include "qs_log.h"
39 #include "qs_malloc.h"
40 #include "qs_parse_attr.h"
41 #include "qs_parse_file.h"
42 #include "qs_parse_string.h"
43 #include "qs_parse_tag.h"
44 #include "chxj_load_device_data.h"
45 #include "chxj_load_emoji_data.h"
46 #include "chxj_specified_device.h"
47 #include "chxj_tag_util.h"
48 #include "chxj_xhtml_mobile_1_0.h"
49 #include "chxj_hdml.h"
50 #include "chxj_chtml10.h"
51 #include "chxj_chtml20.h"
52 #include "chxj_chtml30.h"
53 #include "chxj_jhtml.h"
54
55 #include "chxj_img_conv_format.h"
56 #include "chxj_qr_code.h"
57 #include "chxj_encoding.h"
58 #include "chxj_apply_convrule.h"
59 #include "chxj_cookie.h"
60 #include "chxj_url_encode.h"
61
62
63 #define CHXJ_VERSION_PREFIX PACKAGE_NAME "/"
64 #define CHXJ_VERSION        PACKAGE_VERSION
65
66 converter_t convert_routine[] = {
67   {
68     /* CHXJ_SPEC_UNKNOWN          */
69     .converter = NULL,
70     .encoder  = NULL,
71   },
72   {
73     /* CHXJ_SPEC_Chtml_1_0        */
74     .converter = chxj_exchange_chtml10,
75     .encoder  = chxj_encoding,
76   },
77   {
78     /* CHXJ_SPEC_Chtml_2_0        */
79     .converter = chxj_exchange_chtml20,
80     .encoder  = chxj_encoding,
81   },
82   {
83     /* CHXJ_SPEC_Chtml_3_0        */
84     .converter = chxj_exchange_chtml30,
85     .encoder  = chxj_encoding,
86   },
87   {
88     /* CHXJ_SPEC_Chtml_4_0        */
89     .converter = chxj_exchange_chtml30,
90     .encoder  = chxj_encoding,
91   },
92   {
93     /* CHXJ_SPEC_Chtml_5_0        */
94     .converter = chxj_exchange_chtml30,
95     .encoder  = chxj_encoding,
96   },
97   {
98     /* CHXJ_SPEC_XHtml_Mobile_1_0 */
99     .converter = chxj_exchange_xhtml_mobile_1_0,
100     .encoder  = chxj_encoding,
101   },
102   {
103     /* CHXJ_SPEC_Hdml             */
104     .converter = chxj_exchange_hdml,
105     .encoder  = chxj_encoding,
106   },
107   {
108     /* CHXJ_SPEC_Jhtml            */
109     .converter = chxj_exchange_jhtml,
110     .encoder  = chxj_encoding,
111   },
112   {
113     /* CHXJ_SPEC_HTML             */
114     .converter = NULL,
115     .encoder  = NULL,
116   },
117   {
118     NULL
119   }
120 };
121
122 static int chxj_convert_input_header(request_rec *r,chxjconvrule_entry* entryp);
123
124 /**
125  * Only when User-Agent is specified, the User-Agent header is camouflaged. 
126  *
127  * @param r   [i]
128  */
129 static apr_status_t 
130 chxj_headers_fixup(request_rec *r)
131 {
132   mod_chxj_config*    dconf; 
133   chxjconvrule_entry* entryp;
134   char*               user_agent;
135   device_table*       spec;
136
137   DBG(r, "start chxj_headers_fixup()");
138   dconf = ap_get_module_config(r->per_dir_config, &chxj_module);
139
140   user_agent = (char*)apr_table_get(r->headers_in, HTTP_USER_AGENT);
141   spec = chxj_specified_device(r, user_agent);
142
143   switch(spec->html_spec_type) {
144   case CHXJ_SPEC_Chtml_1_0:
145   case CHXJ_SPEC_Chtml_2_0:
146   case CHXJ_SPEC_Chtml_3_0:
147   case CHXJ_SPEC_Chtml_4_0:
148   case CHXJ_SPEC_Chtml_5_0:
149   case CHXJ_SPEC_XHtml_Mobile_1_0:
150   case CHXJ_SPEC_Hdml:
151   case CHXJ_SPEC_Jhtml:
152     entryp = chxj_apply_convrule(r, dconf->convrules);
153     if (! entryp) {
154       DBG(r, "end chxj_headers_fixup() no pattern");
155       return DECLINED;
156     }
157   
158     apr_table_setn(r->headers_in, 
159                    CHXJ_HTTP_USER_AGENT, 
160                    user_agent);
161   
162     if (entryp->user_agent)
163       apr_table_setn(r->headers_in, 
164                      HTTP_USER_AGENT, 
165                      entryp->user_agent);
166
167     chxj_convert_input_header(r,entryp);
168
169     break;
170   
171   default:
172     break;
173
174   }
175
176   DBG(r, "end chxj_headers_fixup()");
177
178   return DECLINED;
179 }
180
181
182 /**
183  * It converts it from CHTML into XXML corresponding to each model. 
184  *
185  * @param r   [i]
186  * @param src [i]   It is former HTML character string. 
187  * @param len [i/o] It is length of former HTML character string. 
188  */
189 static char* 
190 chxj_exchange(request_rec *r, const char** src, apr_size_t* len)
191 {
192   char*               user_agent;
193   char*               dst;
194   char*               tmp;
195   cookie_t*           cookie;
196   mod_chxj_config*    dconf; 
197   chxjconvrule_entry* entryp;
198
199   dst  = apr_pstrcat(r->pool, (char*)*src, NULL);
200
201   dconf = ap_get_module_config(r->per_dir_config, &chxj_module);
202
203
204   entryp = chxj_apply_convrule(r, dconf->convrules);
205
206   if (!entryp || !(entryp->action & CONVRULE_ENGINE_ON_BIT))
207     return (char*)*src;
208
209
210   /*------------------------------------------------------------------------*/
211   /* get UserAgent from http header                                         */
212   /*------------------------------------------------------------------------*/
213   if (entryp->user_agent)
214     user_agent = (char*)apr_table_get(r->headers_in, CHXJ_HTTP_USER_AGENT);
215   else
216     user_agent = (char*)apr_table_get(r->headers_in, HTTP_USER_AGENT);
217
218   DBG1(r,"User-Agent:[%s]", user_agent);
219   DBG(r, "start chxj_exchange()");
220   DBG1(r,"content type is %s", r->content_type);
221
222
223   if (*(char*)r->content_type == 't' 
224   && strncmp(r->content_type, "text/html",   9) != 0) {
225     DBG1(r,"content type is %s", r->content_type);
226     return (char*)*src;
227   }
228
229   device_table* spec = chxj_specified_device(r, user_agent);
230
231   /*
232    * save cookie.
233    */
234   cookie = NULL;
235   if (entryp->action & CONVRULE_COOKIE_ON_BIT) {
236     switch(spec->html_spec_type) {
237     case CHXJ_SPEC_Chtml_1_0:
238     case CHXJ_SPEC_Chtml_2_0:
239     case CHXJ_SPEC_Chtml_3_0:
240     case CHXJ_SPEC_Chtml_4_0:
241     case CHXJ_SPEC_Chtml_5_0:
242     case CHXJ_SPEC_Jhtml:
243       cookie = chxj_save_cookie(r);
244       break;
245     default:
246       break;
247     }
248   }
249
250   if (!r->header_only) {
251
252     tmp = NULL;
253     if (convert_routine[spec->html_spec_type].encoder)
254       tmp = convert_routine[spec->html_spec_type].encoder(r, 
255                                                           *src, 
256                                                           (apr_size_t*)len);
257
258     if (convert_routine[spec->html_spec_type].converter) {
259       if (tmp)
260         dst = convert_routine[spec->html_spec_type].converter(r, 
261                                                               spec, 
262                                                               tmp, 
263                                                               *len, 
264                                                               len, 
265                                                               entryp, 
266                                                               cookie);
267       else
268         dst = convert_routine[spec->html_spec_type].converter(r,
269                                                               spec, 
270                                                               tmp, 
271                                                               *len, 
272                                                               len, 
273                                                               entryp, 
274                                                               cookie);
275     }
276   }
277
278   if (*len == 0) {
279     dst = apr_psprintf(r->pool, "\n");
280     *len = 1;
281   }
282   dst[*len] = 0;
283
284   DBG(r, "end chxj_exchange()");
285
286   return dst;
287 }
288
289
290 /**
291  * It converts it from HEADER.
292  *
293  * @param r   [i]
294  */
295 static int
296 chxj_convert_input_header(request_rec *r,chxjconvrule_entry* entryp) 
297 {
298
299   char*      buff;
300   char*      buff_pre;
301   apr_size_t urilen;
302   char*      result;
303   char*      pair;
304   char*      name;
305   char*      value;
306   char*      pstate;
307   char*      vstate;
308   cookie_t*  cookie;
309   int        no_update_flag = 0;
310
311   DBG(r, "start chxj_convert_input_header()");
312
313   if (! r->args) {
314     DBG(r, "r->args=[null]");
315     DBG(r, "end   chxj_convert_input_header()");
316     return 0;
317   }
318   urilen = strlen(r->args);
319
320   result = qs_alloc_zero_byte_string(r);
321
322   buff_pre = apr_pstrdup(r->pool, r->args);
323
324   for (;;) {
325     char* pair_sv;
326
327     pair = apr_strtok(buff_pre, "&", &pstate);
328     if (pair == NULL)
329       break;
330
331     buff_pre = NULL;
332
333     pair_sv = apr_pstrdup(r->pool, pair);
334
335     name  = apr_strtok(pair, "=", &vstate);
336     value = apr_strtok(NULL, "=", &vstate);
337     if (strcasecmp(name, CHXJ_COOKIE_NOUPDATE_PARAM) == 0) {
338       DBG(r, "found cookie no update parameter");
339       no_update_flag++;
340     }
341   }
342
343   buff = apr_pstrdup(r->pool, r->args);
344   DBG1(r, "r->args=[%s]", buff);
345
346   /* _chxj_dmy */
347   /* _chxj_c_ */
348   /* _chxj_r_ */
349   /* _chxj_s_ */
350   for (;;) {
351     char* pair_sv;
352
353     pair = apr_strtok(buff, "&", &pstate);
354     if (pair == NULL)
355       break;
356
357     buff = NULL;
358
359     pair_sv = apr_pstrdup(r->pool, pair);
360
361     name  = apr_strtok(pair, "=", &vstate);
362     value = apr_strtok(NULL, "=", &vstate);
363     if (strncasecmp(name, "_chxj", 5) != 0) {
364       if (strlen(result) != 0) 
365         result = apr_pstrcat(r->pool, result, "&", NULL);
366
367       if (strcasecmp(entryp->encoding, "NONE") != 0 && value && strlen(value)) {
368         apr_size_t dlen;
369         char* dvalue;
370
371         dlen   = strlen(value);
372         value = chxj_url_decode(r, value);
373         DBG1(r, "************ before encoding[%s]", value);
374
375         dvalue = chxj_rencoding(r, value, &dlen);
376         dvalue = chxj_url_encode(r, dvalue);
377
378         DBG1(r, "************ after encoding[%s]", dvalue);
379
380         result = apr_pstrcat(r->pool, result, name, "=", dvalue, NULL);
381       }
382       else {
383         if (strcmp(name, pair_sv) != 0)
384           result = apr_pstrcat(r->pool, result, name, "=", value, NULL);
385         else
386           result = apr_pstrcat(r->pool, result, name, NULL);
387       }
388     }
389     else
390     if (strncasecmp(name, "_chxj_c_", 8) == 0 
391     ||  strncasecmp(name, "_chxj_r_", 8) == 0
392     ||  strncasecmp(name, "_chxj_s_", 8) == 0) {
393       if (value == NULL)
394         continue;
395
396       if (strlen(value) == 0)
397         continue;
398
399       if (strlen(result) != 0)
400         result = apr_pstrcat(r->pool, result, "&", NULL);
401
402       result = apr_pstrcat(r->pool, result, &name[8], "=", value, NULL);
403     }
404     else
405     if (strcasecmp(name, CHXJ_COOKIE_PARAM) == 0) {
406       DBG1(r, "found cookie parameter[%s]", value);
407       DBG(r, "call start chxj_load_cookie()");
408       cookie = chxj_load_cookie(r, value);
409       DBG(r, "call end   chxj_load_cookie()");
410       if (! no_update_flag) {
411         chxj_update_cookie(r, cookie);
412       }
413     }
414   }
415   r->args = result;
416
417   DBG1(r, "result r->args=[%s]", r->args);
418   DBG(r, "end   chxj_convert_input_header()");
419   return 0;
420 }
421
422
423 /**
424  * It converts it from POSTDATA .
425  *
426  * @param r   [i]
427  * @param src [i]   It is POSTDATA character string.
428  * @param len [i/o] It is length of former HTML character string.
429  */
430 static char*
431 chxj_input_convert(
432   request_rec*        r, 
433   const char**        src, 
434   apr_size_t*         len, 
435   chxjconvrule_entry* entryp)
436 {
437   char* pair;
438   char* name;
439   char* value;
440   char* pstate;
441   char* vstate;
442   char* s;
443   char* result;
444   cookie_t* cookie;
445   char* buff_pre;
446   int   no_update_flag = 0;
447
448   s        = apr_pstrdup(r->pool, *src);
449   buff_pre = apr_pstrdup(r->pool, *src);
450
451   result = qs_alloc_zero_byte_string(r);
452
453   DBG1(r, "BEFORE input convert source = [%s]", s);
454
455   for (;;) {
456     char* pair_sv;
457
458     pair = apr_strtok(buff_pre, "&", &pstate);
459     if (pair == NULL)
460       break;
461
462     buff_pre = NULL;
463
464     pair_sv = apr_pstrdup(r->pool, pair);
465
466     name  = apr_strtok(pair, "=", &vstate);
467     value = apr_strtok(NULL, "=", &vstate);
468     if (strcasecmp(name, CHXJ_COOKIE_NOUPDATE_PARAM) == 0) {
469       DBG(r, "found cookie no update parameter");
470       no_update_flag++;
471     }
472   }
473
474   /* _chxj_dmy */
475   /* _chxj_c_ */
476   /* _chxj_r_ */
477   /* _chxj_s_ */
478   for (;;) {
479     pair = apr_strtok(s, "&", &pstate);
480     if (pair == NULL)
481       break;
482     s = NULL;
483
484     name  = apr_strtok(pair, "=", &vstate);
485     value = apr_strtok(NULL, "=", &vstate);
486     if (strncasecmp(name, "_chxj", 5) != 0) {
487       if (strlen(result) != 0) 
488         result = apr_pstrcat(r->pool, result, "&", NULL);
489
490       if (strcasecmp(entryp->encoding, "NONE") != 0 
491       &&  value && strlen(value)) {
492         apr_size_t dlen;
493         char*      dvalue;
494
495         dlen   = strlen(value);
496         value = chxj_url_decode(r, value);
497         DBG1(r, "************ before encoding[%s]", value);
498
499         dvalue = chxj_rencoding(r, value, &dlen);
500         dvalue = chxj_url_encode(r,dvalue);
501
502         DBG1(r, "************ after encoding[%s]", dvalue);
503
504         result = apr_pstrcat(r->pool, result, name, "=", dvalue, NULL);
505
506       }
507       else {
508         result = apr_pstrcat(r->pool, result, name, "=", value, NULL);
509       }
510     }
511     else
512     if (strncasecmp(name, "_chxj_c_", 8) == 0 
513     ||  strncasecmp(name, "_chxj_r_", 8) == 0
514     ||  strncasecmp(name, "_chxj_s_", 8) == 0) {
515       if (value == NULL)
516         continue;
517
518       if (strlen(value) == 0)
519         continue;
520
521       if (strlen(result) != 0)
522         result = apr_pstrcat(r->pool, result, "&", NULL);
523
524       if (strcasecmp(entryp->encoding, "NONE") != 0 && value && strlen(value)) {
525         apr_size_t dlen;
526         char*      dvalue;
527
528         dlen   = strlen(value);
529         value = chxj_url_decode(r, value);
530         DBG1(r, "************ before encoding[%s]", value);
531
532         dvalue = chxj_rencoding(r, value, &dlen);
533         dvalue = chxj_url_encode(r,dvalue);
534
535         DBG1(r, "************ after encoding[%s]", dvalue);
536
537         result = apr_pstrcat(r->pool, result, &name[8], "=", dvalue, NULL);
538
539       }
540       else {
541         result = apr_pstrcat(r->pool, result, &name[8], "=", value, NULL);
542       }
543     }
544     else
545     if (strcasecmp(name, CHXJ_COOKIE_PARAM) == 0) {
546       DBG1(r, "found cookie parameter[%s]", value);
547       DBG(r, "call start chxj_load_cookie()");
548       cookie = chxj_load_cookie(r, value);
549       DBG(r, "call end   chxj_load_cookie()");
550       if (! no_update_flag) {
551         chxj_update_cookie(r, cookie);
552       }
553     }
554   }
555   *len = strlen(result);
556
557   DBG1(r, "AFTER input convert result = [%s]", result);
558
559   return result;
560 }
561
562
563 /**
564  * The received data is returned to the filter.
565  *
566  * @param f    [i/o] It is a filter. 
567  * @param data [i]   It is data returned to the filter. 
568  * @param len  [i]   It is length of the data returned to the filter. 
569  */
570 static apr_status_t 
571 pass_data_to_filter(ap_filter_t *f, const char *data, 
572                                         apr_size_t len)
573 {
574   request_rec*        r = f->r;
575   conn_rec*           c = r->connection;
576   apr_status_t        rv;
577   apr_bucket_brigade* bb;
578   apr_bucket*         b;
579
580   DBG(r, "start pass_data_to_filter()");
581
582   bb = apr_brigade_create(r->pool, c->bucket_alloc);
583   b  = apr_bucket_transient_create(data, len, c->bucket_alloc);
584
585   APR_BRIGADE_INSERT_TAIL(bb, b);
586   b = apr_bucket_eos_create(f->c->bucket_alloc);
587   APR_BRIGADE_INSERT_TAIL(bb, b);
588
589   rv = ap_pass_brigade(f->next, bb);
590   if (rv != APR_SUCCESS) {
591     DBG(r, "ap_pass_brigade()");
592     return rv;
593   }
594
595   DBG(r, "end pass_data_to_filter()");
596
597   return rv;
598 }
599
600
601 /**
602  * It is the main loop of the output filter. 
603  *
604  * @param f   [i/o] It is a filter.
605  * @param bb  [i]   
606  */
607 static apr_status_t 
608 chxj_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
609 {
610   request_rec*        r;
611   apr_status_t        rv;
612   apr_bucket*         b;
613   const char*         data;
614   char*               contentLength;
615   apr_size_t          len;
616   mod_chxj_ctx*       ctx;
617   cookie_t*           cookie;
618   char*               location_header;
619   mod_chxj_config*    dconf;
620   chxjconvrule_entry* entryp;
621
622
623
624   DBG(f->r, "start of chxj_output_filter()");
625
626   r  = f->r;
627   rv = APR_SUCCESS;
628
629   if (!f->ctx) {
630     if ((f->r->proto_num >= 1001) 
631     &&  !f->r->main 
632     &&  !f->r->prev) 
633       f->r->chunked = 1;
634   }
635
636   dconf  = ap_get_module_config(r->per_dir_config, &chxj_module);
637   entryp = chxj_apply_convrule(r, dconf->convrules);
638
639
640   for (b = APR_BRIGADE_FIRST(bb);
641        b != APR_BRIGADE_SENTINEL(bb); 
642        b = APR_BUCKET_NEXT(b)) {
643
644     if (APR_BUCKET_IS_EOS(b)) {
645
646       DBG(r, "eos");
647       /*----------------------------------------------------------------------*/
648       /* End Of File                                                          */
649       /*----------------------------------------------------------------------*/
650       if (f->ctx) {
651
652         ctx = (mod_chxj_ctx*)f->ctx;
653
654         DBG1(r, "content_type=[%s]", r->content_type);
655
656         if (r->content_type 
657         && *(char*)r->content_type == 't' 
658         && strncmp(r->content_type, "text/html",   9) == 0) {
659
660           if (ctx->len) {
661             char* tmp;
662
663             tmp = apr_palloc(r->pool, ctx->len + 1);
664
665             memset(tmp, 0, ctx->len + 1);
666             memcpy(tmp, ctx->buffer, ctx->len);
667
668 #if 0
669             DBG2(r, "input data=[%s] len=[%d]", tmp, ctx->len);
670 #endif
671
672             ctx->buffer = chxj_exchange(r, 
673                                         (const char**)&tmp, 
674                                         (apr_size_t*)&ctx->len);
675
676 #if 0
677             DBG2(r, "output data=[%.*s]", ctx->len,ctx->buffer);
678 #endif
679           }
680           else {
681             ctx->buffer = apr_psprintf(r->pool, "\n");
682             ctx->len += 1;
683             ctx->buffer = chxj_exchange(r, 
684                                         (const char**)&ctx->buffer, 
685                                         (apr_size_t*)&ctx->len);
686
687           }
688         }
689         if (r->content_type
690         && *(char*)r->content_type == 't'
691         && strncmp(r->content_type, "text/xml",   8) == 0) {
692           DBG(r, "text/XML");
693
694           Doc       doc;
695           Node*     root;
696           Node*     child;
697           qr_code_t qrcode;
698           int       sts;
699       
700           memset(&doc,    0, sizeof(Doc));
701           memset(&qrcode, 0, sizeof(qr_code_t));
702           doc.r = r;
703           doc.parse_mode  = PARSE_MODE_CHTML;
704           qrcode.doc      = &doc;
705           qrcode.r        = r;
706       
707           qs_init_malloc(&doc);
708       
709           root = qs_parse_string(&doc, ctx->buffer, ctx->len);
710
711           sts = 0;
712           for (child = qs_get_child_node(&doc,root);
713                child ;
714                child = qs_get_next_node(&doc,child)) {
715         
716             char* name;
717         
718             name = qs_get_node_name(&doc,child);
719         
720             if (strcasecmp("qrcode",name) == 0) {
721               sts++;
722               break;
723             }
724           }
725           qs_all_free(&doc,QX_LOGMARK);
726           if (sts) {
727             r->handler = apr_psprintf(r->pool, "chxj-qrcode");
728             chxj_qrcode_node_to_qrcode(&qrcode, root);
729             sts = chxj_qrcode_create_image_data(&qrcode, &ctx->buffer, &ctx->len);
730             if (sts != OK) {
731               ERR(r, "qrcode create failed.");
732               return sts;
733             }
734             r->content_type = apr_psprintf(r->pool, "image/jpg");
735           }
736         }
737
738         if (r->content_type 
739         && *(char*)r->content_type == 'i' 
740         && strncmp(r->content_type, "image/", 6) == 0) {
741           if (ctx->len) {
742             char* tmp;
743
744             tmp = apr_palloc(r->pool, ctx->len + 1);
745
746             memset(tmp, 0, ctx->len + 1);
747             memcpy(tmp, ctx->buffer, ctx->len);
748
749 #if 0
750             DBG1(r, "input data=[%s]", tmp);
751 #endif
752
753
754             ctx->buffer = 
755               chxj_exchange_image(r, 
756                                   (const char**)&tmp,
757                                   (apr_size_t*)&ctx->len);
758
759             if (ctx->buffer == NULL)
760               ctx->buffer = tmp;
761
762 #if 0
763             DBG2(r, "output data=[%.*s]", ctx->len,ctx->buffer);
764 #endif
765           }
766         }
767
768         contentLength = apr_psprintf(r->pool, "%d", ctx->len);
769         apr_table_setn(r->headers_out, "Content-Length", contentLength);
770         
771         if (ctx->len > 0) {
772           rv = pass_data_to_filter(f, 
773                                    (const char*)ctx->buffer, 
774                                    (apr_size_t)ctx->len);
775         }
776         f->ctx = NULL;
777
778         return rv;
779       }
780       else {
781         DBG1(r, " SAVE COOKIE[%x]", entryp->action);
782
783         /*
784          * save cookie.
785          */
786         if (entryp->action & CONVRULE_COOKIE_ON_BIT) {
787           DBG(r, "entryp->action == COOKIE_ON_BIT");
788           char* user_agent = (char*)apr_table_get(r->headers_in, HTTP_USER_AGENT);
789           device_table* spec = chxj_specified_device(r, user_agent);
790
791           switch(spec->html_spec_type) {
792           case CHXJ_SPEC_Chtml_1_0:
793           case CHXJ_SPEC_Chtml_2_0:
794           case CHXJ_SPEC_Chtml_3_0:
795           case CHXJ_SPEC_Chtml_4_0:
796           case CHXJ_SPEC_Chtml_5_0:
797           case CHXJ_SPEC_Jhtml:
798
799             cookie = chxj_save_cookie(r);
800   
801             /*
802              * Location Header Check to add cookie parameter.
803              */
804             location_header = (char*)apr_table_get(r->headers_out, "Location");
805             if (location_header) {
806               DBG1(r, "Location Header=[%s]", location_header);
807               location_header = chxj_add_cookie_parameter(r,
808                                                           location_header,
809                                                           cookie);
810               apr_table_setn(r->headers_out, "Location", location_header);
811               DBG1(r, "Location Header=[%s]", location_header);
812             }
813             break;
814
815           default:
816             break;
817           }
818         }
819
820         apr_table_setn(r->headers_out, "Content-Length", "0");
821         rv = pass_data_to_filter(f, (const char*)"", (apr_size_t)0);
822
823         return rv;
824       }
825     }
826     else
827     if (apr_bucket_read(b, &data, &len, APR_BLOCK_READ) == APR_SUCCESS) {
828       DBG2(r, "read data[%.*s]",len, data);
829
830       if (f->ctx == NULL) {
831         /*--------------------------------------------------------------------*/
832         /* Start                                                              */
833         /*--------------------------------------------------------------------*/
834         DBG(r, "new context");
835         ctx = (mod_chxj_ctx*)apr_palloc(r->pool, sizeof(mod_chxj_ctx));
836         if (len > 0) {
837           ctx->buffer = apr_palloc(r->pool, len);
838           memcpy(ctx->buffer, data, len);
839         }
840         else {
841           ctx->buffer = apr_palloc(r->pool, 1);
842           ctx->buffer = '\0';
843         }
844         ctx->len = len;
845         f->ctx = (void*)ctx;
846       }
847       else {
848         /*--------------------------------------------------------------------*/
849         /* append data                                                        */
850         /*--------------------------------------------------------------------*/
851         char* tmp;
852         DBG(r, "append data start");
853         ctx = (mod_chxj_ctx*)f->ctx;
854
855         if (len > 0) {
856           tmp = apr_palloc(r->pool, ctx->len);
857           memcpy(tmp, ctx->buffer, ctx->len);
858
859           ctx->buffer = apr_palloc(r->pool, ctx->len + len);
860
861           memcpy(ctx->buffer, tmp, ctx->len);
862           memcpy(&ctx->buffer[ctx->len], data, len);
863
864           ctx->len += len;
865         }
866         DBG(r, "append data end");
867       }
868     }
869   }
870   apr_brigade_destroy(bb);
871
872   DBG(r, "end of output filter");
873
874   return APR_SUCCESS;
875 }
876
877
878 /**
879  * It is the main loop of the input filter. 
880  *
881  * @param f         [i/o] It is a filter.
882  * @param bb        [i]   brigade
883  * @param mode      [i]   mode
884  * @param block     [i]   block
885  * @param readbytes [i]   readbyte
886  */
887 static apr_status_t 
888 chxj_input_filter(ap_filter_t*        f, 
889                  apr_bucket_brigade*  bb,
890                  ap_input_mode_t      mode, 
891                  apr_read_type_e      block,
892                  apr_off_t            readbytes)
893 {
894   request_rec*        r;
895   apr_status_t        rv;
896   conn_rec*           c;
897   apr_bucket*         b;
898   /*--------------------------------------------------------------------------*/
899   /* It is the brigade area for output                                        */
900   /*--------------------------------------------------------------------------*/
901   apr_bucket_brigade* ibb;            
902   /*--------------------------------------------------------------------------*/
903   /* It is the brigade area for input                                         */
904   /*--------------------------------------------------------------------------*/
905   apr_bucket_brigade* obb;
906   apr_size_t          len;
907   apr_bucket*         tmp_heap;
908   apr_bucket*         eos;
909   const char*         data;
910   char*               data_bucket;
911   char*               data_brigade;
912   char*               content_type;
913   device_table*       spec ;
914   char*               user_agent;
915   mod_chxj_config*    dconf;
916   chxjconvrule_entry* entryp;
917
918   r = f->r;
919   c = r->connection;
920
921   DBG(r, "start of chxj_input_filter()");
922
923   data_brigade = qs_alloc_zero_byte_string(r);
924
925
926   ibb = apr_brigade_create(r->pool, c->bucket_alloc);
927   obb = apr_brigade_create(r->pool, c->bucket_alloc);
928
929   content_type = (char*)apr_table_get(r->headers_in, "Content-Type");
930   if (content_type 
931   && strncasecmp("multipart/form-data", content_type, 19) == 0) {
932
933     DBG(r, "detect multipart/form-data");
934     ap_remove_input_filter(f);
935
936     return ap_get_brigade(f->next, bb, mode, block, readbytes);
937   }
938
939   dconf = ap_get_module_config(r->per_dir_config, &chxj_module);
940
941   entryp = chxj_apply_convrule(r, dconf->convrules);
942   if (!entryp || !(entryp->action & CONVRULE_ENGINE_ON_BIT)) {
943     DBG(r,"EngineOff");
944
945     ap_remove_input_filter(f);
946     return ap_get_brigade(f->next, bb, mode, block, readbytes);
947   }
948
949   user_agent = (char*)apr_table_get(r->headers_in, "User-Agent");
950   spec = chxj_specified_device(r, user_agent);
951
952   switch(spec->html_spec_type) {
953   case CHXJ_SPEC_Chtml_1_0:
954   case CHXJ_SPEC_Chtml_2_0:
955   case CHXJ_SPEC_Chtml_3_0:
956   case CHXJ_SPEC_Chtml_4_0:
957   case CHXJ_SPEC_Chtml_5_0:
958   case CHXJ_SPEC_XHtml_Mobile_1_0:
959   case CHXJ_SPEC_Hdml:
960   case CHXJ_SPEC_Jhtml:
961     break;
962
963   default:
964     ap_remove_input_filter(f);
965     return ap_get_brigade(f->next, bb, mode, block, readbytes);
966   }
967
968
969   rv = ap_get_brigade(f->next, ibb, mode, block, readbytes);
970   if (rv != APR_SUCCESS) {
971     DBG(r, "ap_get_brigade() failed");
972     return rv;
973   }
974
975   APR_BRIGADE_FOREACH(b, ibb) {
976     rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
977     if (rv != APR_SUCCESS) {
978       DBG(r, "apr_bucket_read() failed");
979       return rv;
980     }
981
982     if (data != NULL) {
983       data_bucket = apr_palloc(r->pool, len+1);
984       memset((void*)data_bucket, 0, len+1);
985       memcpy(data_bucket, data, len);
986       DBG1(r, "(in)POSTDATA:[%s]", data_bucket);
987   
988       data_brigade = apr_pstrcat(r->pool, data_brigade, data_bucket, NULL);
989     }
990
991     if (APR_BUCKET_IS_EOS(b)) {
992       break;
993     }
994   }
995   apr_brigade_cleanup(ibb);
996
997
998   len = strlen(data_brigade);
999   if (len == 0) {
1000     DBG(r,"data_brigade length is 0");
1001     DBG(r,"end of chxj_input_filter()");
1002     ap_remove_input_filter(f);
1003     return ap_get_brigade(f->next, bb, mode, block, readbytes);
1004   }
1005
1006   data_brigade = chxj_input_convert(
1007     r, 
1008     (const char**)&data_brigade, 
1009     (apr_size_t*)&len,
1010     entryp
1011     );
1012
1013   if (len > 0) {
1014     DBG1(r, "(in:exchange)POSTDATA:[%s]", data_brigade);
1015
1016     obb = apr_brigade_create(r->pool, c->bucket_alloc);
1017
1018     tmp_heap = apr_bucket_heap_create(data_brigade, 
1019                                       len, 
1020                                       NULL, 
1021                                       f->c->bucket_alloc);
1022     eos      = apr_bucket_eos_create(f->c->bucket_alloc);
1023
1024     APR_BRIGADE_INSERT_TAIL(obb, tmp_heap);
1025     APR_BRIGADE_INSERT_TAIL(obb, eos);
1026     APR_BRIGADE_CONCAT(bb, obb);
1027   }
1028
1029   DBG(r, "end of chxj_input_filter()");
1030
1031   return APR_SUCCESS;
1032 }
1033
1034
1035 static mod_chxj_global_config*
1036 chxj_global_config_create(apr_pool_t* pool, server_rec* s)
1037 {
1038   mod_chxj_global_config* conf;
1039
1040   SDBG(s, "start chxj_global_config_create()");
1041
1042   /*--------------------------------------------------------------------------*/
1043   /* allocate an own subpool which survives server restarts                   */
1044   /*--------------------------------------------------------------------------*/
1045   conf = (mod_chxj_global_config*)apr_palloc(pool, 
1046                   sizeof(mod_chxj_global_config));
1047 #if 0
1048   conf->cookie_db_lock = NULL;
1049 #endif
1050   SDBG(s, "end   chxj_global_config_create()");
1051
1052   return conf;
1053 }
1054
1055
1056 /**
1057  * initialize chxj module
1058  */
1059 static int 
1060 chxj_init_module(apr_pool_t *p, 
1061                   apr_pool_t *plog, 
1062                   apr_pool_t *ptemp, 
1063                   server_rec *s)
1064 {
1065 #if 0
1066   mod_chxj_global_config* conf;
1067 #endif
1068   void *user_data;
1069
1070   SDBG(s, "start chxj_init_module()");
1071
1072   apr_pool_userdata_get(&user_data, CHXJ_MOD_CONFIG_KEY, s->process->pool);
1073   SDBG(s, " ");
1074   if (user_data == NULL) {
1075     SDBG(s, " ");
1076     /*
1077      * dummy user_data set.
1078      */
1079     apr_pool_userdata_set(
1080       (const void *)(1), 
1081       CHXJ_MOD_CONFIG_KEY, 
1082       apr_pool_cleanup_null, 
1083       s->process->pool);
1084     SDBG(s, "end  chxj_init_module()");
1085     return OK;
1086   }
1087
1088   ap_add_version_component(p, CHXJ_VERSION_PREFIX CHXJ_VERSION);
1089
1090
1091 #if 0
1092   conf = (mod_chxj_global_config *)ap_get_module_config(s->module_config, 
1093                                                         &chxj_module);
1094
1095   if (apr_global_mutex_create(&(conf->cookie_db_lock), 
1096                               NULL, APR_LOCK_DEFAULT, p) != APR_SUCCESS) {
1097     SERR(s, "end  chxj_init_module()");
1098     return HTTP_INTERNAL_SERVER_ERROR;
1099   }
1100
1101 #ifdef AP_NEED_SET_MUTEX_PERMS
1102   if (unixd_set_global_mutex_perms(conf->cookie_db_lock) != APR_SUCCESS) {
1103     SERR(s, "end  chxj_init_module()");
1104     return HTTP_INTERNAL_SERVER_ERROR;
1105   }
1106 #endif
1107 #endif
1108
1109   SDBG(s, "end  chxj_init_module()");
1110
1111   return OK;
1112 }
1113
1114
1115 static void 
1116 chxj_child_init(apr_pool_t *p, server_rec *s)
1117 {
1118 #if 0
1119   mod_chxj_global_config* conf;
1120 #endif
1121
1122   SDBG(s, "start chxj_child_init()");
1123
1124 #if 0
1125   conf = (mod_chxj_global_config*)ap_get_module_config(s->module_config, 
1126                                                        &chxj_module);
1127
1128   if (apr_global_mutex_child_init(&conf->cookie_db_lock, NULL, p) 
1129   != APR_SUCCESS) {
1130     SERR(s, "Can't attach global mutex.");
1131     return;
1132   }
1133 #endif
1134
1135   SDBG(s, "end   chxj_child_init()");
1136 }
1137
1138
1139 /**
1140  * A set structure of each server is generated. 
1141  * 
1142  * @param p
1143  * @param s
1144  */
1145 static void*
1146 chxj_config_server_create(apr_pool_t *p, server_rec *s)
1147 {
1148   mod_chxj_global_config *gc;
1149
1150   gc = chxj_global_config_create(p,s);
1151
1152   return gc;
1153 }
1154
1155
1156 static int
1157 chxj_translate_name(request_rec *r)
1158 {
1159   return chxj_trans_name(r);
1160 }
1161
1162
1163 static void 
1164 chxj_insert_filter(request_rec *r)
1165 {
1166   char*               user_agent;
1167   device_table*       spec;
1168   mod_chxj_config*    dconf;
1169   chxjconvrule_entry* entryp;
1170 DBG(r, "start chxj_insert_filter()");
1171
1172   dconf = ap_get_module_config(r->per_dir_config, &chxj_module);
1173
1174   user_agent = (char*)apr_table_get(r->headers_in, HTTP_USER_AGENT);
1175   spec = chxj_specified_device(r, user_agent);
1176   entryp = NULL;
1177
1178   entryp = chxj_apply_convrule(r, dconf->convrules);
1179   if (!entryp) {
1180     DBG(r, "end chxj_insert_filter()");
1181     return;
1182   }
1183
1184   if (entryp->action & CONVRULE_ENGINE_ON_BIT) {
1185     ap_add_input_filter("chxj_input_filter",   NULL, r, r->connection);
1186     ap_add_output_filter("chxj_output_filter", NULL, r, r->connection);
1187   }
1188
1189 DBG(r, "end   chxj_insert_filter()");
1190 }
1191
1192
1193 /**
1194  * The hook is registered.
1195  *
1196  * @param p
1197  */
1198 static void 
1199 chxj_register_hooks(apr_pool_t *p)
1200 {
1201   ap_hook_post_config(chxj_init_module,
1202                       NULL,
1203                       NULL,
1204                       APR_HOOK_REALLY_FIRST);
1205   ap_hook_child_init(chxj_child_init, 
1206                      NULL, 
1207                      NULL, 
1208                      APR_HOOK_REALLY_FIRST);
1209   ap_register_output_filter (
1210                       "chxj_output_filter", 
1211                       chxj_output_filter, 
1212                       NULL, 
1213                       AP_FTYPE_RESOURCE);
1214   ap_register_input_filter(
1215                       "chxj_input_filter", 
1216                       chxj_input_filter, 
1217                       NULL, 
1218                       AP_FTYPE_RESOURCE);
1219   ap_hook_insert_filter(chxj_insert_filter, NULL, NULL, APR_HOOK_MIDDLE);
1220   ap_hook_handler(chxj_img_conv_format_handler, NULL, NULL, APR_HOOK_MIDDLE);
1221   ap_hook_handler(chxj_qr_code_handler, NULL, NULL, APR_HOOK_MIDDLE);
1222   ap_hook_translate_name(chxj_translate_name, NULL, NULL, APR_HOOK_MIDDLE);
1223   ap_hook_fixups(chxj_headers_fixup, NULL, NULL, APR_HOOK_LAST);
1224 }
1225
1226
1227 /**
1228  * A set structure according to the directory is generated. 
1229  *
1230  * @param p
1231  * @param arg
1232  */
1233 static void* 
1234 chxj_create_per_dir_config(apr_pool_t *p, char *arg) 
1235 {
1236   mod_chxj_config* conf;
1237
1238   conf = apr_pcalloc(p, sizeof(mod_chxj_config));
1239   conf->device_data_file = NULL;
1240   conf->devices          = NULL;
1241   conf->emoji_data_file  = NULL;
1242   conf->emoji            = NULL;
1243   conf->emoji_tail       = NULL;
1244   conf->image            = CHXJ_IMG_OFF;
1245   conf->image_cache_dir  = apr_psprintf(p, "%s",DEFAULT_IMAGE_CACHE_DIR);
1246   conf->server_side_encoding = NULL;
1247   conf->cookie_db_dir    = NULL;
1248   conf->cookie_timeout   = 0;
1249
1250   if (arg == NULL) {
1251     conf->dir                  = NULL;
1252   }
1253   else {
1254     conf->dir                  = apr_pcalloc(p, strlen(arg)+1);
1255     memset(conf->dir, 0, strlen(arg)+1);
1256     strcpy(conf->dir, arg);
1257   }
1258   conf->convrules   = apr_array_make(p, 2, sizeof(chxjconvrule_entry));
1259
1260   /* Default is copyleft */
1261   conf->image_copyright = NULL; 
1262
1263   return conf;
1264 }
1265
1266
1267 /*
1268  *  Merge per-directory CHXJ configurations
1269  */
1270 static void*
1271 chxj_merge_per_dir_config(apr_pool_t *p, void *basev, void *addv)
1272 {
1273   mod_chxj_config *base;
1274   mod_chxj_config *add;
1275   mod_chxj_config *mrg;
1276
1277   base = (mod_chxj_config*)basev;
1278   add  = (mod_chxj_config*)addv;
1279   mrg  = (mod_chxj_config*)apr_palloc(p, sizeof(mod_chxj_config));
1280
1281   mrg->device_data_file = NULL;
1282   mrg->devices          = NULL;
1283   mrg->emoji_data_file  = NULL;
1284   mrg->image            = CHXJ_IMG_OFF;
1285   mrg->image_cache_dir  = NULL;
1286   mrg->image_copyright  = NULL;
1287   mrg->emoji            = NULL;
1288   mrg->emoji_tail       = NULL;
1289
1290   mrg->dir = apr_pstrdup(p, add->dir);
1291
1292   if (! add->device_data_file) {
1293     mrg->devices = base->devices;
1294     mrg->device_data_file = apr_pstrdup(p, base->device_data_file);
1295   }
1296   else {
1297     mrg->devices = add->devices;
1298     mrg->device_data_file = apr_pstrdup(p, add->device_data_file);
1299   }
1300
1301   if (! add->emoji_data_file) {
1302     mrg->emoji = base->emoji;
1303     mrg->emoji_tail = base->emoji_tail;
1304     mrg->emoji_data_file = apr_pstrdup(p, base->emoji_data_file);
1305   }
1306   else {
1307     mrg->emoji = add->emoji;
1308     mrg->emoji_tail = add->emoji_tail;
1309     mrg->emoji_data_file = apr_pstrdup(p, add->emoji_data_file);
1310   }
1311
1312   if (add->image == CHXJ_IMG_OFF) 
1313     mrg->image = base->image;
1314   else 
1315     mrg->image = add->image;
1316
1317
1318   if (strcasecmp(add->image_cache_dir ,DEFAULT_IMAGE_CACHE_DIR)==0) 
1319     mrg->image_cache_dir = apr_pstrdup(p, base->image_cache_dir);
1320   else 
1321     mrg->image_cache_dir = apr_pstrdup(p, add->image_cache_dir);
1322
1323   if (add->image_copyright) 
1324     mrg->image_copyright = apr_pstrdup(p, add->image_copyright);
1325   else
1326     mrg->image_copyright = apr_pstrdup(p, base->image_copyright);
1327
1328   if (add->server_side_encoding) {
1329     mrg->server_side_encoding = apr_pstrdup(p, add->server_side_encoding);
1330   }
1331   else 
1332   if (base->server_side_encoding) {
1333     mrg->server_side_encoding = apr_pstrdup(p, base->server_side_encoding);
1334   }
1335   else {
1336     mrg->server_side_encoding = apr_pstrdup(p, DEFAULT_SERVER_SIDE_ENCODING);
1337   }
1338
1339   mrg->convrules    = apr_array_append(p, add->convrules, base->convrules);
1340
1341   if (add->cookie_db_dir) {
1342     mrg->cookie_db_dir = apr_pstrdup(p, add->cookie_db_dir);
1343   }
1344   else
1345   if (base->cookie_db_dir) {
1346     mrg->cookie_db_dir = apr_pstrdup(p, base->cookie_db_dir);
1347   }
1348   else {
1349     mrg->cookie_db_dir = NULL;
1350   }
1351
1352   if (add->cookie_timeout) {
1353     mrg->cookie_timeout   = add->cookie_timeout;
1354   }
1355   else
1356   if (base->cookie_db_dir) {
1357     mrg->cookie_timeout   = base->cookie_timeout;
1358   }
1359   else {
1360     mrg->cookie_timeout   = 0;
1361   }
1362
1363   return mrg;
1364 }
1365
1366
1367 static int
1368 chxj_command_parse_take5(
1369   const char* arg, 
1370   char** prm1, 
1371   char** prm2, 
1372   char** prm3, 
1373   char** prm4, 
1374   char** prm5)
1375 {
1376   int isquoted;
1377   char* strp;
1378
1379   strp = (char*)arg;
1380
1381   for (;*strp == ' '||*strp == '\t'; strp++) ;
1382
1383   isquoted = 0; 
1384   if (*strp == '"') { 
1385     isquoted = 1;
1386     strp++;
1387   }
1388
1389   *prm1 = strp;
1390
1391   for (; *strp != '\0'; strp++) {
1392     if ((isquoted && (*strp == ' ' || *strp == '\t'))
1393     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1394       strp++;
1395       continue;
1396     }
1397
1398     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1399     ||  (isquoted  && *strp == '"'))
1400       break;
1401   }
1402
1403   if (! *strp) {
1404     *prm2 = strp;
1405     *prm3 = strp;
1406     *prm4 = strp;
1407     *prm5 = strp;
1408     return 1;
1409   }
1410
1411   *strp++ = '\0';
1412
1413   for (;*strp == ' '||*strp == '\t'; strp++) ;
1414
1415   isquoted = 0; 
1416   if (*strp == '"') { 
1417     isquoted = 1;
1418     strp++;
1419   }
1420
1421   *prm2 = strp;
1422   for (; *strp != '\0'; strp++) {
1423     if ((isquoted && (*strp == ' ' || *strp == '\t'))
1424     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1425       strp++;
1426       continue;
1427     }
1428
1429     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1430     ||  (isquoted  && *strp == '"'))
1431       break;
1432   }
1433
1434   if (! *strp) {
1435     *prm3 = strp;
1436     *prm4 = strp;
1437     *prm5 = strp;
1438     return 0;
1439   }
1440
1441   *strp++ = '\0';
1442
1443   for (;*strp == ' '||*strp == '\t'; strp++);
1444
1445   isquoted = 0; 
1446   if (*strp == '"') { 
1447     isquoted = 1;
1448     strp++;
1449   }
1450   *prm3 = strp;
1451   for (; *strp != '\0'; strp++) {
1452     if ((isquoted && (*strp == ' ' || *strp == '\t'))
1453     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1454       strp++;
1455       continue;
1456     }
1457
1458     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1459     ||  (isquoted  && *strp == '"'))
1460       break;
1461   }
1462
1463   if (! *strp) {
1464     *prm4 = strp;
1465     *prm5 = strp;
1466     return 0;
1467   }
1468
1469   *strp++ = '\0';
1470
1471   for (;*strp == ' '||*strp == '\t'; strp++);
1472
1473   isquoted = 0; 
1474   if (*strp == '"') { 
1475     isquoted = 1;
1476     strp++;
1477   }
1478   *prm4 = strp;
1479   for (; *strp != '\0'; strp++) {
1480     if ((isquoted && (*strp == ' ' || *strp == '\t'))
1481     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1482       strp++;
1483       continue;
1484     }
1485
1486     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1487     ||  (isquoted  && *strp == '"'))
1488       break;
1489   }
1490
1491   if (! *strp) {
1492     *prm5 = strp;
1493     return 0;
1494   }
1495
1496   *strp++ = '\0';
1497
1498   for (;*strp == ' '||*strp == '\t'; strp++);
1499
1500   isquoted = 0; 
1501   if (*strp == '"') { 
1502     isquoted = 1;
1503     strp++;
1504   }
1505   *prm5 = strp;
1506   for (; *strp != '\0'; strp++) {
1507     if ((isquoted && (*strp == ' ' || *strp == '\t'))
1508     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1509       strp++;
1510       continue;
1511     }
1512
1513     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1514     ||  (isquoted  && *strp == '"'))
1515       break;
1516   }
1517   *strp = '\0';
1518
1519   return 0;
1520 }
1521
1522
1523 /**
1524  * The device definition file is loaded. 
1525  *
1526  * @param arg     [i]   The name of the device definition file is specified.
1527  * @param mconfig [i/o] The pointer to a set structure is specified. 
1528  * @param parms   [i]   
1529  */
1530 static const char* 
1531 cmd_load_device_data(cmd_parms *parms, void *mconfig, const char* arg) 
1532 {
1533   mod_chxj_config* conf;
1534   Doc              doc;
1535
1536   doc.r = NULL;
1537
1538   if (strlen(arg) > 256) 
1539     return "mod_chxj: device data filename too long.";
1540
1541   conf = (mod_chxj_config*)mconfig;
1542   conf->device_data_file = apr_pstrdup(parms->pool, arg);
1543
1544   qs_init_malloc(&doc);
1545   qs_init_root_node(&doc);
1546
1547   qs_parse_file((Doc*)&doc, (const char*)arg);
1548   chxj_load_device_data(&doc,parms->pool, conf);
1549   qs_all_free(&doc, QX_LOGMARK);
1550
1551   return NULL;
1552 }
1553
1554
1555 /**
1556  * Device definition information is loaded. 
1557  *
1558  * @param parms 
1559  * @param arg     [i]   The name of the device definition file is specified. 
1560  * @param mconfig [i/o] The pointer to a set structure is specified. 
1561  * @return 
1562  */
1563 static const char* 
1564 cmd_load_emoji_data(cmd_parms *parms, void *mconfig, const char* arg) 
1565 {
1566   mod_chxj_config* conf;
1567   char*            rtn;
1568   Doc              doc;
1569
1570   doc.r = NULL;
1571
1572
1573   if (strlen(arg) > 256) 
1574     return "mod_chxj: emoji data filename too long.";
1575
1576   conf = (mod_chxj_config*)mconfig;
1577   conf->emoji_data_file = apr_pstrdup(parms->pool, arg);
1578   qs_init_malloc(&doc);
1579   qs_init_root_node(&doc);
1580
1581   qs_parse_file((Doc*)&doc, (const char*)arg);
1582
1583   rtn = chxj_load_emoji_data(&doc,parms->pool, conf);
1584
1585   qs_all_free(&doc, QX_LOGMARK);
1586
1587
1588   return rtn;
1589 }
1590
1591
1592 static const char* 
1593 cmd_set_image_engine(cmd_parms *parms, void *mconfig, const char* arg) 
1594 {
1595   mod_chxj_config* conf;
1596   Doc              doc;
1597
1598   doc.r = NULL;
1599
1600   if (strlen(arg) > 256) 
1601     return "image uri is too long.";
1602
1603   conf = (mod_chxj_config*)mconfig;
1604   if (strcasecmp("ON", arg) == 0)
1605     conf->image = CHXJ_IMG_ON;
1606   else
1607     conf->image = CHXJ_IMG_OFF;
1608
1609   return NULL;
1610 }
1611
1612
1613 static const char* 
1614 cmd_set_image_cache_dir(cmd_parms *parms, void *mconfig, const char* arg) 
1615 {
1616   mod_chxj_config* conf;
1617   Doc              doc;
1618
1619   doc.r = NULL;
1620
1621   if (strlen(arg) > 256) 
1622     return "cache dir name is too long.";
1623
1624   conf = (mod_chxj_config*)mconfig;
1625   conf->image_cache_dir = apr_pstrdup(parms->pool, arg);
1626
1627   return NULL;
1628 }
1629
1630
1631 static const char* 
1632 cmd_set_image_copyright(cmd_parms *parms, void* mconfig, const char* arg) 
1633 {
1634   mod_chxj_config* conf;
1635   Doc              doc;
1636
1637   doc.r = NULL;
1638
1639   if (strlen(arg) > 256) 
1640     return "Copyright Flag is too long.";
1641
1642   conf = (mod_chxj_config*)mconfig;
1643   conf->image_copyright = apr_pstrdup(parms->pool, arg);
1644
1645   return NULL;
1646 }
1647
1648
1649 static const char*
1650 cmd_convert_rule(cmd_parms *cmd, void* mconfig, const char *arg)
1651 {
1652   int                 mode;
1653   ap_regex_t*         regexp;
1654   mod_chxj_config*    dconf;
1655   chxjconvrule_entry* newrule;
1656   char*               prm1;
1657   char*               prm2;
1658   char*               prm3;
1659   char*               prm4;
1660   char*               prm5;
1661   char*               pstate;
1662   char*               action;
1663   char*               pp;
1664
1665   dconf = (mod_chxj_config*)mconfig;
1666
1667   if (strlen(arg) > 4096) 
1668     return "mod_chxj: ChxjConvertRule: is too long.";
1669
1670   dconf = (mod_chxj_config*)mconfig;
1671   if (dconf->convrules == NULL)
1672     dconf->convrules   = apr_array_make(cmd->pool, 
1673                                         2, 
1674                                         sizeof(chxjconvrule_entry));
1675
1676   newrule = apr_array_push(dconf->convrules);
1677
1678   newrule->flags  = 0;
1679   newrule->action = 0;
1680
1681   if (chxj_command_parse_take5(arg, &prm1, &prm2, &prm3, &prm4, &prm5))
1682     return "ChxjConvertRule: bad argument line";
1683
1684   newrule->pattern = apr_pstrdup(cmd->pool, prm1);
1685
1686   /* Parse action */
1687   for (;;) {
1688     if ((action = apr_strtok(prm2, ",", &pstate)) == NULL)
1689       break;
1690     prm2 = NULL;
1691     switch(*action) {
1692     case 'e':
1693     case 'E':
1694       if (strcasecmp(CONVRULE_ENGINE_ON_CMD, action) == 0) {
1695         newrule->action |= CONVRULE_ENGINE_ON_BIT;
1696       }
1697       else
1698       if (strcasecmp(CONVRULE_ENGINE_OFF_CMD, action) == 0) {
1699         newrule->action |= CONVRULE_ENGINE_OFF_BIT;
1700       }
1701       break;
1702
1703     case 'C':
1704     case 'c':
1705       if (strcasecmp(CONVRULE_COOKIE_ON_CMD, action) == 0) {
1706         newrule->action |= CONVRULE_COOKIE_ON_BIT;
1707       }
1708       break;
1709     default:
1710       break;
1711     }
1712   }
1713   
1714   pp = prm1;
1715   if (*pp == '!') {
1716     newrule->flags |= CONVRULE_FLAG_NOTMATCH;
1717     pp++;
1718   }
1719
1720   mode = AP_REG_EXTENDED;
1721   if ((regexp = ap_pregcomp(cmd->pool, pp, mode)) == NULL)
1722     return "RewriteRule: cannot compile regular expression ";
1723
1724   newrule->regexp = regexp;
1725   if (*prm3)
1726     newrule->encoding = apr_pstrdup(cmd->pool, prm3);
1727   else
1728     newrule->encoding = apr_pstrdup(cmd->pool, "none");
1729
1730   newrule->pc_flag = CONVRULE_PC_FLAG_OFF_BIT;
1731   if (*prm4)
1732     if (strcasecmp(CONVRULE_PC_FLAG_ON_CMD, prm4) == 0)
1733       newrule->pc_flag = CONVRULE_PC_FLAG_ON_BIT;
1734
1735   newrule->user_agent = NULL;
1736   if (*prm5)
1737     newrule->user_agent = apr_pstrdup(cmd->pool, prm5);
1738     
1739   return NULL;
1740 }
1741
1742
1743 static const char*
1744 cmd_set_cookie_dir(
1745   cmd_parms*  cmd, 
1746   void*       mconfig, 
1747   const char* arg)
1748 {
1749   mod_chxj_config*    dconf;
1750
1751
1752   if (strlen(arg) > 4096) 
1753     return "mod_chxj: ChxjCookieDir is too long.";
1754
1755   dconf = (mod_chxj_config*)mconfig;
1756
1757   dconf->cookie_db_dir = apr_pstrdup(cmd->pool, arg);
1758
1759   return NULL;
1760 }
1761
1762
1763 static const char*
1764 cmd_set_cookie_timeout(
1765   cmd_parms*  cmd, 
1766   void*       mconfig, 
1767   const char* arg)
1768 {
1769   return NULL;
1770 }
1771
1772
1773 static const command_rec cmds[] = {
1774   AP_INIT_TAKE1(
1775     "ChxjLoadDeviceData",
1776     cmd_load_device_data,
1777     NULL,
1778     OR_ALL,
1779     "Load Device Data"),
1780   AP_INIT_TAKE1(
1781     "ChxjLoadEmojiData",
1782     cmd_load_emoji_data,
1783     NULL,
1784     OR_ALL,
1785     "Load Emoji Data"),
1786   AP_INIT_TAKE1(
1787     "ChxjImageEngine",
1788     cmd_set_image_engine,
1789     NULL,
1790     OR_ALL,
1791     "Convert Target URI"),
1792   AP_INIT_TAKE1(
1793     "ChxjImageCacheDir",
1794     cmd_set_image_cache_dir,
1795     NULL,
1796     OR_ALL,
1797     "Image Cache Directory"),
1798   AP_INIT_TAKE1(
1799     "ChxjImageCopyright",
1800     cmd_set_image_copyright,
1801     NULL,
1802     OR_ALL,
1803     "Copyright Flag"),
1804   AP_INIT_RAW_ARGS(
1805     "ChxjConvertRule",
1806     cmd_convert_rule,
1807     NULL, 
1808     OR_FILEINFO,
1809     "an URL-applied regexp-pattern and a substitution URL"),
1810   AP_INIT_TAKE1(
1811     "ChxjCookieDir",
1812     cmd_set_cookie_dir,
1813     NULL,
1814     OR_ALL,
1815     "save cookie.db directory."),
1816   AP_INIT_TAKE1(
1817     "ChxjCookieTimeout",
1818     cmd_set_cookie_timeout,
1819     NULL,
1820     OR_ALL,
1821     "The compulsion time-out time of the cookie is specified. "),
1822   { NULL },
1823 };
1824
1825
1826 /*----------------------------------------------------------------------------*/
1827 /* Dispatch list for API hooks                                                */
1828 /*----------------------------------------------------------------------------*/
1829 module AP_MODULE_DECLARE_DATA chxj_module = {
1830   STANDARD20_MODULE_STUFF, 
1831   chxj_create_per_dir_config,          /* create per-dir    config structures */
1832   chxj_merge_per_dir_config,           /* merge  per-dir    config structures */
1833   chxj_config_server_create,           /* create per-server config structures */
1834   NULL,                                /* merge  per-server config structures */
1835   cmds,                                /* table of config file commands       */
1836   chxj_register_hooks                  /* register hooks                      */
1837 };
1838 /*
1839  * vim:ts=2 et
1840  */