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   /*
230    * save cookie.
231    */
232   cookie = NULL;
233   if (entryp->action & CONVRULE_COOKIE_ON_BIT)
234     cookie = chxj_save_cookie(r);
235
236   if (!r->header_only) {
237     device_table* spec = chxj_specified_device(r, user_agent);
238
239     tmp = NULL;
240     if (convert_routine[spec->html_spec_type].encoder)
241       tmp = convert_routine[spec->html_spec_type].encoder(r, 
242                                                           *src, 
243                                                           (apr_size_t*)len);
244
245     if (convert_routine[spec->html_spec_type].converter) {
246       if (tmp)
247         dst = convert_routine[spec->html_spec_type].converter(r, 
248                                                               spec, 
249                                                               tmp, 
250                                                               *len, 
251                                                               len, 
252                                                               entryp, 
253                                                               cookie);
254       else
255         dst = convert_routine[spec->html_spec_type].converter(r,
256                                                               spec, 
257                                                               tmp, 
258                                                               *len, 
259                                                               len, 
260                                                               entryp, 
261                                                               cookie);
262     }
263   }
264
265   if (*len == 0) {
266     dst = apr_psprintf(r->pool, "\n");
267     *len = 1;
268   }
269   dst[*len] = 0;
270
271   DBG(r, "end chxj_exchange()");
272
273   return dst;
274 }
275
276
277 /**
278  * It converts it from HEADER.
279  *
280  * @param r   [i]
281  */
282 static int
283 chxj_convert_input_header(request_rec *r,chxjconvrule_entry* entryp) 
284 {
285
286   char*      buff;
287   apr_size_t urilen;
288   char*      result;
289   char*      pair;
290   char*      name;
291   char*      value;
292   char*      pstate;
293   char*      vstate;
294
295   DBG(r, "start chxj_convert_input_header()");
296
297   if (! r->args) {
298     DBG(r, "r->args=[null]");
299     DBG(r, "end   chxj_convert_input_header()");
300     return 0;
301   }
302   urilen = strlen(r->args);
303
304   result = qs_alloc_zero_byte_string(r);
305
306   buff = apr_pstrdup(r->pool, r->args);
307   DBG1(r, "r->args=[%s]", buff);
308
309   /* _chxj_dmy */
310   /* _chxj_c_ */
311   /* _chxj_r_ */
312   /* _chxj_s_ */
313   for (;;) {
314     char* pair_sv;
315
316     pair = apr_strtok(buff, "&", &pstate);
317     if (pair == NULL)
318       break;
319
320     buff = NULL;
321
322     pair_sv = apr_pstrdup(r->pool, pair);
323
324     name  = apr_strtok(pair, "=", &vstate);
325     value = apr_strtok(NULL, "=", &vstate);
326     if (strncasecmp(name, "_chxj", 5) != 0) {
327       if (strlen(result) != 0) 
328         result = apr_pstrcat(r->pool, result, "&", NULL);
329
330       if (strcasecmp(entryp->encoding, "NONE") != 0 && value && strlen(value)) {
331         apr_size_t dlen;
332         char* dvalue;
333
334         dlen   = strlen(value);
335         value = chxj_url_decode(r, value);
336         DBG1(r, "************ before encoding[%s]", value);
337
338         dvalue = chxj_rencoding(r, value, &dlen);
339         dvalue = chxj_url_encode(r, dvalue);
340
341         DBG1(r, "************ after encoding[%s]", dvalue);
342
343         result = apr_pstrcat(r->pool, result, name, "=", dvalue, NULL);
344       }
345       else {
346         if (strcmp(name, pair_sv) != 0)
347           result = apr_pstrcat(r->pool, result, name, "=", value, NULL);
348         else
349           result = apr_pstrcat(r->pool, result, name, NULL);
350       }
351     }
352     else
353     if (strncasecmp(name, "_chxj_c_", 8) == 0 
354     ||  strncasecmp(name, "_chxj_r_", 8) == 0
355     ||  strncasecmp(name, "_chxj_s_", 8) == 0) {
356       if (value == NULL)
357         continue;
358
359       if (strlen(value) == 0)
360         continue;
361
362       if (strlen(result) != 0)
363         result = apr_pstrcat(r->pool, result, "&", NULL);
364
365       result = apr_pstrcat(r->pool, result, &name[8], "=", value, NULL);
366     }
367     else
368     if (strcasecmp(name, CHXJ_COOKIE_PARAM) == 0) {
369       DBG1(r, "found cookie parameter[%s]", value);
370       chxj_load_cookie(r, value);
371     }
372   }
373   r->args = result;
374
375   DBG1(r, "result r->args=[%s]", r->args);
376   DBG(r, "end   chxj_convert_input_header()");
377   return 0;
378 }
379
380
381 /**
382  * It converts it from POSTDATA .
383  *
384  * @param r   [i]
385  * @param src [i]   It is POSTDATA character string.
386  * @param len [i/o] It is length of former HTML character string.
387  */
388 static char*
389 chxj_input_convert(
390   request_rec*        r, 
391   const char**        src, 
392   apr_size_t*         len, 
393   chxjconvrule_entry* entryp)
394 {
395   char* pair;
396   char* name;
397   char* value;
398   char* pstate;
399   char* vstate;
400   char* s;
401   char* result;
402
403   s = apr_pstrdup(r->pool, *src);
404
405   result = qs_alloc_zero_byte_string(r);
406
407   DBG1(r, "BEFORE input convert source = [%s]", s);
408
409   /* _chxj_dmy */
410   /* _chxj_c_ */
411   /* _chxj_r_ */
412   /* _chxj_s_ */
413   for (;;) {
414     pair = apr_strtok(s, "&", &pstate);
415     if (pair == NULL)
416       break;
417     s = NULL;
418
419     name  = apr_strtok(pair, "=", &vstate);
420     value = apr_strtok(NULL, "=", &vstate);
421     if (strncasecmp(name, "_chxj", 5) != 0) {
422       if (strlen(result) != 0) 
423         result = apr_pstrcat(r->pool, result, "&", NULL);
424
425       if (strcasecmp(entryp->encoding, "NONE") != 0 
426       &&  value && strlen(value)) {
427         apr_size_t dlen;
428         char*      dvalue;
429
430         dlen   = strlen(value);
431         value = chxj_url_decode(r, value);
432         DBG1(r, "************ before encoding[%s]", value);
433
434         dvalue = chxj_rencoding(r, value, &dlen);
435         dvalue = chxj_url_encode(r,dvalue);
436
437         DBG1(r, "************ after encoding[%s]", dvalue);
438
439         result = apr_pstrcat(r->pool, result, name, "=", dvalue, NULL);
440
441       }
442       else {
443         result = apr_pstrcat(r->pool, result, name, "=", value, NULL);
444       }
445     }
446     else
447     if (strncasecmp(name, "_chxj_c_", 8) == 0 
448     ||  strncasecmp(name, "_chxj_r_", 8) == 0
449     ||  strncasecmp(name, "_chxj_s_", 8) == 0) {
450       if (value == NULL)
451         continue;
452
453       if (strlen(value) == 0)
454         continue;
455
456       if (strlen(result) != 0)
457         result = apr_pstrcat(r->pool, result, "&", NULL);
458
459       if (strcasecmp(entryp->encoding, "NONE") != 0 && value && strlen(value)) {
460         apr_size_t dlen;
461         char*      dvalue;
462
463         dlen   = strlen(value);
464         value = chxj_url_decode(r, value);
465         DBG1(r, "************ before encoding[%s]", value);
466
467         dvalue = chxj_rencoding(r, value, &dlen);
468         dvalue = chxj_url_encode(r,dvalue);
469
470         DBG1(r, "************ after encoding[%s]", dvalue);
471
472         result = apr_pstrcat(r->pool, result, &name[8], "=", dvalue, NULL);
473
474       }
475       else {
476         result = apr_pstrcat(r->pool, result, &name[8], "=", value, NULL);
477       }
478     }
479     else
480     if (strcasecmp(name, CHXJ_COOKIE_PARAM) == 0) {
481       DBG1(r, "found cookie parameter[%s]", value);
482       chxj_load_cookie(r, value);
483     }
484   }
485   *len = strlen(result);
486
487   DBG1(r, "AFTER input convert result = [%s]", result);
488
489   return result;
490 }
491
492
493 /**
494  * The received data is returned to the filter.
495  *
496  * @param f    [i/o] It is a filter. 
497  * @param data [i]   It is data returned to the filter. 
498  * @param len  [i]   It is length of the data returned to the filter. 
499  */
500 static apr_status_t 
501 pass_data_to_filter(ap_filter_t *f, const char *data, 
502                                         apr_size_t len)
503 {
504   request_rec*        r = f->r;
505   conn_rec*           c = r->connection;
506   apr_status_t        rv;
507   apr_bucket_brigade* bb;
508   apr_bucket*         b;
509
510   DBG(r, "start pass_data_to_filter()");
511
512   bb = apr_brigade_create(r->pool, c->bucket_alloc);
513   b  = apr_bucket_transient_create(data, len, c->bucket_alloc);
514
515   APR_BRIGADE_INSERT_TAIL(bb, b);
516   b = apr_bucket_eos_create(f->c->bucket_alloc);
517   APR_BRIGADE_INSERT_TAIL(bb, b);
518
519   rv = ap_pass_brigade(f->next, bb);
520   if (rv != APR_SUCCESS) {
521     DBG(r, "ap_pass_brigade()");
522     return rv;
523   }
524
525   DBG(r, "end pass_data_to_filter()");
526
527   return rv;
528 }
529
530
531 /**
532  * It is the main loop of the output filter. 
533  *
534  * @param f   [i/o] It is a filter.
535  * @param bb  [i]   
536  */
537 static apr_status_t 
538 chxj_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
539 {
540   request_rec*        r;
541   apr_status_t        rv;
542   apr_bucket*         b;
543   const char*         data;
544   char*               contentLength;
545   apr_size_t          len;
546   mod_chxj_ctx*       ctx;
547   cookie_t*           cookie;
548   char*               location_header;
549   mod_chxj_config*    dconf;
550   chxjconvrule_entry* entryp;
551
552
553
554   DBG(f->r, "start of chxj_output_filter()");
555
556   r  = f->r;
557   rv = APR_SUCCESS;
558
559   if (!f->ctx) {
560     if ((f->r->proto_num >= 1001) 
561     &&  !f->r->main 
562     &&  !f->r->prev) 
563       f->r->chunked = 1;
564   }
565
566   dconf  = ap_get_module_config(r->per_dir_config, &chxj_module);
567   entryp = chxj_apply_convrule(r, dconf->convrules);
568
569
570   for (b = APR_BRIGADE_FIRST(bb);
571        b != APR_BRIGADE_SENTINEL(bb); 
572        b = APR_BUCKET_NEXT(b)) {
573
574     if (APR_BUCKET_IS_EOS(b)) {
575
576       DBG(r, "eos");
577       /*----------------------------------------------------------------------*/
578       /* End Of File                                                          */
579       /*----------------------------------------------------------------------*/
580       if (f->ctx) {
581
582         ctx = (mod_chxj_ctx*)f->ctx;
583
584         DBG1(r, "content_type=[%s]", r->content_type);
585
586         if (r->content_type 
587         && *(char*)r->content_type == 't' 
588         && strncmp(r->content_type, "text/html",   9) == 0) {
589
590           if (ctx->len) {
591             char* tmp;
592
593             tmp = apr_palloc(r->pool, ctx->len + 1);
594
595             memset(tmp, 0, ctx->len + 1);
596             memcpy(tmp, ctx->buffer, ctx->len);
597
598             DBG2(r, "input data=[%s] len=[%d]", tmp, ctx->len);
599
600             ctx->buffer = chxj_exchange(r, 
601                                         (const char**)&tmp, 
602                                         (apr_size_t*)&ctx->len);
603
604             DBG2(r, "output data=[%.*s]", ctx->len,ctx->buffer);
605           }
606           else {
607             ctx->buffer = apr_psprintf(r->pool, "\n");
608             ctx->len += 1;
609             ctx->buffer = chxj_exchange(r, 
610                                         (const char**)&ctx->buffer, 
611                                         (apr_size_t*)&ctx->len);
612
613           }
614         }
615
616         if (r->content_type 
617         && *(char*)r->content_type == 'i' 
618         && strncmp(r->content_type, "image/", 6) == 0) {
619           if (ctx->len) {
620             char* tmp;
621
622             tmp = apr_palloc(r->pool, ctx->len + 1);
623
624             memset(tmp, 0, ctx->len + 1);
625             memcpy(tmp, ctx->buffer, ctx->len);
626
627             DBG1(r, "input data=[%s]", tmp);
628
629             ctx->buffer = 
630               chxj_exchange_image(r, 
631                                   (const char**)&tmp,
632                                   (apr_size_t*)&ctx->len);
633
634             if (ctx->buffer == NULL)
635               ctx->buffer = tmp;
636
637             DBG2(r, "output data=[%.*s]", ctx->len,ctx->buffer);
638           }
639         }
640
641         contentLength = apr_psprintf(r->pool, "%d", ctx->len);
642         apr_table_setn(r->headers_out, "Content-Length", contentLength);
643         
644         if (ctx->len > 0) {
645           rv = pass_data_to_filter(f, 
646                                    (const char*)ctx->buffer, 
647                                    (apr_size_t)ctx->len);
648         }
649         f->ctx = NULL;
650
651         return rv;
652       }
653       else {
654         DBG1(r, " SAVE COOKIE[%x]", entryp->action);
655
656         /*
657          * save cookie.
658          */
659         if (entryp->action & CONVRULE_COOKIE_ON_BIT) {
660           DBG(r, "entryp->action == COOKIE_ON_BIT");
661
662           cookie = chxj_save_cookie(r);
663
664           /*
665            * Location Header Check to add cookie parameter.
666            */
667           location_header = (char*)apr_table_get(r->headers_out, "Location");
668           if (location_header) {
669             DBG1(r, "Location Header=[%s]", location_header);
670             location_header = chxj_add_cookie_parameter(r,
671                                                         location_header,
672                                                         cookie);
673             apr_table_setn(r->headers_out, "Location", location_header);
674             DBG1(r, "Location Header=[%s]", location_header);
675           }
676         }
677
678         apr_table_setn(r->headers_out, "Content-Length", "0");
679         rv = pass_data_to_filter(f, (const char*)"", (apr_size_t)0);
680
681         return rv;
682       }
683     }
684     else
685     if (apr_bucket_read(b, &data, &len, APR_BLOCK_READ) == APR_SUCCESS) {
686       DBG2(r, "read data[%.*s]",len, data);
687
688       if (f->ctx == NULL) {
689         /*--------------------------------------------------------------------*/
690         /* Start                                                              */
691         /*--------------------------------------------------------------------*/
692         DBG(r, "new context");
693         ctx = (mod_chxj_ctx*)apr_palloc(r->pool, sizeof(mod_chxj_ctx));
694         if (len > 0) {
695           ctx->buffer = apr_palloc(r->pool, len);
696           memcpy(ctx->buffer, data, len);
697         }
698         else {
699           ctx->buffer = apr_palloc(r->pool, 1);
700           ctx->buffer = '\0';
701         }
702         ctx->len = len;
703         f->ctx = (void*)ctx;
704       }
705       else {
706         /*--------------------------------------------------------------------*/
707         /* append data                                                        */
708         /*--------------------------------------------------------------------*/
709         char* tmp;
710         DBG(r, "append data start");
711         ctx = (mod_chxj_ctx*)f->ctx;
712
713         if (len > 0) {
714           tmp = apr_palloc(r->pool, ctx->len);
715           memcpy(tmp, ctx->buffer, ctx->len);
716
717           ctx->buffer = apr_palloc(r->pool, ctx->len + len);
718
719           memcpy(ctx->buffer, tmp, ctx->len);
720           memcpy(&ctx->buffer[ctx->len], data, len);
721
722           ctx->len += len;
723         }
724         DBG(r, "append data end");
725       }
726     }
727   }
728   apr_brigade_destroy(bb);
729
730   DBG(r, "end of output filter");
731
732   return APR_SUCCESS;
733 }
734
735
736 /**
737  * It is the main loop of the input filter. 
738  *
739  * @param f         [i/o] It is a filter.
740  * @param bb        [i]   brigade
741  * @param mode      [i]   mode
742  * @param block     [i]   block
743  * @param readbytes [i]   readbyte
744  */
745 static apr_status_t 
746 chxj_input_filter(ap_filter_t*        f, 
747                  apr_bucket_brigade*  bb,
748                  ap_input_mode_t      mode, 
749                  apr_read_type_e      block,
750                  apr_off_t            readbytes)
751 {
752   request_rec*        r;
753   apr_status_t        rv;
754   conn_rec*           c;
755   apr_bucket*         b;
756   /*--------------------------------------------------------------------------*/
757   /* It is the brigade area for output                                        */
758   /*--------------------------------------------------------------------------*/
759   apr_bucket_brigade* ibb;            
760   /*--------------------------------------------------------------------------*/
761   /* It is the brigade area for input                                         */
762   /*--------------------------------------------------------------------------*/
763   apr_bucket_brigade* obb;
764   apr_size_t          len;
765   apr_bucket*         tmp_heap;
766   apr_bucket*         eos;
767   const char*         data;
768   char*               data_bucket;
769   char*               data_brigade;
770   char*               content_type;
771   device_table*       spec ;
772   char*               user_agent;
773   mod_chxj_config*    dconf;
774   chxjconvrule_entry* entryp;
775
776   r = f->r;
777   c = r->connection;
778
779   DBG(r, "start of chxj_input_filter()");
780
781   data_brigade = qs_alloc_zero_byte_string(r);
782
783
784   ibb = apr_brigade_create(r->pool, c->bucket_alloc);
785   obb = apr_brigade_create(r->pool, c->bucket_alloc);
786
787   content_type = (char*)apr_table_get(r->headers_in, "Content-Type");
788   if (content_type 
789   && strncasecmp("multipart/form-data", content_type, 19) == 0) {
790
791     DBG(r, "detect multipart/form-data");
792     ap_remove_input_filter(f);
793
794     return ap_get_brigade(f->next, bb, mode, block, readbytes);
795   }
796
797   dconf = ap_get_module_config(r->per_dir_config, &chxj_module);
798
799   entryp = chxj_apply_convrule(r, dconf->convrules);
800   if (!entryp || !(entryp->action & CONVRULE_ENGINE_ON_BIT)) {
801     DBG(r,"EngineOff");
802
803     ap_remove_input_filter(f);
804     return ap_get_brigade(f->next, bb, mode, block, readbytes);
805   }
806
807   user_agent = (char*)apr_table_get(r->headers_in, "User-Agent");
808   spec = chxj_specified_device(r, user_agent);
809
810   switch(spec->html_spec_type) {
811   case CHXJ_SPEC_Chtml_1_0:
812   case CHXJ_SPEC_Chtml_2_0:
813   case CHXJ_SPEC_Chtml_3_0:
814   case CHXJ_SPEC_Chtml_4_0:
815   case CHXJ_SPEC_Chtml_5_0:
816   case CHXJ_SPEC_XHtml_Mobile_1_0:
817   case CHXJ_SPEC_Hdml:
818   case CHXJ_SPEC_Jhtml:
819     break;
820
821   default:
822     ap_remove_input_filter(f);
823     return ap_get_brigade(f->next, bb, mode, block, readbytes);
824   }
825
826
827   rv = ap_get_brigade(f->next, ibb, mode, block, readbytes);
828   if (rv != APR_SUCCESS) {
829     DBG(r, "ap_get_brigade() failed");
830     return rv;
831   }
832
833   APR_BRIGADE_FOREACH(b, ibb) {
834     rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
835     if (rv != APR_SUCCESS) {
836       DBG(r, "apr_bucket_read() failed");
837       return rv;
838     }
839
840     if (data != NULL) {
841       data_bucket = apr_palloc(r->pool, len+1);
842       memset((void*)data_bucket, 0, len+1);
843       memcpy(data_bucket, data, len);
844       DBG1(r, "(in)POSTDATA:[%s]", data_bucket);
845   
846       data_brigade = apr_pstrcat(r->pool, data_brigade, data_bucket, NULL);
847     }
848
849     if (APR_BUCKET_IS_EOS(b)) {
850       break;
851     }
852   }
853   apr_brigade_cleanup(ibb);
854
855
856   len = strlen(data_brigade);
857   if (len == 0) {
858     DBG(r,"data_brigade length is 0");
859     DBG(r,"end of chxj_input_filter()");
860     ap_remove_input_filter(f);
861     return ap_get_brigade(f->next, bb, mode, block, readbytes);
862   }
863
864   data_brigade = chxj_input_convert(
865     r, 
866     (const char**)&data_brigade, 
867     (apr_size_t*)&len,
868     entryp
869     );
870
871   if (len > 0) {
872     DBG1(r, "(in:exchange)POSTDATA:[%s]", data_brigade);
873
874     obb = apr_brigade_create(r->pool, c->bucket_alloc);
875
876     tmp_heap = apr_bucket_heap_create(data_brigade, 
877                                       len, 
878                                       NULL, 
879                                       f->c->bucket_alloc);
880     eos      = apr_bucket_eos_create(f->c->bucket_alloc);
881
882     APR_BRIGADE_INSERT_TAIL(obb, tmp_heap);
883     APR_BRIGADE_INSERT_TAIL(obb, eos);
884     APR_BRIGADE_CONCAT(bb, obb);
885   }
886
887   DBG(r, "end of chxj_input_filter()");
888
889   return APR_SUCCESS;
890 }
891
892
893 static mod_chxj_global_config*
894 chxj_global_config_create(apr_pool_t* pool, server_rec* s)
895 {
896   mod_chxj_global_config* conf;
897
898   SDBG(s, "start chxj_global_config_create()");
899
900   /*--------------------------------------------------------------------------*/
901   /* allocate an own subpool which survives server restarts                   */
902   /*--------------------------------------------------------------------------*/
903   conf = (mod_chxj_global_config*)apr_palloc(pool, 
904                   sizeof(mod_chxj_global_config));
905 #if 0
906   conf->cookie_db_lock = NULL;
907 #endif
908   SDBG(s, "end   chxj_global_config_create()");
909
910   return conf;
911 }
912
913
914 /**
915  * initialize chxj module
916  */
917 static int 
918 chxj_init_module(apr_pool_t *p, 
919                   apr_pool_t *plog, 
920                   apr_pool_t *ptemp, 
921                   server_rec *s)
922 {
923 #if 0
924   mod_chxj_global_config* conf;
925 #endif
926   void *user_data;
927
928   SDBG(s, "start chxj_init_module()");
929
930   apr_pool_userdata_get(&user_data, CHXJ_MOD_CONFIG_KEY, s->process->pool);
931   SDBG(s, " ");
932   if (user_data == NULL) {
933     SDBG(s, " ");
934     /*
935      * dummy user_data set.
936      */
937     apr_pool_userdata_set(
938       (const void *)(1), 
939       CHXJ_MOD_CONFIG_KEY, 
940       apr_pool_cleanup_null, 
941       s->process->pool);
942     SDBG(s, "end  chxj_init_module()");
943     return OK;
944   }
945
946   ap_add_version_component(p, CHXJ_VERSION_PREFIX CHXJ_VERSION);
947
948
949 #if 0
950   conf = (mod_chxj_global_config *)ap_get_module_config(s->module_config, 
951                                                         &chxj_module);
952
953   if (apr_global_mutex_create(&(conf->cookie_db_lock), 
954                               NULL, APR_LOCK_DEFAULT, p) != APR_SUCCESS) {
955     SERR(s, "end  chxj_init_module()");
956     return HTTP_INTERNAL_SERVER_ERROR;
957   }
958
959 #ifdef AP_NEED_SET_MUTEX_PERMS
960   if (unixd_set_global_mutex_perms(conf->cookie_db_lock) != APR_SUCCESS) {
961     SERR(s, "end  chxj_init_module()");
962     return HTTP_INTERNAL_SERVER_ERROR;
963   }
964 #endif
965 #endif
966
967   SDBG(s, "end  chxj_init_module()");
968
969   return OK;
970 }
971
972
973 static void 
974 chxj_child_init(apr_pool_t *p, server_rec *s)
975 {
976 #if 0
977   mod_chxj_global_config* conf;
978 #endif
979
980   SDBG(s, "start chxj_child_init()");
981
982 #if 0
983   conf = (mod_chxj_global_config*)ap_get_module_config(s->module_config, 
984                                                        &chxj_module);
985
986   if (apr_global_mutex_child_init(&conf->cookie_db_lock, NULL, p) 
987   != APR_SUCCESS) {
988     SERR(s, "Can't attach global mutex.");
989     return;
990   }
991 #endif
992
993   SDBG(s, "end   chxj_child_init()");
994 }
995
996
997 /**
998  * A set structure of each server is generated. 
999  * 
1000  * @param p
1001  * @param s
1002  */
1003 static void*
1004 chxj_config_server_create(apr_pool_t *p, server_rec *s)
1005 {
1006   mod_chxj_global_config *gc;
1007
1008   gc = chxj_global_config_create(p,s);
1009
1010   return gc;
1011 }
1012
1013
1014 static int
1015 chxj_translate_name(request_rec *r)
1016 {
1017   return chxj_trans_name(r);
1018 }
1019
1020
1021 /**
1022  * The hook is registered.
1023  *
1024  * @param p
1025  */
1026 static void 
1027 chxj_register_hooks(apr_pool_t *p)
1028 {
1029   ap_hook_post_config(chxj_init_module,
1030                       NULL,
1031                       NULL,
1032                       APR_HOOK_REALLY_FIRST);
1033   ap_hook_child_init(chxj_child_init, 
1034                      NULL, 
1035                      NULL, 
1036                      APR_HOOK_REALLY_FIRST);
1037   ap_register_output_filter (
1038                       "chxj_output_filter", 
1039                       chxj_output_filter, 
1040                       NULL, 
1041                       AP_FTYPE_RESOURCE);
1042   ap_register_input_filter(
1043                       "chxj_input_filter", 
1044                       chxj_input_filter, 
1045                       NULL, 
1046                       AP_FTYPE_RESOURCE);
1047   ap_hook_handler(chxj_img_conv_format_handler, NULL, NULL, APR_HOOK_MIDDLE);
1048   ap_hook_handler(chxj_qr_code_handler, NULL, NULL, APR_HOOK_MIDDLE);
1049   ap_hook_translate_name(chxj_translate_name, NULL, NULL, APR_HOOK_MIDDLE);
1050   ap_hook_fixups(chxj_headers_fixup, NULL, NULL, APR_HOOK_LAST);
1051 }
1052
1053
1054 /**
1055  * A set structure according to the directory is generated. 
1056  *
1057  * @param p
1058  * @param arg
1059  */
1060 static void* 
1061 chxj_create_per_dir_config(apr_pool_t *p, char *arg) 
1062 {
1063   mod_chxj_config* conf;
1064
1065   conf = apr_pcalloc(p, sizeof(mod_chxj_config));
1066   conf->device_data_file = NULL;
1067   conf->devices          = NULL;
1068   conf->emoji_data_file  = NULL;
1069   conf->emoji            = NULL;
1070   conf->emoji_tail       = NULL;
1071   conf->image            = CHXJ_IMG_OFF;
1072   conf->image_cache_dir  = apr_psprintf(p, "%s",DEFAULT_IMAGE_CACHE_DIR);
1073   conf->server_side_encoding = NULL;
1074   if (arg == NULL) {
1075     conf->dir                  = NULL;
1076   }
1077   else {
1078     conf->dir                  = apr_pcalloc(p, strlen(arg)+1);
1079     memset(conf->dir, 0, strlen(arg)+1);
1080     strcpy(conf->dir, arg);
1081   }
1082   conf->convrules   = apr_array_make(p, 2, sizeof(chxjconvrule_entry));
1083
1084   /* Default is copyleft */
1085   conf->image_copyright = NULL; 
1086
1087   return conf;
1088 }
1089
1090
1091 /*
1092  *  Merge per-directory CHXJ configurations
1093  */
1094 static void*
1095 chxj_merge_per_dir_config(apr_pool_t *p, void *basev, void *addv)
1096 {
1097   mod_chxj_config *base;
1098   mod_chxj_config *add;
1099   mod_chxj_config *mrg;
1100
1101   base = (mod_chxj_config*)basev;
1102   add  = (mod_chxj_config*)addv;
1103   mrg  = (mod_chxj_config*)apr_palloc(p, sizeof(mod_chxj_config));
1104
1105   mrg->device_data_file = NULL;
1106   mrg->devices          = NULL;
1107   mrg->emoji_data_file  = NULL;
1108   mrg->image            = CHXJ_IMG_OFF;
1109   mrg->image_cache_dir  = NULL;
1110   mrg->image_copyright  = NULL;
1111   mrg->emoji            = NULL;
1112   mrg->emoji_tail       = NULL;
1113
1114   mrg->dir = apr_pstrdup(p, add->dir);
1115
1116   if (! add->device_data_file) {
1117     mrg->devices = base->devices;
1118     mrg->device_data_file = apr_pstrdup(p, base->device_data_file);
1119   }
1120   else {
1121     mrg->devices = add->devices;
1122     mrg->device_data_file = apr_pstrdup(p, add->device_data_file);
1123   }
1124
1125   if (! add->emoji_data_file) {
1126     mrg->emoji = base->emoji;
1127     mrg->emoji_tail = base->emoji_tail;
1128     mrg->emoji_data_file = apr_pstrdup(p, base->emoji_data_file);
1129   }
1130   else {
1131     mrg->emoji = add->emoji;
1132     mrg->emoji_tail = add->emoji_tail;
1133     mrg->emoji_data_file = apr_pstrdup(p, add->emoji_data_file);
1134   }
1135
1136   if (add->image == CHXJ_IMG_OFF) 
1137     mrg->image = base->image;
1138   else 
1139     mrg->image = add->image;
1140
1141
1142   if (strcasecmp(add->image_cache_dir ,DEFAULT_IMAGE_CACHE_DIR)==0) 
1143     mrg->image_cache_dir = apr_pstrdup(p, base->image_cache_dir);
1144   else 
1145     mrg->image_cache_dir = apr_pstrdup(p, add->image_cache_dir);
1146
1147   if (add->image_copyright) 
1148     mrg->image_copyright = apr_pstrdup(p, add->image_copyright);
1149   else
1150     mrg->image_copyright = apr_pstrdup(p, base->image_copyright);
1151
1152   if (add->server_side_encoding) {
1153     mrg->server_side_encoding = apr_pstrdup(p, add->server_side_encoding);
1154   }
1155   else 
1156   if (base->server_side_encoding) {
1157     mrg->server_side_encoding = apr_pstrdup(p, base->server_side_encoding);
1158   }
1159   else {
1160     mrg->server_side_encoding = apr_pstrdup(p, DEFAULT_SERVER_SIDE_ENCODING);
1161   }
1162
1163   mrg->convrules    = apr_array_append(p, add->convrules, base->convrules);
1164
1165   return mrg;
1166 }
1167
1168
1169 static int
1170 chxj_command_parse_take5(
1171   const char* arg, 
1172   char** prm1, 
1173   char** prm2, 
1174   char** prm3, 
1175   char** prm4, 
1176   char** prm5)
1177 {
1178   int isquoted;
1179   char* strp;
1180
1181   strp = (char*)arg;
1182
1183   for (;*strp == ' '||*strp == '\t'; strp++) ;
1184
1185   isquoted = 0; 
1186   if (*strp == '"') { 
1187     isquoted = 1;
1188     strp++;
1189   }
1190
1191   *prm1 = strp;
1192
1193   for (; *strp != '\0'; strp++) {
1194     if ((isquoted && (*strp == ' ' || *strp == '\t'))
1195     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1196       strp++;
1197       continue;
1198     }
1199
1200     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1201     ||  (isquoted  && *strp == '"'))
1202       break;
1203   }
1204
1205   if (! *strp) {
1206     *prm2 = strp;
1207     *prm3 = strp;
1208     *prm4 = strp;
1209     *prm5 = strp;
1210     return 1;
1211   }
1212
1213   *strp++ = '\0';
1214
1215   for (;*strp == ' '||*strp == '\t'; strp++) ;
1216
1217   isquoted = 0; 
1218   if (*strp == '"') { 
1219     isquoted = 1;
1220     strp++;
1221   }
1222
1223   *prm2 = strp;
1224   for (; *strp != '\0'; strp++) {
1225     if ((isquoted && (*strp == ' ' || *strp == '\t'))
1226     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1227       strp++;
1228       continue;
1229     }
1230
1231     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1232     ||  (isquoted  && *strp == '"'))
1233       break;
1234   }
1235
1236   if (! *strp) {
1237     *prm3 = strp;
1238     *prm4 = strp;
1239     *prm5 = strp;
1240     return 0;
1241   }
1242
1243   *strp++ = '\0';
1244
1245   for (;*strp == ' '||*strp == '\t'; strp++);
1246
1247   isquoted = 0; 
1248   if (*strp == '"') { 
1249     isquoted = 1;
1250     strp++;
1251   }
1252   *prm3 = strp;
1253   for (; *strp != '\0'; strp++) {
1254     if ((isquoted && (*strp == ' ' || *strp == '\t'))
1255     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1256       strp++;
1257       continue;
1258     }
1259
1260     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1261     ||  (isquoted  && *strp == '"'))
1262       break;
1263   }
1264
1265   if (! *strp) {
1266     *prm4 = strp;
1267     *prm5 = strp;
1268     return 0;
1269   }
1270
1271   *strp++ = '\0';
1272
1273   for (;*strp == ' '||*strp == '\t'; strp++);
1274
1275   isquoted = 0; 
1276   if (*strp == '"') { 
1277     isquoted = 1;
1278     strp++;
1279   }
1280   *prm4 = strp;
1281   for (; *strp != '\0'; strp++) {
1282     if ((isquoted && (*strp == ' ' || *strp == '\t'))
1283     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1284       strp++;
1285       continue;
1286     }
1287
1288     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1289     ||  (isquoted  && *strp == '"'))
1290       break;
1291   }
1292
1293   if (! *strp) {
1294     *prm5 = strp;
1295     return 0;
1296   }
1297
1298   *strp++ = '\0';
1299
1300   for (;*strp == ' '||*strp == '\t'; strp++);
1301
1302   isquoted = 0; 
1303   if (*strp == '"') { 
1304     isquoted = 1;
1305     strp++;
1306   }
1307   *prm5 = strp;
1308   for (; *strp != '\0'; strp++) {
1309     if ((isquoted && (*strp == ' ' || *strp == '\t'))
1310     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1311       strp++;
1312       continue;
1313     }
1314
1315     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1316     ||  (isquoted  && *strp == '"'))
1317       break;
1318   }
1319   *strp = '\0';
1320
1321   return 0;
1322 }
1323
1324
1325 /**
1326  * The device definition file is loaded. 
1327  *
1328  * @param arg     [i]   The name of the device definition file is specified.
1329  * @param mconfig [i/o] The pointer to a set structure is specified. 
1330  * @param parms   [i]   
1331  */
1332 static const char* 
1333 cmd_load_device_data(cmd_parms *parms, void *mconfig, const char* arg) 
1334 {
1335   mod_chxj_config* conf;
1336   Doc              doc;
1337
1338   doc.r = NULL;
1339
1340   if (strlen(arg) > 256) 
1341     return "device data filename too long.";
1342
1343   conf = (mod_chxj_config*)mconfig;
1344   conf->device_data_file = apr_pstrdup(parms->pool, arg);
1345
1346   qs_init_malloc(&doc);
1347   qs_init_root_node(&doc);
1348
1349   qs_parse_file((Doc*)&doc, (const char*)arg);
1350   chxj_load_device_data(&doc,parms->pool, conf);
1351   qs_all_free(&doc, QX_LOGMARK);
1352
1353   return NULL;
1354 }
1355
1356
1357 /**
1358  * Device definition information is loaded. 
1359  *
1360  * @param parms 
1361  * @param arg     [i]   The name of the device definition file is specified. 
1362  * @param mconfig [i/o] The pointer to a set structure is specified. 
1363  * @return 
1364  */
1365 static const char* 
1366 cmd_load_emoji_data(cmd_parms *parms, void *mconfig, const char* arg) 
1367 {
1368   mod_chxj_config* conf;
1369   char*            rtn;
1370   Doc              doc;
1371
1372   doc.r = NULL;
1373
1374
1375   if (strlen(arg) > 256) 
1376     return "emoji data filename too long.";
1377
1378   conf = (mod_chxj_config*)mconfig;
1379   conf->emoji_data_file = apr_pstrdup(parms->pool, arg);
1380   qs_init_malloc(&doc);
1381   qs_init_root_node(&doc);
1382
1383   qs_parse_file((Doc*)&doc, (const char*)arg);
1384
1385   rtn = chxj_load_emoji_data(&doc,parms->pool, conf);
1386
1387   qs_all_free(&doc, QX_LOGMARK);
1388
1389
1390   return rtn;
1391 }
1392
1393
1394 static const char* 
1395 cmd_set_image_engine(cmd_parms *parms, void *mconfig, const char* arg) 
1396 {
1397   mod_chxj_config* conf;
1398   Doc              doc;
1399
1400   doc.r = NULL;
1401
1402   if (strlen(arg) > 256) 
1403     return "image uri is too long.";
1404
1405   conf = (mod_chxj_config*)mconfig;
1406   if (strcasecmp("ON", arg) == 0)
1407     conf->image = CHXJ_IMG_ON;
1408   else
1409     conf->image = CHXJ_IMG_OFF;
1410
1411   return NULL;
1412 }
1413
1414
1415 static const char* 
1416 cmd_set_image_cache_dir(cmd_parms *parms, void *mconfig, const char* arg) 
1417 {
1418   mod_chxj_config* conf;
1419   Doc              doc;
1420
1421   doc.r = NULL;
1422
1423   if (strlen(arg) > 256) 
1424     return "cache dir name is too long.";
1425
1426   conf = (mod_chxj_config*)mconfig;
1427   conf->image_cache_dir = apr_pstrdup(parms->pool, arg);
1428
1429   return NULL;
1430 }
1431
1432
1433 static const char* 
1434 cmd_set_image_copyright(cmd_parms *parms, void* mconfig, const char* arg) 
1435 {
1436   mod_chxj_config* conf;
1437   Doc              doc;
1438
1439   doc.r = NULL;
1440
1441   if (strlen(arg) > 256) 
1442     return "Copyright Flag is too long.";
1443
1444   conf = (mod_chxj_config*)mconfig;
1445   conf->image_copyright = apr_pstrdup(parms->pool, arg);
1446
1447   return NULL;
1448 }
1449
1450
1451 static const char*
1452 cmd_convert_rule(cmd_parms *cmd, void* mconfig, const char *arg)
1453 {
1454   int                 mode;
1455   ap_regex_t*         regexp;
1456   mod_chxj_config*    dconf;
1457   chxjconvrule_entry* newrule;
1458   char*               prm1;
1459   char*               prm2;
1460   char*               prm3;
1461   char*               prm4;
1462   char*               prm5;
1463   char*               pstate;
1464   char*               action;
1465   char*               pp;
1466
1467   dconf = (mod_chxj_config*)mconfig;
1468
1469   if (strlen(arg) > 4096) 
1470     return "ChxjConvertRule: is too long.";
1471
1472   dconf = (mod_chxj_config*)mconfig;
1473   if (dconf->convrules == NULL)
1474     dconf->convrules   = apr_array_make(cmd->pool, 
1475                                         2, 
1476                                         sizeof(chxjconvrule_entry));
1477
1478   newrule = apr_array_push(dconf->convrules);
1479
1480   newrule->flags  = 0;
1481   newrule->action = 0;
1482
1483   if (chxj_command_parse_take5(arg, &prm1, &prm2, &prm3, &prm4, &prm5))
1484     return "ChxjConvertRule: bad argument line";
1485
1486   newrule->pattern = apr_pstrdup(cmd->pool, prm1);
1487
1488   /* Parse action */
1489   for (;;) {
1490     if ((action = apr_strtok(prm2, ",", &pstate)) == NULL)
1491       break;
1492     prm2 = NULL;
1493     switch(*action) {
1494     case 'e':
1495     case 'E':
1496       if (strcasecmp(CONVRULE_ENGINE_ON_CMD, action) == 0) {
1497         newrule->action |= CONVRULE_ENGINE_ON_BIT;
1498       }
1499       else
1500       if (strcasecmp(CONVRULE_ENGINE_OFF_CMD, action) == 0) {
1501         newrule->action |= CONVRULE_ENGINE_OFF_BIT;
1502       }
1503       break;
1504
1505     case 'C':
1506     case 'c':
1507       if (strcasecmp(CONVRULE_COOKIE_ON_CMD, action) == 0) {
1508         newrule->action |= CONVRULE_COOKIE_ON_BIT;
1509       }
1510       break;
1511     default:
1512       break;
1513     }
1514   }
1515   
1516   pp = prm1;
1517   if (*pp == '!') {
1518     newrule->flags |= CONVRULE_FLAG_NOTMATCH;
1519     pp++;
1520   }
1521
1522   mode = AP_REG_EXTENDED;
1523   if ((regexp = ap_pregcomp(cmd->pool, pp, mode)) == NULL)
1524     return "RewriteRule: cannot compile regular expression ";
1525
1526   newrule->regexp = regexp;
1527   if (*prm3)
1528     newrule->encoding = apr_pstrdup(cmd->pool, prm3);
1529   else
1530     newrule->encoding = apr_pstrdup(cmd->pool, "none");
1531
1532   newrule->pc_flag = CONVRULE_PC_FLAG_OFF_BIT;
1533   if (*prm4)
1534     if (strcasecmp(CONVRULE_PC_FLAG_ON_CMD, prm4) == 0)
1535       newrule->pc_flag = CONVRULE_PC_FLAG_ON_BIT;
1536
1537   newrule->user_agent = NULL;
1538   if (*prm5)
1539     newrule->user_agent = apr_pstrdup(cmd->pool, prm5);
1540     
1541   return NULL;
1542 }
1543
1544
1545 static const command_rec cmds[] = {
1546   AP_INIT_TAKE1(
1547     "ChxjLoadDeviceData",
1548     cmd_load_device_data,
1549     NULL,
1550     OR_ALL,
1551     "Load Device Data"),
1552   AP_INIT_TAKE1(
1553     "ChxjLoadEmojiData",
1554     cmd_load_emoji_data,
1555     NULL,
1556     OR_ALL,
1557     "Load Emoji Data"),
1558   AP_INIT_TAKE1(
1559     "ChxjImageEngine",
1560     cmd_set_image_engine,
1561     NULL,
1562     OR_ALL,
1563     "Convert Target URI"),
1564   AP_INIT_TAKE1(
1565     "ChxjImageCacheDir",
1566     cmd_set_image_cache_dir,
1567     NULL,
1568     OR_ALL,
1569     "Image Cache Directory"),
1570   AP_INIT_TAKE1(
1571     "ChxjImageCopyright",
1572     cmd_set_image_copyright,
1573     NULL,
1574     OR_ALL,
1575     "Copyright Flag"),
1576   AP_INIT_RAW_ARGS(
1577     "ChxjConvertRule",
1578     cmd_convert_rule,
1579     NULL, 
1580     OR_FILEINFO,
1581     "an URL-applied regexp-pattern and a substitution URL"),
1582   { NULL },
1583 };
1584
1585
1586 /*----------------------------------------------------------------------------*/
1587 /* Dispatch list for API hooks                                                */
1588 /*----------------------------------------------------------------------------*/
1589 module AP_MODULE_DECLARE_DATA chxj_module = {
1590   STANDARD20_MODULE_STUFF, 
1591   chxj_create_per_dir_config,          /* create per-dir    config structures */
1592   chxj_merge_per_dir_config,           /* merge  per-dir    config structures */
1593   chxj_config_server_create,           /* create per-server config structures */
1594   NULL,                                /* merge  per-server config structures */
1595   cmds,                                /* table of config file commands       */
1596   chxj_register_hooks                  /* register hooks                      */
1597 };
1598 /*
1599  * vim:ts=2 et
1600  */