OSDN Git Service

* Fixed bug
[modchxj/mod_chxj.git] / src / chxj_img_conv_format.c
1 /*
2  * Copyright (C) 2005-2009 Atsushi Konno All rights reserved.
3  * Copyright (C) 2005 QSDN,Inc. All rights reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 #include <libgen.h>
18 #include <limits.h>
19 #include "mod_chxj.h"
20 #include "chxj_img_conv_format.h"
21 #include "chxj_specified_device.h"
22 #include "chxj_str_util.h"
23 #include "chxj_qr_code.h"
24 #include "chxj_apply_convrule.h"
25 #include "chxj_url_encode.h"
26 #include "qs_parse_string.h"
27 #include "chxj_preg_replace.h"
28
29 #include "http_core.h"
30
31 #include <wand/magick_wand.h>
32
33 #if !defined(LONG_LONG_MAX) && defined(LLONG_MAX)
34 #  define LONG_LONG_MAX LLONG_MAX
35 #endif
36
37
38 #define EXIT_MAGICK_ERROR() \
39   do { \
40     char *description; ExceptionType severity; \
41     description=MagickGetException(magick_wand,&severity); \
42     ap_log_rerror(APLOG_MARK,APLOG_ERR, 0, r,"%s %s %d %s\n",__FILE__,(__func__),__LINE__,description); \
43     description=(char *) MagickRelinquishMemory(description); \
44     DestroyMagickWand(magick_wand); \
45   } while(0) 
46
47 typedef enum img_conv_mode_t {
48   IMG_CONV_MODE_NORMAL = 0,
49   IMG_CONV_MODE_THUMBNAIL,
50   IMG_CONV_MODE_WALLPAPER,
51   IMG_CONV_MODE_EZGET,
52 } img_conv_mode_t;
53
54 /*----------------------------------------------------------------------------*/
55 /* User-Agent use flag                                                        */
56 /*----------------------------------------------------------------------------*/
57 typedef enum _ua_use_flag_t {
58   UA_USE=0,               /* User-Agent is used.                              */
59   UA_IGN,                 /* User-Agent is disregarded.                       */
60 } ua_use_flag_t;
61
62 /*----------------------------------------------------------------------------*/
63 /* Request parameter maintenance structure                                    */
64 /*----------------------------------------------------------------------------*/
65 typedef struct query_string_param_t query_string_param_t;
66
67 struct query_string_param_t {
68   img_conv_mode_t   mode;
69   char              *user_agent;
70   ua_use_flag_t     ua_flag;
71
72   char              *name;      /* for EZGET */
73   long              offset;    /* for EZGET */
74   long              count;     /* for EZGET */
75   int               width;
76   int               height;
77 };
78
79 /*----------------------------------------------------------------------------*/
80 /* Device_spec when User-Agent is disregarded                                 */
81 /*----------------------------------------------------------------------------*/
82 static device_table v_ignore_spec = {
83   NULL,
84   0,
85   "IGN",
86   "IGN",
87   CHXJ_SPEC_HTML,
88   640,
89   480,
90   640,
91   480,
92   1024*1024,
93   1,
94   1,
95   1,
96   0,
97   0,
98   96,
99   96,
100   65536,
101   NULL,
102   "Shift_JIS"
103 };
104
105 /*----------------------------------------------------------------------------*/
106 /* CRC calculation table for AU                                               */
107 /*----------------------------------------------------------------------------*/
108 static unsigned short  AU_CRC_TBL[256] = {
109   0x0000,0x1021,0x2042,0x3063,0x4084,0x50A5,0x60C6,0x70E7,
110   0x8108,0x9129,0xA14A,0xB16B,0xC18C,0xD1AD,0xE1CE,0xF1EF,
111   0x1231,0x0210,0x3273,0x2252,0x52B5,0x4294,0x72F7,0x62D6,
112   0x9339,0x8318,0xB37B,0xA35A,0xD3BD,0xC39C,0xF3FF,0xE3DE,
113   0x2462,0x3443,0x0420,0x1401,0x64E6,0x74C7,0x44A4,0x5485,
114   0xA56A,0xB54B,0x8528,0x9509,0xE5EE,0xF5CF,0xC5AC,0xD58D,
115   0x3653,0x2672,0x1611,0x0630,0x76D7,0x66F6,0x5695,0x46B4,
116   0xB75B,0xA77A,0x9719,0x8738,0xF7DF,0xE7FE,0xD79D,0xC7BC,
117   0x48C4,0x58E5,0x6886,0x78A7,0x0840,0x1861,0x2802,0x3823,
118   0xC9CC,0xD9ED,0xE98E,0xF9AF,0x8948,0x9969,0xA90A,0xB92B,
119   0x5AF5,0x4AD4,0x7AB7,0x6A96,0x1A71,0x0A50,0x3A33,0x2A12,
120   0xDBFD,0xCBDC,0xFBBF,0xEB9E,0x9B79,0x8B58,0xBB3B,0xAB1A,
121   0x6CA6,0x7C87,0x4CE4,0x5CC5,0x2C22,0x3C03,0x0C60,0x1C41,
122   0xEDAE,0xFD8F,0xCDEC,0xDDCD,0xAD2A,0xBD0B,0x8D68,0x9D49,
123   0x7E97,0x6EB6,0x5ED5,0x4EF4,0x3E13,0x2E32,0x1E51,0x0E70,
124   0xFF9F,0xEFBE,0xDFDD,0xCFFC,0xBF1B,0xAF3A,0x9F59,0x8F78,
125   0x9188,0x81A9,0xB1CA,0xA1EB,0xD10C,0xC12D,0xF14E,0xE16F,
126   0x1080,0x00A1,0x30C2,0x20E3,0x5004,0x4025,0x7046,0x6067,
127   0x83B9,0x9398,0xA3FB,0xB3DA,0xC33D,0xD31C,0xE37F,0xF35E,
128   0x02B1,0x1290,0x22F3,0x32D2,0x4235,0x5214,0x6277,0x7256,
129   0xB5EA,0xA5CB,0x95A8,0x8589,0xF56E,0xE54F,0xD52C,0xC50D,
130   0x34E2,0x24C3,0x14A0,0x0481,0x7466,0x6447,0x5424,0x4405,
131   0xA7DB,0xB7FA,0x8799,0x97B8,0xE75F,0xF77E,0xC71D,0xD73C,
132   0x26D3,0x36F2,0x0691,0x16B0,0x6657,0x7676,0x4615,0x5634,
133   0xD94C,0xC96D,0xF90E,0xE92F,0x99C8,0x89E9,0xB98A,0xA9AB,
134   0x5844,0x4865,0x7806,0x6827,0x18C0,0x08E1,0x3882,0x28A3,
135   0xCB7D,0xDB5C,0xEB3F,0xFB1E,0x8BF9,0x9BD8,0xABBB,0xBB9A,
136   0x4A75,0x5A54,0x6A37,0x7A16,0x0AF1,0x1AD0,0x2AB3,0x3A92,
137   0xFD2E,0xED0F,0xDD6C,0xCD4D,0xBDAA,0xAD8B,0x9DE8,0x8DC9,
138   0x7C26,0x6C07,0x5C64,0x4C45,0x3CA2,0x2C83,0x1CE0,0x0CC1,
139   0xEF1F,0xFF3E,0xCF5D,0xDF7C,0xAF9B,0xBFBA,0x8FD9,0x9FF8,
140   0x6E17,0x7E36,0x4E55,0x5E74,0x2E93,0x3EB2,0x0ED1,0x1EF0 
141 };
142
143 /*----------------------------------------------------------------------------*/
144 /* Download page for AU                                                       */
145 /*----------------------------------------------------------------------------*/
146 static const char *HDML_FIRST_PAGE = 
147   "<HDML VERSION=3.0 TTL=0 PUBLIC=TRUE>\r\n"
148   "  <NODISPLAY>\r\n"
149   "    <ACTION TYPE=ACCEPT TASK=GOSUB DEST=\"device:data/dnld?url=%s&name=%s%s&size=%ld&disposition=%s&title=%s\">\r\n"
150   "  </NODISPLAY>\r\n"
151   "</HDML>\r\n";
152
153 static const char *HDML_SUCCESS_PAGE =
154   "<HDML VERSION=3.0 TTL=0 PUBLIC=TRUE>\r\n"
155   "  <DISPLAY>\r\n"
156   "    <ACTION TYPE=ACCEPT TASK=RETURN>\r\n"
157   "    \x83\x5f\x83\x45\x83\x93\x83\x8d\x81\x5b\x83\x68\x82\xc9\x90\xac\x8c\xf7\x82\xb5\x82\xdc\x82\xb5\x82\xbd\r\n"
158   "  </DISPLAY>\r\n"
159   "<HDML>\r\n";
160
161 static const char *HDML_FAIL_PAGE =
162   "<HDML VERSION=3.0 TTL=0 PUBLIC=TRUE>\r\n"
163   "  <DISPLAY>\r\n"
164   "    <ACTION TYPE=ACCEPT TASK=RETURN>\r\n"
165   "    \x83\x5f\x83\x45\x83\x93\x83\x8d\x81\x5b\x83\x68\x82\xc9\x8e\xb8\x94\x73\x82\xb5\x82\xdc\x82\xb5\x82\xbd\r\n"
166   "  </DISPLAY>\r\n"
167   "<HDML>\r\n";
168
169 static ap_regex_t *v_docomo_serial_pattern1 = NULL;
170 static ap_regex_t *v_docomo_serial_pattern2 = NULL;
171 static ap_regex_t *v_docomo_serial_pattern3 = NULL;
172 static ap_regex_t *v_softbank_serial_pattern1 = NULL;
173
174 /*----------------------------------------------------------------------------*/
175 /* Prototype declaration                                                      */
176 /*----------------------------------------------------------------------------*/
177 static char *s_create_workfile_name(request_rec *, 
178                                     mod_chxj_config *, 
179                                     const char *,
180                                     query_string_param_t *);
181
182 static apr_status_t s_create_cache_file(request_rec          *r, 
183                                         const char           *tmpfile, 
184                                         device_table         *spec,
185                                         apr_finfo_t          *st,
186                                         query_string_param_t *qsp,
187                                         mod_chxj_config      *conf);
188
189 static apr_status_t s_send_cache_file(mod_chxj_config       *conf,
190                                       device_table          *spec,
191                                       query_string_param_t  *query_string,
192                                       request_rec           *r,
193                                       const char            *tmpfile);
194
195 static apr_status_t s_send_original_file(request_rec *r, 
196                                          const char  *originalfile);
197
198 static apr_status_t s_header_only_cache_file(device_table         *spec, 
199                                              query_string_param_t *query_string, 
200                                              request_rec          *r, 
201                                              const char           *tmpfile);
202
203 static query_string_param_t *s_get_query_string_param(request_rec *r);
204
205 static unsigned short s_add_crc(const char *writedata, apr_size_t witebyte);
206
207 static MagickWand *s_fixup_size(MagickWand   *, 
208                                 request_rec  *r, 
209                                 device_table *spec, 
210                                 query_string_param_t *qsp);
211
212 static MagickWand *s_fixup_color(MagickWand *magick_wand, 
213                                  request_rec *r, 
214                                  device_table *spec, 
215                                  img_conv_mode_t mode);
216 static MagickWand *s_fixup_depth(MagickWand* magick_wand, 
217                                  request_rec* r, device_table* spec);
218 static MagickWand *s_img_down_sizing(MagickWand *magick_wand, 
219                                 request_rec *r, device_table *spec);
220
221 static MagickWand *s_add_copyright(MagickWand *magick_wand,
222                                    request_rec *r,
223                                    device_table *spec);
224
225 static char *s_create_blob_data(request_rec *r,
226                                 device_table *spec,
227                                 query_string_param_t *qsp,
228                                 char *indata,
229                                 apr_size_t *len);
230
231 static int s_img_conv_format_from_file(request_rec          *r, 
232                                        mod_chxj_config      *conf, 
233                                        const char           *user_agent,
234                                        query_string_param_t *qsp,
235                                        device_table         *spec);
236 static int s_convert_to_jpeg(MagickWand *magick_wand, request_rec *r, device_table *spec);
237 static int s_convert_to_png(MagickWand *maigck_wand, request_rec *r, device_table *spec);
238 static int s_convert_to_gif(MagickWand *magick_wand, request_rec *r, device_table *spec);
239 static int s_convert_to_bmp(MagickWand *magick_wand, request_rec *r, device_table *spec);
240 static int s_chxj_trans_name2(request_rec *r);
241 static char *s_add_comment_to_png(request_rec *r, char *data, apr_size_t *len);
242
243
244
245 int 
246 chxj_img_conv_format_handler(request_rec *r)
247 {
248   mod_chxj_config       *conf;
249   query_string_param_t  *qsp;
250   char                  *user_agent;
251   device_table          *spec;
252   chxjconvrule_entry    *entryp;
253   int                   rtn;
254
255   DBG(r, "REQ[%X] start chxj_img_conv_format_handler()", (unsigned int)(apr_size_t)r);
256
257   s_chxj_trans_name2(r);
258   
259   if (! r->handler || 
260       (r->handler && !STRCASEEQ('c','C',"chxj-picture",r->handler) && !STRCASEEQ('c','C',"chxj-qrcode",r->handler))) {
261     DBG(r, "REQ[%X] Response Code:[%d]", (unsigned int)(apr_size_t)r, r->status);
262     DBG(r, "REQ[%X] end chxj_img_conv_format_handler() r->handler is[%s]", TO_ADDR(r), r->handler);
263     return DECLINED;
264   }
265
266   qsp = s_get_query_string_param(r);
267   conf = chxj_get_module_config(r->per_dir_config, &chxj_module);
268   if (conf == NULL) {
269     DBG(r, "REQ[%X] end chxj_img_conv_format_handler() conf is null",TO_ADDR(r));
270     return DECLINED;
271   }
272
273   if (STRCASEEQ('c','C',"chxj-qrcode",r->handler) && conf->image == CHXJ_IMG_OFF) {
274     DBG(r, "REQ[%X] end chxj_img_conv_format_handler() chxj-qrcode and ImageEngineOff", (unsigned int)(apr_size_t)r);
275     return DECLINED;
276   }
277
278   /*------------------------------------------------------------------------*/
279   /* get UserAgent from http header                                         */
280   /*------------------------------------------------------------------------*/
281   /*--------------------------------------------------------------------------*/
282   /* User-Agent to spec                                                       */
283   /*--------------------------------------------------------------------------*/
284   if (qsp->user_agent) {
285     user_agent = apr_pstrdup(r->pool, qsp->user_agent);
286   }
287   else {
288     entryp = chxj_apply_convrule(r, conf->convrules);
289     if (entryp && entryp->user_agent) {
290       user_agent = (char*)apr_table_get(r->headers_in, CHXJ_HTTP_USER_AGENT);
291     }
292     else {
293       user_agent = (char*)apr_table_get(r->headers_in, HTTP_USER_AGENT);
294     }
295   }
296
297
298
299   if (qsp->ua_flag == UA_IGN)
300     spec = &v_ignore_spec;
301   else
302     spec = chxj_specified_device(r, user_agent);
303
304   DBG(r,"REQ[%X] found device_name=[%s]", (unsigned int)(apr_size_t)r, spec->device_name);
305   DBG(r,"REQ[%X] User-Agent=[%s]", (unsigned int)(apr_size_t)r, user_agent);
306
307
308   rtn = s_img_conv_format_from_file(r, conf, user_agent, qsp, spec);
309   DBG(r, "REQ[%X] end chxj_img_conv_format_handler()", (unsigned int)(apr_size_t)r);
310   return rtn;
311 }
312
313
314
315 /**
316  * It converts it from ImageData corresponding to each model.
317  *
318  * @param r   [i]
319  * @param src [i]   It is former image binary data.
320  * @param len [i/o] It is length of former image binary data.
321  */
322 char *
323 chxj_convert_image(request_rec *r, const char **src, apr_size_t *len)
324 {
325   mod_chxj_config       *conf;
326   query_string_param_t  *qsp;
327   char                  *user_agent;
328   device_table          *spec;
329   char                  *dst;
330   char                  *conv_check;
331   chxjconvrule_entry    *entryp;
332
333   DBG(r, "REQ[%X] start chxj_convert_image()",(unsigned int)(apr_size_t)r);
334
335   conv_check = (char*)apr_table_get(r->headers_in, "CHXJ_IMG_CONV");
336   if (conv_check) {
337     DBG(r, "REQ[%X] end chxj_convert_image() already convert.", (unsigned int)(apr_size_t)r);
338     return NULL;
339   }
340
341
342   qsp = s_get_query_string_param(r);
343   conf = chxj_get_module_config(r->per_dir_config, &chxj_module);
344   if (conf == NULL) {
345     DBG(r, "REQ[%X] end chxj_convert_image()", (unsigned int)(apr_size_t)r);
346     return NULL;
347   }
348
349   /*--------------------------------------------------------------------------*/
350   /* User-Agent to spec                                                       */
351   /*--------------------------------------------------------------------------*/
352   if (qsp->user_agent) {
353     user_agent = apr_pstrdup(r->pool, qsp->user_agent);
354   }
355   else {
356     entryp = chxj_apply_convrule(r, conf->convrules);
357     if (entryp && entryp->user_agent) {
358       user_agent = (char*)apr_table_get(r->headers_in, CHXJ_HTTP_USER_AGENT);
359     }
360     else {
361       user_agent = (char*)apr_table_get(r->headers_in, HTTP_USER_AGENT);
362     }
363   }
364
365   if (qsp->ua_flag == UA_IGN)
366     spec = &v_ignore_spec;
367   else
368     spec = chxj_specified_device(r, user_agent);
369
370   DBG(r,"found device_name=[%s]", spec->device_name);
371   DBG(r, "User-Agent=[%s]", user_agent);
372
373   if (spec->width == 0 || spec->heigh == 0) {
374     DBG(r, "REQ[%X] end chxj_convert_image() not convert (width or height is 0)", (unsigned int)(apr_size_t)r);
375     return NULL;
376   }
377
378   dst = s_create_blob_data(r, spec, qsp, (char*)*src, len);
379   if (dst == NULL) 
380     *len = 0;
381
382   DBG(r, "REQ[%X] end chxj_convert_image()", (unsigned int)(apr_size_t)r);
383
384   return dst;
385 }
386
387
388 static int
389 s_img_conv_format_from_file(
390                 request_rec          *r, 
391                 mod_chxj_config      *conf, 
392                 const char           *user_agent,
393                 query_string_param_t *qsp,
394                 device_table         *spec)
395 {
396   apr_status_t   rv;
397   apr_finfo_t    st;
398   apr_finfo_t    cache_st;
399   char           *tmpfile;
400   int            try_count;
401
402   if (spec->html_spec_type == CHXJ_SPEC_UNKNOWN) {
403     /* 
404      * If ``ua'' parameter is specified, it must be CHXJ_SPEC_HTML. 
405      */
406     return s_send_original_file(r, r->filename);
407   }
408
409   /*--------------------------------------------------------------------------*/
410   /* Create Workfile Name                                                     */
411   /*--------------------------------------------------------------------------*/
412   tmpfile = s_create_workfile_name(r, conf, user_agent, qsp);
413   DBG(r,"REQ[%X] workfile=[%s]", TO_ADDR(r), tmpfile);
414
415   rv = apr_stat(&st, r->filename, APR_FINFO_MIN, r->pool);
416   if (rv != APR_SUCCESS)
417     return HTTP_NOT_FOUND;
418
419   apr_table_setn(r->headers_in, "CHXJ_IMG_CONV", "done");
420   try_count = CACHE_RETRY_COUNT;
421   do {
422     rv = apr_stat(&cache_st, tmpfile, APR_FINFO_MIN, r->pool);
423   
424     if (rv != APR_SUCCESS || cache_st.ctime < st.mtime) {
425       /*------------------------------------------------------------------------*/
426       /* It tries to make the cash file when it doesn't exist or there is       */
427       /* change time later since the making time of the cash file.              */
428       /*------------------------------------------------------------------------*/
429       rv = s_create_cache_file(r,tmpfile, spec, &st, qsp, conf);
430       if (rv != OK)
431         return rv;
432     }
433   
434     DBG(r,"REQ[%X] color=[%d]", TO_ADDR(r), spec->color);
435     if (! r->header_only)  {
436       rv = s_send_cache_file(conf, spec, qsp,r, tmpfile);
437     }
438     else {
439       rv = s_header_only_cache_file(spec, qsp, r, tmpfile);
440     }
441     if (rv == OK) break;
442     if (rv == HTTP_NOT_FOUND) {
443       DBG(r, "recheck wait... try_count[%d]", try_count);
444       apr_sleep(CACHE_RECHECK_WAIT);
445     }
446   } while (try_count--); 
447   if (try_count <= 0) {
448     WRN(r, "cache retry failure....");
449     WRN(r, "cache file was deleted...");
450   }
451
452   DBG(r,"end chxj_img_conv_format");
453
454   return rv;
455 }
456
457
458 static apr_status_t
459 s_create_cache_file(request_rec          *r, 
460                     const char           *tmpfile, 
461                     device_table         *spec, 
462                     apr_finfo_t          *st, 
463                     query_string_param_t *qsp,
464                     mod_chxj_config      *conf)
465 {
466   apr_status_t       rv;
467   apr_size_t         readbyte;
468   apr_size_t         writebyte;
469   unsigned short     crc;
470   img_conv_mode_t    mode = qsp->mode;
471
472   char *writedata = NULL;
473   char *readdata  = NULL;
474
475   apr_file_t  *fout;
476   apr_file_t  *fin;
477   apr_finfo_t cache_dir_st;
478
479   MagickWand *magick_wand;
480   int        img_count;
481
482   if (STRCASEEQ('c','C',"chxj-qrcode",r->handler)) {
483     /*------------------------------------------------------------------------*/
484     /* QRCODE用のファイルの場合                                               */
485     /*------------------------------------------------------------------------*/
486     Doc       doc;
487     Node      *root;
488     qr_code_t qrcode;
489     int       sts;
490
491     memset(&doc,    0, sizeof(Doc));
492     memset(&qrcode, 0, sizeof(qr_code_t));
493     doc.r = r;
494     doc.parse_mode  = PARSE_MODE_CHTML;
495     qrcode.doc      = &doc;
496     qrcode.r        = r;
497
498     qs_init_malloc(&doc);
499
500     root = qs_parse_file(&doc, r->filename);
501
502     chxj_qrcode_node_to_qrcode(&qrcode, root);
503
504     qs_all_free(&doc,QX_LOGMARK);
505
506     sts = chxj_qrcode_create_image_data(&qrcode, &readdata, &readbyte);
507     if (sts != OK) {
508       ERR(r, "qrcode create failed.");
509       return sts;
510     }
511   }
512   else {
513     /*------------------------------------------------------------------------*/
514     /* 通常のイメージファイルの場合                                           */
515     /*------------------------------------------------------------------------*/
516     rv = apr_file_open(&fin, 
517                     r->filename, 
518                     APR_FOPEN_READ | APR_FOPEN_BINARY | APR_FOPEN_BUFFERED | APR_FOPEN_SHARELOCK | APR_FOPEN_SENDFILE_ENABLED,
519                     APR_OS_DEFAULT, 
520                     r->pool);
521     if (rv != APR_SUCCESS) {
522       DBG(r,"file open failed.[%s]", r->filename);
523       return HTTP_NOT_FOUND;
524     }
525   
526     readdata = apr_palloc(r->pool, st->size);
527     rv = apr_file_read_full(fin, (void*)readdata, st->size, &readbyte);
528     if (rv != APR_SUCCESS || readbyte != st->size) {
529       DBG(r,"REQ[%X] file read failed.[%s]", TO_ADDR(r), r->filename);
530       apr_file_close(fin);
531   
532       return HTTP_NOT_FOUND;
533     }
534   }
535   DBG(r,"REQ[%X] start img convert", TO_ADDR(r));
536
537
538   magick_wand = NewMagickWand();
539   if (MagickReadImageBlob(magick_wand,readdata, readbyte) == MagickFalse) {
540     EXIT_MAGICK_ERROR();
541     return HTTP_NOT_FOUND;
542   }
543
544   /*------------------*/
545   /* for AnimationGIF */
546   /*------------------*/
547   img_count = MagickGetNumberImages(magick_wand);
548   DBG(r, "REQ[%X] img_count is [%d]", (unsigned int)(apr_size_t)r, img_count);
549   if (img_count > 1) {
550     MagickSetImageIndex(magick_wand, 0);
551     MagickWand *magick_wand2 = MagickGetImage(magick_wand);
552     DestroyMagickWand(magick_wand);
553     magick_wand = magick_wand2;
554   }
555
556   if (MagickStripImage(magick_wand) == MagickFalse) {
557     ERR(r, "mod_chxj: strip image failure.");
558     EXIT_MAGICK_ERROR();
559     return HTTP_NOT_FOUND;
560   }
561   {
562     MagickWand *magick_wand2;
563     if ((magick_wand2 = MagickCoalesceImages(magick_wand)) == NULL) {
564       EXIT_MAGICK_ERROR();
565       return HTTP_NOT_FOUND;
566     }
567     DestroyMagickWand(magick_wand);
568     magick_wand = magick_wand2;
569   }
570
571   if (spec->html_spec_type != CHXJ_SPEC_UNKNOWN) {
572     int oldw = MagickGetImageWidth(magick_wand);
573     int oldh = MagickGetImageHeight(magick_wand);
574     int done_fixup_size = 0;
575     if ((qsp->mode == IMG_CONV_MODE_WALLPAPER && spec->wp_width < oldw && spec->wp_heigh < oldh)
576       || (qsp->mode != IMG_CONV_MODE_WALLPAPER && spec->width < oldw && spec->heigh < oldh)) {
577       /*
578        * The size of the image is changed.
579        */
580       DBG(r,"REQ[%X] call s_fixup_size()", TO_ADDR(r));
581   
582       if ((magick_wand = s_fixup_size(magick_wand, r, spec, qsp)) == NULL) 
583         return HTTP_NOT_FOUND;
584
585       done_fixup_size = 1;
586     }
587     /*
588      * The colors of the image is changed.
589      */
590     DBG(r,"REQ[%X] call s_fixup_color()", TO_ADDR(r));
591   
592     if ((magick_wand = s_fixup_color(magick_wand, r,spec, mode)) == NULL) 
593       return HTTP_NOT_FOUND;
594   
595     /*
596      * DEPTH of the image is changed.
597      */
598     DBG(r,"REQ[%X] call s_fixup_depth()", TO_ADDR(r));
599   
600     if ((magick_wand = s_fixup_depth(magick_wand, r, spec)) == NULL) 
601       return HTTP_NOT_FOUND;
602   
603   
604     if (! done_fixup_size) {
605       /*
606        * The size of the image is changed.
607        */
608       DBG(r,"REQ[%X] call s_fixup_size()", TO_ADDR(r));
609       if ((magick_wand = s_fixup_size(magick_wand, r, spec, qsp)) == NULL) 
610         return HTTP_NOT_FOUND;
611     }
612
613     char *nowFormat = MagickGetImageFormat(magick_wand);
614     int fixFormatFlag = 0;
615     if (nowFormat) {
616       if (STRCASEEQ('g','G',"gif",nowFormat)) {
617         if (spec->available_gif) {
618           if (s_convert_to_gif(magick_wand, r, spec)) {
619             return HTTP_NOT_FOUND;
620           }
621           fixFormatFlag = 1;
622         }
623       }
624       else if (STRCASEEQ('j','J',"jpg",nowFormat)||STRCASEEQ('j','J',"jpeg",nowFormat)) {
625         if (spec->available_jpeg) {
626           if (s_convert_to_jpeg(magick_wand, r, spec)) {
627             return HTTP_NOT_FOUND;
628           }
629           fixFormatFlag = 1;
630         }
631       }
632       else if (STRCASEEQ('p','P',"png",nowFormat)) {
633         if (spec->available_png) {
634           if (s_convert_to_png(magick_wand, r, spec)) {
635             return HTTP_NOT_FOUND;
636           }
637           fixFormatFlag = 1;
638         }
639       }
640       else if (STRCASEEQ('b','B',"bmp",nowFormat)) {
641         if (spec->available_bmp4 || spec->available_bmp2) {
642           if (s_convert_to_bmp(magick_wand, r, spec)) {
643             return HTTP_NOT_FOUND;
644           }
645           fixFormatFlag = 1;
646         }
647       }
648     }
649
650     DBG(r,"REQ[%X] start convert and compression", TO_ADDR(r));
651   
652     if (! fixFormatFlag) {
653       if (spec->available_jpeg) {
654         if (s_convert_to_jpeg(magick_wand, r, spec)) {
655           return HTTP_NOT_FOUND;
656         }
657       }
658       else if (spec->available_gif) {
659         if (s_convert_to_gif(magick_wand, r, spec)) {
660           return HTTP_NOT_FOUND;
661         }  
662       }
663       else if (spec->available_png) {
664         if (s_convert_to_png(magick_wand, r, spec)) {
665           return HTTP_NOT_FOUND;
666         }  
667   
668       }
669       else
670       if (spec->available_bmp2 || spec->available_bmp4) { 
671         if (s_convert_to_bmp(magick_wand, r, spec)) {
672           return HTTP_NOT_FOUND;
673         }
674       }
675     }
676   
677     /*
678      * Add Comment (Copyright and so on.)
679      */
680     DBG(r, "call s_add_copyright()");
681   
682     if ((magick_wand = s_add_copyright(magick_wand, r, spec)) == NULL) 
683       return HTTP_NOT_FOUND;
684   }
685   else {
686     char* fmt;
687     fmt = MagickGetImageFormat(magick_wand);
688     if (fmt == NULL) {
689       if (MagickSetImageFormat(magick_wand, "jpg") == MagickFalse) {
690         EXIT_MAGICK_ERROR();
691         return HTTP_NOT_FOUND;
692       }
693   
694       r->content_type = apr_psprintf(r->pool, "image/jpeg");
695     }
696     else {
697       if (strcasecmp(fmt, "jpg") == 0) {
698         r->content_type = apr_psprintf(r->pool, "image/jpeg");
699       }
700       else
701       if (strcasecmp(fmt, "jpeg") == 0) {
702         r->content_type = apr_psprintf(r->pool, "image/jpeg");
703       }
704       else
705       if (strcasecmp(fmt, "gif") == 0) {
706         r->content_type = apr_psprintf(r->pool, "image/gif");
707       }
708       else
709       if (strcasecmp(fmt, "png") == 0) {
710         r->content_type = apr_psprintf(r->pool, "image/png");
711       }
712     }
713   }
714
715   char *sv_writedata;
716   sv_writedata = writedata = (char*)MagickGetImageBlob(magick_wand, &writebyte);
717
718   if (! writebyte) {
719     DestroyMagickWand(magick_wand);
720     ERR(r,"convert failure to Jpeg [%s]", tmpfile);
721
722     return HTTP_INTERNAL_SERVER_ERROR;
723   }
724   writedata = apr_palloc(r->pool, writebyte);
725   memcpy(writedata, sv_writedata, writebyte);
726
727   DBG(r, "end convert and compression");
728
729   /* Added PNG Comment if type is image/png. */
730   if (r->content_type && strcmp(r->content_type, "image/png") == 0) {
731     if ((writedata = s_add_comment_to_png(r, writedata, &writebyte)) == NULL) {
732       DBG(r, "REQ[%X] Add comment to PNG failure.",(unsigned int)(apr_size_t)r);
733       DestroyMagickWand(magick_wand);
734       if (sv_writedata) free(sv_writedata);
735       return HTTP_INTERNAL_SERVER_ERROR;
736     }
737   }
738   
739   /* check limit */
740   rv = apr_stat(&cache_dir_st, conf->image_cache_dir, APR_FINFO_MIN, r->pool);
741   if (rv != APR_SUCCESS) {
742     DestroyMagickWand(magick_wand);
743     ERR(r,"dir stat error.[%s]", conf->image_cache_dir);
744     if (sv_writedata) free(sv_writedata);
745     return HTTP_INTERNAL_SERVER_ERROR;
746   }
747   
748   for (;;) {
749     /* delete candidate */
750     apr_finfo_t dcf;   
751     /* get dir files size */
752     apr_dir_t *dir;
753     unsigned long total_size = 0;
754     int found_file = 0;
755     unsigned long max_size = (! conf->image_cache_limit) ? DEFAULT_IMAGE_CACHE_LIMIT : conf->image_cache_limit;
756     char *delete_file_name;
757
758     rv = apr_dir_open(&dir, conf->image_cache_dir, r->pool);
759     if (rv != APR_SUCCESS) { 
760       DestroyMagickWand(magick_wand);
761       ERR(r,"dir open error.[%s]", conf->image_cache_dir);
762       if (sv_writedata) free(sv_writedata);
763       return HTTP_INTERNAL_SERVER_ERROR;
764     }
765     memset(&dcf, 0, sizeof(apr_finfo_t));
766     dcf.atime = (apr_time_t)LONG_LONG_MAX;
767     for (;;) {
768       apr_finfo_t dirf;
769       rv = apr_dir_read(&dirf, APR_FINFO_SIZE|APR_FINFO_NAME|APR_FINFO_DIRENT|APR_FINFO_ATIME , dir);
770       if (rv != APR_SUCCESS) {
771         break;
772       }
773       if (dirf.name && strcmp(dirf.name, ".") != 0 && strcmp(dirf.name, "..") != 0) {
774         total_size += (unsigned long)dirf.size;
775         DBG(r, "dirf.name=[%s] dirf.size=[%ld] dirf.atime=[%lld]", dirf.name, (long)dirf.size, (long long int)dirf.atime);
776         if (dcf.atime >= dirf.atime) {
777           memcpy(&dcf, &dirf, sizeof(apr_finfo_t));
778         }
779         found_file++;
780       }
781     }
782     apr_dir_close(dir);
783     if (total_size + writebyte < max_size) {
784       DBG(r, "There is an enough size in cache. total_size:[%lu] max_size:[%lu] found_file=[%d] max_size=[%lu]", total_size, max_size, found_file, max_size);
785       break;
786     }
787     if (found_file == 0 && writebyte >= max_size) {
788       ERR(r, "==========================================");
789       ERR(r, "cache space is too small...");
790       ERR(r, "At least the same size as %luByte is necessary for me.", (unsigned long)writebyte); 
791       ERR(r, "Please specify the ChxjImageCacheLimit that is larger than now value. ");
792       ERR(r, "==========================================");
793       if (sv_writedata) free(sv_writedata);
794       return HTTP_INTERNAL_SERVER_ERROR;
795     }
796     DBG(r, "Image Cache dir is full. total_size:[%lu] max_size:[%lu]", total_size + writebyte, max_size);
797     /* search delete candidate */
798     delete_file_name = apr_psprintf(r->pool, "%s/%s", conf->image_cache_dir, dcf.name);
799     DBG(r, "delete image cache target:[%s] atime:[%lld]", delete_file_name, (long long int)dcf.atime);
800     rv = apr_file_remove(delete_file_name, r->pool);
801     if (rv != APR_SUCCESS) {
802       ERR(r, "cache file delete failure.[%s]", delete_file_name);
803       if (sv_writedata) free(sv_writedata);
804       return HTTP_INTERNAL_SERVER_ERROR;
805     }
806     DBG(r, "deleted image cache target:[%s]", delete_file_name);
807     if (total_size + writebyte - dcf.size < max_size) {
808       DBG(r, "OK, there is an enough size in cache.");
809       break;
810     }
811   }
812
813   /* to cache */
814   rv = apr_file_open(&fout, tmpfile,
815                   APR_FOPEN_WRITE| APR_FOPEN_CREATE | APR_FOPEN_BINARY | APR_SHARELOCK ,
816                   APR_OS_DEFAULT,
817                   r->pool);
818   if (rv != APR_SUCCESS) {
819     DestroyMagickWand(magick_wand);
820     ERR(r,"REQ[%X] file open error.[%s]", TO_ADDR(r), tmpfile);
821     if (sv_writedata) free(sv_writedata);
822     return HTTP_INTERNAL_SERVER_ERROR;
823   }
824
825   rv = apr_file_write(fout, (void*)writedata, &writebyte);
826   if (rv != APR_SUCCESS) {
827     DestroyMagickWand(magick_wand);
828     apr_file_close(fout);
829     if (sv_writedata) free(sv_writedata);
830     return HTTP_INTERNAL_SERVER_ERROR;
831   }
832
833   /*
834    * CRC is added for AU for EzGET.
835    */
836   if (spec->html_spec_type == CHXJ_SPEC_XHtml_Mobile_1_0
837   ||  spec->html_spec_type == CHXJ_SPEC_Hdml            ) {
838
839     crc = s_add_crc(writedata, writebyte);
840
841     rv = apr_file_putc((crc >> 8)  & 0xff, fout);
842     if (rv != APR_SUCCESS) {
843       DestroyMagickWand(magick_wand);
844       if (sv_writedata) free(sv_writedata);
845       return HTTP_INTERNAL_SERVER_ERROR;
846     }
847
848     rv = apr_file_putc( crc        & 0xff, fout);
849     if (rv != APR_SUCCESS) {
850       DestroyMagickWand(magick_wand);
851       if (sv_writedata) free(sv_writedata);
852       return HTTP_INTERNAL_SERVER_ERROR;
853     }
854   }
855
856   DestroyMagickWand(magick_wand);
857   if (sv_writedata) free(sv_writedata);
858
859   rv = apr_file_close(fout);
860   if (rv != APR_SUCCESS) {
861     DBG(r,"file write error.[%s]", tmpfile);
862     return HTTP_INTERNAL_SERVER_ERROR;
863   }
864
865   return OK;
866 }
867
868
869 static int
870 s_convert_to_jpeg(MagickWand *magick_wand, request_rec *r, device_table *spec) 
871 {
872   if (MagickSetImageCompression(magick_wand,JPEGCompression) == MagickFalse) {
873     EXIT_MAGICK_ERROR();
874     return -1;
875   }
876   
877   if (MagickSetImageFormat(magick_wand, "jpg") == MagickFalse) {
878     EXIT_MAGICK_ERROR();
879     return -1;
880   }
881   
882   if (MagickStripImage(magick_wand) == MagickFalse) {
883     EXIT_MAGICK_ERROR();
884     return -1;
885   }
886   
887   if ((magick_wand = s_img_down_sizing(magick_wand, r, spec)) == NULL)
888     return -1;
889   
890   r->content_type = apr_psprintf(r->pool, "image/jpeg");
891   ap_set_content_type(r, "image/jpeg");
892   DBG(r,"convert to jpg");
893   return 0;
894 }
895
896
897 static int
898 s_convert_to_png(MagickWand *magick_wand, request_rec *r, device_table *spec)
899 {
900   if (MagickSetImageCompression(magick_wand,ZipCompression) == MagickFalse) {
901     EXIT_MAGICK_ERROR();
902     return -1;
903   }
904   
905   if (MagickSetImageFormat(magick_wand, "png") == MagickFalse) {
906     EXIT_MAGICK_ERROR();
907     return -1;
908   }
909   
910   if (MagickStripImage(magick_wand) == MagickFalse) {
911     EXIT_MAGICK_ERROR();
912     return -1;
913   }
914   
915   if ((magick_wand = s_img_down_sizing(magick_wand, r, spec)) == NULL) 
916     return -1;
917   
918   r->content_type = apr_psprintf(r->pool, "image/png");
919   ap_set_content_type(r, "image/png");
920   DBG(r, "convert to png");
921   return 0;
922 }
923
924
925 static int
926 s_convert_to_gif(MagickWand *magick_wand, request_rec *r, device_table *spec)
927 {
928   if (MagickSetImageCompression(magick_wand,LZWCompression) == MagickFalse) {
929     EXIT_MAGICK_ERROR();
930     return -1;
931   }
932   
933   if (MagickSetImageFormat(magick_wand, "gif") == MagickFalse) {
934     EXIT_MAGICK_ERROR();
935     return -1;
936   }
937   
938   if (MagickStripImage(magick_wand) == MagickFalse) {
939     EXIT_MAGICK_ERROR();
940     return -1;
941   }
942   
943   if ((magick_wand = s_img_down_sizing(magick_wand, r, spec)) == NULL) 
944     return -1;
945   
946   r->content_type = apr_psprintf(r->pool, "image/gif");
947   ap_set_content_type(r, "image/gif");
948   
949   DBG(r,"convert to gif");
950   return 0;
951 }
952
953
954 static int
955 s_convert_to_bmp(MagickWand *magick_wand, request_rec *r, device_table *spec)
956 {
957   if (MagickSetImageCompression(magick_wand,NoCompression) == MagickFalse) {
958     EXIT_MAGICK_ERROR();
959     return -1;
960   }
961   
962   if (MagickSetImageFormat(magick_wand, "bmp") == MagickFalse) {
963     EXIT_MAGICK_ERROR();
964     return -1;
965   }
966   
967   if (MagickStripImage(magick_wand) == MagickFalse) {
968     EXIT_MAGICK_ERROR();
969     return -1;
970   }
971   
972   if ((magick_wand = s_img_down_sizing(magick_wand, r, spec)) == NULL) 
973     return -1;
974   
975   r->content_type = apr_psprintf(r->pool, "image/bmp");
976   ap_set_content_type(r, "image/bmp");
977   
978   DBG(r, "convert to bmp(unsupported)");
979   return 0;
980 }
981
982
983 static char *
984 s_create_blob_data(request_rec          *r, 
985                    device_table         *spec, 
986                    query_string_param_t *qsp,
987                    char                 *indata,
988                    apr_size_t           *len)
989 {
990   apr_size_t         writebyte;
991   unsigned short     crc;
992   img_conv_mode_t    mode = qsp->mode;
993
994   char *writedata = NULL;
995   char *dst       = NULL;
996   MagickWand *magick_wand;
997
998   magick_wand = NewMagickWand();
999
1000   if (MagickReadImageBlob(magick_wand,indata, *len) == MagickFalse) {
1001     EXIT_MAGICK_ERROR();
1002     return NULL;
1003   }
1004
1005   if (MagickStripImage(magick_wand) == MagickFalse) {
1006     ERR(r, "mod_chxj: strip image failure.");
1007     EXIT_MAGICK_ERROR();
1008     return NULL;
1009   }
1010
1011   {
1012     int oldw = MagickGetImageWidth(magick_wand);
1013     int oldh = MagickGetImageHeight(magick_wand);
1014     int done_fixup_size = 0;
1015     if ((qsp->mode == IMG_CONV_MODE_WALLPAPER && spec->wp_width < oldw && spec->wp_heigh < oldh)
1016       || (qsp->mode != IMG_CONV_MODE_WALLPAPER && spec->width < oldw && spec->heigh < oldh)) {
1017       /*
1018        * The size of the image is changed.
1019        */
1020       DBG(r,"call s_fixup_size()");
1021
1022       if ((magick_wand = s_fixup_size(magick_wand, r, spec, qsp)) == NULL) {
1023         EXIT_MAGICK_ERROR();
1024         return NULL;
1025       }
1026
1027       done_fixup_size = 1;
1028     }
1029     /*
1030      * The colors of the image is changed.
1031      */
1032     DBG(r,"call s_fixup_color()");
1033
1034     if ((magick_wand = s_fixup_color(magick_wand, r,spec, mode)) == NULL) {
1035       EXIT_MAGICK_ERROR();
1036       return NULL;
1037     }
1038
1039     /*
1040      * DEPTH of the image is changed.
1041      */
1042     DBG(r,"call s_fixup_depth()");
1043
1044     if ((magick_wand = s_fixup_depth(magick_wand, r, spec)) == NULL) {
1045       EXIT_MAGICK_ERROR();
1046       return NULL;
1047     }
1048
1049
1050     if (! done_fixup_size) {
1051       /*
1052        * The size of the image is changed.
1053        */
1054       DBG(r,"call s_fixup_size()");
1055       if ((magick_wand = s_fixup_size(magick_wand, r, spec, qsp)) == NULL) {
1056         EXIT_MAGICK_ERROR();
1057         return NULL;
1058       }
1059     }
1060   }
1061
1062   char *nowFormat = MagickGetImageFormat(magick_wand);
1063   int fixFormatFlag = 0;
1064   if (nowFormat) {
1065     if (STRCASEEQ('g','G',"gif",nowFormat)) {
1066       if (spec->available_gif) {
1067         if (s_convert_to_gif(magick_wand, r, spec)) {
1068           return NULL;
1069         }
1070         fixFormatFlag = 1;
1071       }
1072     }
1073     else if (STRCASEEQ('j','J',"jpg",nowFormat)||STRCASEEQ('j','J',"jpeg",nowFormat)) {
1074       if (spec->available_jpeg) {
1075         if (s_convert_to_jpeg(magick_wand, r, spec)) {
1076           return NULL;
1077         }
1078         fixFormatFlag = 1;
1079       }
1080     }
1081     else if (STRCASEEQ('p','P',"png",nowFormat)) {
1082       if (spec->available_png) {
1083         if (s_convert_to_png(magick_wand, r, spec)) {
1084           return NULL;
1085         }
1086         fixFormatFlag = 1;
1087       }
1088     }
1089   }
1090
1091   DBG(r,"start convert and compression");
1092
1093   if (!fixFormatFlag) {
1094     if (spec->available_jpeg) {
1095       if (s_convert_to_jpeg(magick_wand, r, spec)) {
1096         return NULL;
1097       }
1098     }
1099     else if (spec->available_png) {
1100       if (s_convert_to_png(magick_wand, r, spec)) {
1101         return NULL;
1102       }
1103     }
1104     else if (spec->available_gif) {
1105       if (s_convert_to_gif(magick_wand, r, spec)) {
1106         return NULL;
1107       }
1108     }
1109     else if (spec->available_bmp2 || spec->available_bmp4) {
1110       if (s_convert_to_bmp(magick_wand, r, spec)) {
1111         return NULL;
1112       }
1113     }
1114   }
1115   /*--------------------------------------------------------------------------*/
1116   /* Add Comment (Copyright and so on.)                                       */
1117   /*--------------------------------------------------------------------------*/
1118   DBG(r,"call s_add_copyright()");
1119
1120   if ((magick_wand = s_add_copyright(magick_wand, r, spec)) == NULL)
1121     return NULL;
1122
1123   writedata = (char*)MagickGetImageBlob(magick_wand, &writebyte);
1124
1125   if (! writebyte) {
1126     DestroyMagickWand(magick_wand);
1127     DBG(r,"convert failure to Jpeg ");
1128     return NULL;
1129   }
1130
1131   DBG(r,"end convert and compression");
1132
1133
1134   
1135   dst = apr_palloc(r->pool, writebyte+2);
1136
1137   memcpy(dst, writedata, writebyte);
1138   /*--------------------------------------------------------------------------*/
1139   /* CRC is added for AU for EzGET.                                           */
1140   /*--------------------------------------------------------------------------*/
1141   if (spec->html_spec_type == CHXJ_SPEC_XHtml_Mobile_1_0
1142   ||  spec->html_spec_type == CHXJ_SPEC_Hdml) {
1143     crc = s_add_crc(writedata, writebyte);
1144     dst[writebyte + 0] = (crc >> 8) & 0xff;
1145     dst[writebyte + 1] = (crc     ) & 0xff;
1146     writebyte += 2;
1147   }
1148
1149   DestroyMagickWand(magick_wand);
1150
1151   *len = writebyte;
1152   return dst;
1153 }
1154
1155
1156 static MagickWand *
1157 s_fixup_size(MagickWand           *magick_wand, 
1158              request_rec          *r, 
1159              device_table         *spec, 
1160              query_string_param_t *qsp)
1161 {
1162   img_conv_mode_t mode = qsp->mode;
1163   int oldw;
1164   int oldh;
1165   int neww;
1166   int newh;
1167   int c_width;
1168   int c_heigh;
1169
1170   oldw = MagickGetImageWidth(magick_wand);
1171   oldh = MagickGetImageHeight(magick_wand);
1172
1173   DBG(r,"detect width=[%d]", oldw);
1174   DBG(r,"detect heigh=[%d]", oldh);
1175
1176   neww = oldw;
1177   newh = oldh;
1178
1179   DBG(r,"detect spec width=[%d]", spec->width);
1180   DBG(r,"detect spec heigh=[%d]", spec->heigh);
1181
1182   c_width = spec->width;
1183   c_heigh = spec->heigh;
1184
1185   switch(mode) {
1186   case IMG_CONV_MODE_THUMBNAIL:
1187
1188     DBG(r,"**** detect thumbnail mode ****");
1189
1190     if (neww > c_width) {
1191       newh = (int)((double)newh * (double)((double)c_width / (double)neww));
1192       neww = (int)((double)neww * (double)((double)c_width / (double)neww));
1193     }
1194     if (newh > c_heigh) {
1195       neww = (int)((double)neww * (double)((double)c_heigh / (double)newh));
1196       newh = (int)((double)newh * (double)((double)c_heigh / (double)newh));
1197     }
1198
1199     neww = (int)((double)(neww / 3) * 0.8);
1200     newh = (int)((double)(newh / 3) * 0.8);
1201     break;
1202
1203   case IMG_CONV_MODE_WALLPAPER:
1204   case IMG_CONV_MODE_EZGET:
1205
1206     DBG(r,"**** detect wallpaper mode ****");
1207
1208     if (spec->wp_width && spec->wp_heigh) {
1209       c_width = spec->wp_width;
1210       c_heigh = spec->wp_heigh;
1211     }
1212
1213     DBG(r,"calc new width and height");
1214
1215     neww = (int)((double)neww * (double)((double)c_heigh / (double)newh));
1216     newh = (int)((double)newh * (double)((double)c_heigh / (double)newh));
1217
1218     DBG(r,"newh = [%d] neww = [%d]", newh, neww);
1219     break;
1220
1221   default:
1222
1223     DBG(r,"**** detect normal mode ****");
1224
1225     if (qsp->ua_flag != UA_IGN && spec->html_spec_type != CHXJ_SPEC_UNKNOWN) {
1226       if (neww > c_width) {
1227         newh = (int)((double)newh * (double)((double)c_width / (double)neww));
1228         neww = (int)((double)neww * (double)((double)c_width / (double)neww));
1229       }
1230
1231       if (newh > c_heigh) {
1232         neww = (int)((double)neww * (double)((double)c_heigh / (double)newh));
1233         newh = (int)((double)newh * (double)((double)c_heigh / (double)newh));
1234       }
1235     }
1236     break;
1237   }
1238
1239   if (neww == 0) neww = 1;
1240   if (newh == 0) newh = 1;
1241
1242   if (spec->html_spec_type != CHXJ_SPEC_UNKNOWN) {
1243     DBG(r,"convert width=[%d --> %d]", oldw, neww);
1244     DBG(r,"convert heigh=[%d --> %d]", oldh, newh);
1245   
1246     MagickResetIterator(magick_wand);
1247   
1248     while (MagickNextImage(magick_wand) != MagickFalse) {
1249       switch (mode) {
1250       case IMG_CONV_MODE_WALLPAPER:
1251       case IMG_CONV_MODE_EZGET:
1252   
1253         if (MagickResizeImage(magick_wand,neww,newh,LanczosFilter,1.0) == MagickFalse) {
1254           EXIT_MAGICK_ERROR();
1255           return NULL;
1256         }
1257   
1258         if (MagickCropImage(magick_wand, 
1259                         (unsigned long)c_width, 
1260                         (unsigned long)c_heigh,
1261                         (long)((neww - c_width) / 2),
1262                         (long)((newh - c_heigh) / 2)) == MagickFalse) {
1263           EXIT_MAGICK_ERROR();
1264           return NULL;
1265         }
1266         break;
1267   
1268       case IMG_CONV_MODE_NORMAL:
1269         if (qsp->width) {
1270           DBG(r,"convert width=[%d --> %d]", neww, qsp->width);
1271           neww = qsp->width;
1272         }
1273         if (qsp->height) {
1274           DBG(r,"convert heigh=[%d --> %d]", newh, qsp->height);
1275           newh = qsp->height;
1276         }
1277   
1278       default:
1279         if (MagickResizeImage(magick_wand,neww,newh,LanczosFilter,1.0) == MagickFalse) {
1280           EXIT_MAGICK_ERROR();
1281           return NULL;
1282         }
1283         break;
1284       }
1285   
1286       if (spec->html_spec_type != CHXJ_SPEC_UNKNOWN) {
1287         if (MagickSetImageUnits(magick_wand, PixelsPerInchResolution) == MagickFalse) {
1288           EXIT_MAGICK_ERROR();
1289           return NULL;
1290         }
1291     
1292         if (MagickSetImageResolution(magick_wand,
1293                                      (double)spec->dpi_width,
1294                                      (double)spec->dpi_heigh) == MagickFalse) {
1295           EXIT_MAGICK_ERROR();
1296           return NULL;
1297         }
1298     
1299         if (MagickSetImageDispose(magick_wand, BackgroundDispose) == MagickFalse) {
1300           EXIT_MAGICK_ERROR();
1301           return NULL;
1302         }
1303       }
1304     }
1305   }
1306   return magick_wand;
1307 }
1308
1309
1310 static MagickWand *
1311 s_fixup_color(MagickWand *magick_wand, request_rec *r, device_table *spec, img_conv_mode_t UNUSED(mode))
1312 {
1313   DBG(r,"start chxj_fixup_clor()");
1314
1315   if (spec->html_spec_type == CHXJ_SPEC_UNKNOWN) {
1316     DBG(r, "Pass s_fixup_color proc");
1317     return magick_wand;
1318   }
1319
1320   unsigned long colors = MagickGetImageColors(magick_wand);
1321   DBG(r, "now color:[%ld] spec->color:[%ld]", colors, (unsigned long)spec->color);
1322   if (colors < (unsigned long)spec->color) {
1323     DBG(r, "Pass s_fixup_color proc. color:[%ld] spec->color:[%d]", colors, spec->color);
1324     return magick_wand;
1325   }
1326
1327   if (spec->color >= 256) {
1328
1329     DBG(r,"call MagickQuantizeImage() spec->color=[%d]",spec->color);
1330
1331     if (MagickQuantizeImage(magick_wand,
1332                            spec->color,
1333                            RGBColorspace,
1334                            0,
1335                            1,
1336                            0) == MagickFalse) {
1337       EXIT_MAGICK_ERROR();
1338       return NULL;
1339     }
1340
1341     DBG(r,"call end MagickQuantizeImage() spec->color=[%d]",spec->color);
1342
1343   }
1344   else {
1345     DBG(r,"call MagickQuantizeImage() spec->color=[%d]",spec->color);
1346
1347     if (MagickQuantizeImage(magick_wand,
1348                            spec->color,
1349                            GRAYColorspace,
1350                            0,
1351                            1,
1352                            0) == MagickFalse) {
1353       EXIT_MAGICK_ERROR();
1354       return NULL;
1355     }
1356
1357     DBG(r,"call end MagickQuantizeImage() spec->color=[%d]",spec->color);
1358   }
1359
1360
1361   DBG(r,"end chxj_fixup_clor()");
1362
1363   return magick_wand;
1364 }
1365
1366
1367
1368 static MagickWand *
1369 s_fixup_depth(MagickWand *magick_wand, request_rec *r, device_table *spec)
1370 {
1371   if (spec->html_spec_type == CHXJ_SPEC_UNKNOWN) {
1372     DBG(r, "Pass s_fixup_depth proc");
1373     return magick_wand;
1374   }
1375
1376   if (spec->color == 15680000) {
1377     if (MagickSetImageDepth(magick_wand, 24) == MagickFalse) {
1378       EXIT_MAGICK_ERROR();
1379       return NULL;
1380     }
1381   }
1382   else 
1383   if (spec->color == 262144) {
1384     if (MagickSetImageDepth(magick_wand, 18) == MagickFalse) {
1385       EXIT_MAGICK_ERROR();
1386       return NULL;
1387     }
1388   }
1389   else
1390   if (spec->color == 65536) {
1391     if (MagickSetImageDepth(magick_wand, 16) == MagickFalse) {
1392       EXIT_MAGICK_ERROR();
1393       return NULL;
1394     }
1395   }
1396   else
1397   if (spec->color == 4096) {
1398     if (MagickSetImageDepth(magick_wand, 12) == MagickFalse) {
1399       EXIT_MAGICK_ERROR();
1400       return NULL;
1401     }
1402   }
1403   else
1404   if (spec->color == 256) {
1405     if (MagickSetImageDepth(magick_wand, 8) == MagickFalse) {
1406       EXIT_MAGICK_ERROR();
1407       return NULL;
1408     }
1409   }
1410   else
1411   if (spec->color == 4) {
1412     if (MagickSetImageDepth(magick_wand, 2) == MagickFalse) {
1413       EXIT_MAGICK_ERROR();
1414       return NULL;
1415     }
1416   }
1417   else
1418   if (spec->color == 2) {
1419     if (MagickSetImageDepth(magick_wand, 1) == MagickFalse) {
1420       EXIT_MAGICK_ERROR();
1421       return NULL;
1422     }
1423   }
1424
1425   return magick_wand;
1426 }
1427
1428
1429 static MagickWand *
1430 s_add_copyright(MagickWand *magick_wand, request_rec *r, device_table *spec)
1431 {
1432   mod_chxj_config *conf = chxj_get_module_config(r->per_dir_config, &chxj_module);
1433
1434   if (spec->html_spec_type == CHXJ_SPEC_UNKNOWN) {
1435     DBG(r, "Pass add_copiright proc");
1436     return magick_wand;
1437   }
1438
1439   if (conf->image_copyright) {
1440
1441     DBG(r, "Add COPYRIGHT [%s]", conf->image_copyright);
1442
1443     if (spec->html_spec_type == CHXJ_SPEC_Jhtml
1444     ||  spec->html_spec_type == CHXJ_SPEC_Jxhtml) {
1445       apr_table_setn(r->headers_out, "x-jphone-copyright", "no-transfer");
1446       if (MagickCommentImage(magick_wand, 
1447                              apr_psprintf(r->pool, 
1448                                           "Copyright(C) %s", 
1449                                           conf->image_copyright)) == MagickFalse) 
1450         goto on_error;
1451     }
1452     else
1453     if (spec->html_spec_type == CHXJ_SPEC_XHtml_Mobile_1_0
1454     ||  spec->html_spec_type == CHXJ_SPEC_Hdml) {
1455       if (MagickCommentImage(magick_wand, 
1456                              apr_psprintf(r->pool, 
1457                                          "kddi_copyright=on,%s", 
1458                                           conf->image_copyright)) == MagickFalse) 
1459         goto on_error;
1460     }
1461     else {
1462       if (MagickCommentImage(magick_wand, 
1463                             apr_psprintf(r->pool, 
1464                                          "copy=\"NO\",%s",
1465                                          conf->image_copyright)) == MagickFalse)
1466         goto on_error;
1467     }
1468   }
1469   else {
1470     if (MagickCommentImage(magick_wand, "mod_chxj") == MagickFalse)
1471       goto on_error;
1472   }
1473   return magick_wand;
1474
1475 on_error:
1476   EXIT_MAGICK_ERROR();
1477   return NULL;
1478 }
1479
1480 static MagickWand *
1481 s_img_down_sizing(MagickWand *magick_wand, request_rec *r, device_table *spec)
1482 {
1483   MagickBooleanType  status;
1484   unsigned long      quality = 70;
1485   apr_size_t         writebyte = 0;
1486   char               *writedata;
1487   apr_size_t         prev_size = 0;
1488   char               *fmt;
1489   int                fmt_type = 0;
1490   
1491   writedata = (char *)MagickGetImageBlob(magick_wand, &writebyte);
1492   prev_size = writebyte;
1493
1494   
1495   fmt = MagickGetImageFormat(magick_wand);
1496   if (fmt) {
1497     if (STRCASEEQ('j','J',"jpg",fmt)) fmt_type = 1;
1498     if (STRCASEEQ('p','P',"png",fmt)) fmt_type = 2;
1499     if (STRCASEEQ('g','G',"gif",fmt)) fmt_type = 3;
1500     if (STRCASEEQ('b','B',"bmp",fmt)) fmt_type = 4;
1501   }
1502   if (fmt_type == 1) {
1503     do {
1504       if (MagickSetImageCompressionQuality(magick_wand, quality) == MagickFalse) {
1505         EXIT_MAGICK_ERROR();
1506         return NULL;
1507       }
1508   
1509       writedata = (char*)MagickGetImageBlob(magick_wand, &writebyte);
1510       if (writebyte >= prev_size) {
1511         DBG(r, "quality=[%ld] size=[%d]", (long)quality, (int)writebyte);
1512         quality += 5;
1513         if (quality > 100) {
1514           if (MagickSetImageCompression(magick_wand,NoCompression) == MagickFalse) {
1515             EXIT_MAGICK_ERROR();
1516             return NULL;
1517           }
1518           break;
1519         }
1520         if (MagickSetImageCompressionQuality(magick_wand, quality) == MagickFalse) {
1521           EXIT_MAGICK_ERROR();
1522           return NULL;
1523         }
1524         break;
1525       }
1526   
1527       DBG(r, "quality=[%ld] size=[%d]", (long)quality, (int)writebyte);
1528   
1529       if (spec->cache == 0)
1530         break;
1531   
1532       if (writebyte <= (unsigned int)spec->cache)
1533         break;
1534   
1535       quality -= 5;
1536   
1537       if (quality == 0 || quality > 100)
1538         break;
1539   
1540     }
1541     while (1);
1542   }
1543
1544
1545   if (spec->cache > 0 
1546   &&  writebyte   > (unsigned int)spec->cache) {
1547     unsigned long now_color = spec->color;
1548     unsigned long depth     = 0;
1549     do {
1550       switch(now_color) {
1551       case 2:      depth = 1; break;
1552       case 4:      now_color = 2;        depth = 1;  break;
1553       case 8:      now_color = 4;        depth = 2;  break;
1554       case 16:     now_color = 8;        depth = 3;  break;
1555       case 256:    now_color = 16;       depth = 4;  break;
1556       case 4096:   now_color = 256;      depth = 8;  break;
1557       case 65536:  now_color = 4096;     depth = 12; break;
1558       case 262144: now_color = 65536;    depth = 16; break;
1559       case 15680000: now_color = 262144; depth = 18; break;
1560       default:
1561         now_color = 2;
1562         break;
1563       }
1564
1565       if (now_color <= 2) break;
1566
1567
1568       if (now_color >= 8) {
1569         status = MagickQuantizeImage(magick_wand,
1570                              now_color,
1571                              RGBColorspace,
1572                              0,
1573                              1,
1574                              0);
1575       }
1576       else {
1577         status = MagickQuantizeImage(magick_wand,
1578                              now_color,
1579                              GRAYColorspace,
1580                              0,
1581                              1,
1582                              0);
1583         MagickSetImageType(magick_wand, GrayscaleType);
1584       }
1585       if (status == MagickFalse) {
1586         EXIT_MAGICK_ERROR();
1587         return NULL;
1588       }
1589       if (fmt_type != 2) {
1590         if (MagickSetImageDepth(magick_wand, depth) == MagickFalse) {
1591           EXIT_MAGICK_ERROR();
1592           return NULL;
1593         }
1594       }
1595       writedata = (char*)MagickGetImageBlob(magick_wand, &writebyte);
1596
1597       DBG(r,"now_color=[%ld] size=[%d]", (long)now_color, (int)writebyte);
1598
1599       /* Once per request */
1600       break;
1601     }
1602     while(now_color > 2);
1603   }
1604
1605   return magick_wand;
1606 }
1607
1608
1609 static apr_status_t 
1610 s_send_cache_file(
1611   mod_chxj_config      *conf,
1612   device_table         *spec, 
1613   query_string_param_t *query_string, 
1614   request_rec          *r, 
1615   const char           *tmpfile)
1616 {
1617   apr_status_t rv;
1618   apr_finfo_t  st;
1619   apr_file_t   *fout;
1620   apr_size_t   sendbyte;
1621   char         *contentLength;
1622
1623   rv = apr_stat(&st, tmpfile, APR_FINFO_MIN, r->pool);
1624   if (rv != APR_SUCCESS)
1625     return HTTP_NOT_FOUND;
1626
1627   DBG(r, "REQ[%X] mode:[%d]",    TO_ADDR(r), query_string->mode);
1628   DBG(r, "REQ[%X] name:[%s]",    TO_ADDR(r), query_string->name);
1629   DBG(r, "REQ[%X] offset:[%ld]", TO_ADDR(r), query_string->offset);
1630   DBG(r, "REQ[%X] count:[%ld]",  TO_ADDR(r), query_string->count);
1631
1632   /* for mod_cache */
1633   {
1634     apr_table_setn(r->headers_out, "Vary", "User-Agent");
1635     apr_table_setn(r->err_headers_out, "Vary", "User-Agent");
1636     ap_update_mtime(r, st.mtime);
1637     ap_set_last_modified(r);
1638   }
1639
1640   if (query_string->mode != IMG_CONV_MODE_EZGET && query_string->name == NULL) {
1641     contentLength = apr_psprintf(r->pool, "%d", (int)st.size);
1642     apr_table_setn(r->headers_out, "Content-Length", (const char*)contentLength);
1643   
1644     DBG(r,"Content-Length:[%d]", (int)st.size);
1645     MagickWand *magick_wand = NewMagickWand();
1646     if (MagickReadImage(magick_wand,tmpfile) == MagickFalse) {
1647       EXIT_MAGICK_ERROR();
1648       return HTTP_NOT_FOUND;
1649     }
1650     if (MagickStripImage(magick_wand) == MagickFalse) {
1651       ERR(r, "mod_chxj: strip image failure.");
1652       EXIT_MAGICK_ERROR();
1653       return HTTP_NOT_FOUND;
1654     }
1655     char *nowFormat = MagickGetImageFormat(magick_wand);
1656     DestroyMagickWand(magick_wand);
1657     if (nowFormat) {
1658       if (STRCASEEQ('j','J',"jpeg",nowFormat) || STRCASEEQ('j','J',"jpg",nowFormat)) {
1659         DBG(r, "detect cache file => jpg.");
1660         ap_set_content_type(r, "image/jpeg");
1661       }
1662       else if (STRCASEEQ('p','P',"png", nowFormat)) {
1663         DBG(r, "detect cache file => png.");
1664         ap_set_content_type(r, "image/png");
1665       }
1666       else if (STRCASEEQ('g','G',"gif", nowFormat)) {
1667         DBG(r, "detect cache file => gif.");
1668         ap_set_content_type(r, "image/gif");
1669       }
1670       else if (STRCASEEQ('b','B',"bmp", nowFormat)) {
1671         DBG(r, "detect cache file => bmp.");
1672         ap_set_content_type(r, "image/bmp");
1673       }
1674       else {
1675         ERR(r, "detect unknown file");
1676         return HTTP_NOT_FOUND;
1677       }
1678     }
1679     if (conf->image_copyright) {
1680       DBG(r, "REQ[%X] Add COPYRIGHT Header for SoftBank [%s]", TO_ADDR(r), conf->image_copyright);
1681       if (spec->html_spec_type == CHXJ_SPEC_Jhtml ||  spec->html_spec_type == CHXJ_SPEC_Jxhtml) {
1682         apr_table_setn(r->headers_out, "x-jphone-copyright", "no-transfer");
1683       }
1684     }
1685     rv = apr_file_open(&fout, tmpfile, 
1686       APR_FOPEN_READ | APR_FOPEN_BINARY | APR_FOPEN_BUFFERED | APR_FOPEN_SHARELOCK | APR_FOPEN_SENDFILE_ENABLED, 
1687       APR_OS_DEFAULT, r->pool);
1688     if (rv != APR_SUCCESS) {
1689       DBG(r, "cache file open failed[%s]", tmpfile);
1690       return HTTP_NOT_FOUND;
1691     }
1692     ap_send_fd(fout, r, 0, st.size, &sendbyte);
1693     apr_file_close(fout);
1694     ap_rflush(r);
1695     DBG(r, "REQ[%X] send file data[%d]byte", TO_ADDR(r), (int)sendbyte);
1696   }
1697   else
1698   if (query_string->mode == IMG_CONV_MODE_EZGET) {
1699     char *name = apr_pstrdup(r->pool, basename(r->filename));
1700     name[strlen(name)-4] = 0;
1701     if (strcasecmp(r->content_type, "image/jpeg") == 0) {
1702
1703       chxj_set_content_type(r, "text/x-hdml; charset=Shift_JIS");
1704       ap_rprintf(r, HDML_FIRST_PAGE, r->uri, name, ".jpg", (long)st.size, "devjaww", name);
1705     }
1706     else
1707     if (strcasecmp(r->content_type, "image/bmp") == 0) {
1708       chxj_set_content_type(r, "text/x-hdml; charset=Shift_JIS");
1709       ap_rprintf(r, HDML_FIRST_PAGE, r->uri, name, ".bmp", (long)st.size, "devabm", name);
1710     }
1711     else
1712     if (strcasecmp(r->content_type, "image/png") == 0) {
1713       chxj_set_content_type(r, "text/x-hdml; charset=Shift_JIS");
1714       ap_rprintf(r, HDML_FIRST_PAGE, r->uri, name, ".png", (long)st.size, "dev8aww", name);
1715     }
1716     else
1717     if (strcasecmp(r->content_type, "image/gif") == 0) {
1718       chxj_set_content_type(r, "text/x-hdml; charset=Shift_JIS");
1719       ap_rprintf(r, HDML_FIRST_PAGE, r->uri, name, ".gif", (long)st.size, "devgi0z", name);
1720     }
1721   }
1722   else
1723   if (query_string->mode == IMG_CONV_MODE_WALLPAPER && query_string->name != NULL) {
1724     if (query_string->count == -1 && query_string->offset == -1) {
1725       chxj_set_content_type(r, "text/x-hdml; charset=Shift_JIS");
1726       ap_rprintf(r, HDML_SUCCESS_PAGE);
1727       ap_rflush(r);
1728     }
1729     else
1730     if (query_string->count == -2 && query_string->offset == -1) {
1731       chxj_set_content_type(r, "text/x-hdml; charset=Shift_JIS");
1732       ap_rprintf(r, HDML_FAIL_PAGE);
1733       ap_rflush(r);
1734     }
1735     else { 
1736       chxj_set_content_type(r, "application/x-up-download");
1737       contentLength = apr_psprintf(r->pool, "%ld", query_string->count);
1738       apr_table_setn(r->headers_out, "Content-Length", (const char*)contentLength);
1739   
1740       DBG(r, "Content-Length:[%d]", (int)st.size);
1741
1742       rv = apr_file_open(&fout, tmpfile, 
1743         APR_FOPEN_READ | APR_FOPEN_BINARY | APR_FOPEN_BUFFERED | APR_FOPEN_SHARELOCK | APR_FOPEN_SENDFILE_ENABLED, 
1744         APR_OS_DEFAULT, r->pool);
1745
1746       if (rv != APR_SUCCESS) {
1747         DBG(r,"tmpfile open failed[%s]", tmpfile);
1748         return HTTP_NOT_FOUND;
1749       }
1750
1751       ap_send_fd(fout, r, query_string->offset, query_string->count, &sendbyte);
1752       apr_file_close(fout);
1753       ap_rflush(r);
1754       DBG(r, "send file data[%d]byte", (int)sendbyte);
1755     }
1756   }
1757   
1758   return OK;
1759 }
1760
1761
1762 static apr_status_t 
1763 s_send_original_file(request_rec *r, const char *originalfile)
1764 {
1765   apr_status_t rv;
1766   apr_finfo_t  st;
1767   apr_file_t   *fout;
1768   apr_size_t   sendbyte = 0;
1769
1770   rv = apr_stat(&st, originalfile, APR_FINFO_MIN, r->pool);
1771   if (rv != APR_SUCCESS)
1772     return HTTP_NOT_FOUND;
1773
1774   /* for mod_cache */
1775   {
1776     apr_table_setn(r->headers_out, "Vary", "User-Agent");
1777     apr_table_setn(r->err_headers_out, "Vary", "User-Agent");
1778     ap_update_mtime(r, st.mtime);
1779     ap_set_last_modified(r);
1780   }
1781
1782   rv = apr_file_open(&fout, originalfile, 
1783     APR_FOPEN_READ | APR_FOPEN_BINARY | APR_FOPEN_BUFFERED | APR_FOPEN_SHARELOCK | APR_FOPEN_SENDFILE_ENABLED,
1784     APR_OS_DEFAULT, r->pool);
1785   if (rv != APR_SUCCESS) {
1786     DBG(r, "originalfile open failed[%s]", originalfile);
1787     return HTTP_NOT_FOUND;
1788   }
1789
1790   ap_send_fd(fout, r, 0, st.size, &sendbyte);
1791   apr_file_close(fout);
1792   ap_rflush(r);
1793   DBG(r, "send file data[%d]byte", (int)sendbyte);
1794   
1795   return OK;
1796 }
1797
1798
1799 static apr_status_t 
1800 s_header_only_cache_file(device_table *spec, query_string_param_t *query_string, request_rec *r, const char *tmpfile)
1801 {
1802   apr_status_t rv;
1803   apr_finfo_t  st;
1804   char         *contentLength;
1805   mod_chxj_config *conf = ap_get_module_config(r->per_dir_config, &chxj_module);
1806
1807   DBG(r, "REQ[%X] start s_header_only_cache_file()", TO_ADDR(r));
1808
1809   rv = apr_stat(&st, tmpfile, APR_FINFO_MIN, r->pool);
1810   if (rv != APR_SUCCESS) {
1811     DBG(r, "REQ[%X] end s_header_only_cache_file()", TO_ADDR(r));
1812     return HTTP_NOT_FOUND;
1813   }
1814
1815   DBG(r, "REQ[%X] mode:[%d]",    TO_ADDR(r), query_string->mode);
1816   DBG(r, "REQ[%X] name:[%s]",    TO_ADDR(r), query_string->name);
1817   DBG(r, "REQ[%X] offset:[%ld]", TO_ADDR(r), query_string->offset);
1818   DBG(r, "REQ[%X] count:[%ld]",  TO_ADDR(r), query_string->count);
1819
1820   if (query_string->mode != IMG_CONV_MODE_EZGET && query_string->name == NULL) {
1821     contentLength = apr_psprintf(r->pool, "%d", (int)st.size);
1822     apr_table_setn(r->headers_out, "Content-Length", (const char*)contentLength);
1823
1824     MagickWand *magick_wand = NewMagickWand();
1825     if (MagickReadImage(magick_wand,tmpfile) == MagickFalse) {
1826       EXIT_MAGICK_ERROR();
1827       return HTTP_NOT_FOUND;
1828     }
1829     if (MagickStripImage(magick_wand) == MagickFalse) {
1830       ERR(r, "mod_chxj: strip image failure.");
1831       EXIT_MAGICK_ERROR();
1832       return HTTP_NOT_FOUND;
1833     }
1834     char *nowFormat = MagickGetImageFormat(magick_wand);
1835     DestroyMagickWand(magick_wand);
1836     if (nowFormat) {
1837       if (STRCASEEQ('j','J',"jpeg",nowFormat) || STRCASEEQ('j','J',"jpg",nowFormat)) {
1838         DBG(r, "detect cache file => jpg.");
1839         ap_set_content_type(r, "image/jpeg");
1840       }
1841       else if (STRCASEEQ('p','P',"png", nowFormat)) {
1842         DBG(r, "detect cache file => png.");
1843         ap_set_content_type(r, "image/png");
1844       }
1845       else if (STRCASEEQ('g','G',"gif", nowFormat)) {
1846         DBG(r, "detect cache file => gif.");
1847         ap_set_content_type(r, "image/gif");
1848       }
1849       else if (STRCASEEQ('b','B',"bmp", nowFormat)) {
1850         DBG(r, "detect cache file => bmp.");
1851         ap_set_content_type(r, "image/bmp");
1852       }
1853       else {
1854         ERR(r, "REQ[%X] detect unknown file", TO_ADDR(r));
1855         return HTTP_NOT_FOUND;
1856       }
1857     }
1858   
1859     DBG(r,"Content-Length:[%d]", (int)st.size);
1860   }
1861   else
1862   if (query_string->mode == IMG_CONV_MODE_EZGET) {
1863     char *name = apr_pstrdup(r->pool, basename(r->filename));
1864     name[strlen(name)-4] = 0;
1865     if (strcasecmp(r->content_type, "image/jpeg") == 0) {
1866
1867       chxj_set_content_type(r, "text/x-hdml; charset=Shift_JIS");
1868     }
1869     else
1870     if (strcasecmp(r->content_type, "image/bmp") == 0) {
1871       chxj_set_content_type(r, "text/x-hdml; charset=Shift_JIS");
1872     }
1873     else
1874     if (strcasecmp(r->content_type, "image/png") == 0) {
1875       chxj_set_content_type(r, "text/x-hdml; charset=Shift_JIS");
1876     }
1877     else
1878     if (strcasecmp(r->content_type, "image/gif") == 0) {
1879       chxj_set_content_type(r, "text/x-hdml; charset=Shift_JIS");
1880     }
1881   }
1882   else
1883   if (query_string->mode == IMG_CONV_MODE_WALLPAPER && query_string->name != NULL) {
1884     if (query_string->count == -1 && query_string->offset == -1) {
1885       chxj_set_content_type(r, "text/x-hdml; charset=Shift_JIS");
1886     }
1887     else
1888     if (query_string->count == -2 && query_string->offset == -1) {
1889       chxj_set_content_type(r, "text/x-hdml; charset=Shift_JIS");
1890     }
1891     else { 
1892       chxj_set_content_type(r, "application/x-up-download");
1893       contentLength = apr_psprintf(r->pool, "%ld", query_string->count);
1894       apr_table_setn(r->headers_out, "Content-Length", (const char*)contentLength);
1895   
1896       DBG(r, "REQ[%X] Content-Length:[%d]", TO_ADDR(r), (int)st.size);
1897     }
1898   }
1899   if (conf->image_copyright) {
1900     DBG(r, "REQ[%X] Add COPYRIGHT Header for SoftBank [%s]", TO_ADDR(r), conf->image_copyright);
1901     if (spec->html_spec_type == CHXJ_SPEC_Jhtml ||  spec->html_spec_type == CHXJ_SPEC_Jxhtml) {
1902       apr_table_setn(r->headers_out, "x-jphone-copyright", "no-transfer");
1903     }
1904   }
1905   
1906   DBG(r, "REQ[%X] end s_header_only_cache_file()", TO_ADDR(r));
1907   return OK;
1908 }
1909
1910
1911 static void 
1912 s_init_serial_pattern(apr_pool_t *p)
1913 {
1914   if (!v_docomo_serial_pattern1) {
1915     v_docomo_serial_pattern1 = chxj_compile_for_preg_replace(p, "/ser[^;\\)]+");
1916   }  
1917   if (!v_docomo_serial_pattern2) {
1918     v_docomo_serial_pattern2 = chxj_compile_for_preg_replace(p, ";ser[^;\\)]+");
1919   }  
1920   if (!v_docomo_serial_pattern3) {
1921     v_docomo_serial_pattern3 = chxj_compile_for_preg_replace(p, ";icc[^;\\)]+");
1922   }  
1923   if (!v_softbank_serial_pattern1) {
1924     v_softbank_serial_pattern1 = chxj_compile_for_preg_replace(p, "/SN[0-9a-zA-Z]+ ");
1925   }  
1926 }
1927
1928
1929 static char *
1930 s_create_workfile_name(
1931                 request_rec          *r, 
1932                 mod_chxj_config      *conf, 
1933                 const char           *user_agent, 
1934                 query_string_param_t *qsp)
1935 {
1936   int  ii;
1937   int  jj;
1938   int  len;
1939   char *w = apr_palloc(r->pool, 256);
1940   char *fname;
1941   char *new_user_agent;
1942
1943   s_init_serial_pattern(r->server->process->pool);
1944
1945   /* for DoCoMo */
1946   new_user_agent = chxj_preg_replace(r->pool, v_docomo_serial_pattern1, "", user_agent);
1947   new_user_agent = chxj_preg_replace(r->pool, v_docomo_serial_pattern2, "", new_user_agent);
1948   new_user_agent = chxj_preg_replace(r->pool, v_docomo_serial_pattern3, "", new_user_agent);
1949
1950   /* for SoftBank */
1951   new_user_agent = chxj_preg_replace(r->pool, v_softbank_serial_pattern1, " ", new_user_agent);
1952
1953   DBG(r, "old user_agent:[%s] ==> new user_agent:[%s]", user_agent, new_user_agent);
1954
1955
1956   memset(w, 0, 256);
1957   switch (qsp->mode) {
1958   case IMG_CONV_MODE_THUMBNAIL:
1959     fname = apr_psprintf(r->pool, "%s.%s.thumbnail", r->filename, new_user_agent);
1960     DBG(r, "mode=thumbnail [%s]", fname);
1961     break;
1962
1963   case IMG_CONV_MODE_WALLPAPER:
1964   case IMG_CONV_MODE_EZGET:
1965     fname = apr_psprintf(r->pool, "%s.%s.wallpaper", r->filename, new_user_agent);
1966     DBG(r, "mode=WallPaper [%s]", fname);
1967     break;
1968
1969   case IMG_CONV_MODE_NORMAL:
1970   default:
1971
1972     fname = apr_psprintf(r->pool, "%s.%s", r->filename, new_user_agent);
1973
1974     if (qsp->width)
1975       fname = apr_psprintf(r->pool, "%s.w%d", fname, qsp->width);
1976
1977     if (qsp->height)
1978       fname = apr_psprintf(r->pool, "%s.h%d", fname, qsp->height);
1979
1980     DBG(r,"mode=normal [%s]", fname);
1981     break;
1982   }
1983   if (qsp->ua_flag == UA_IGN) {
1984     fname = apr_psprintf(r->pool, "%s.IGN", fname);
1985   }
1986
1987   len = strlen(fname);
1988   jj=0;
1989   for  (ii=0; ii<len; ii++) {
1990     if (fname[ii] == '/' 
1991     ||  fname[ii] == ' ' 
1992     ||  fname[ii] == '-' 
1993     ||  fname[ii] == '(' 
1994     ||  fname[ii] == ')') {
1995       w[jj++] = '_';
1996     }
1997     else {
1998       w[jj++] = fname[ii];
1999     }
2000   }
2001
2002   return apr_psprintf(r->pool, "%s/%s", conf->image_cache_dir,w);
2003 }
2004
2005
2006 static unsigned short
2007 s_add_crc(const char *writedata, apr_size_t writebyte)
2008 {
2009   unsigned short crc = 0xffff;
2010   apr_size_t     ii;
2011   unsigned char  ch;
2012
2013   for(ii=0;ii<writebyte;ii++) {
2014     ch  = writedata[ii];
2015     crc = AU_CRC_TBL[(crc>>8^ch)&0xff]^(crc<<8);
2016   }
2017   return crc;
2018 }
2019
2020
2021 int
2022 chxj_trans_name(request_rec *r)
2023 {
2024   const char      *ccp;
2025   char            *docroot;
2026   int             len;
2027   apr_finfo_t     st;
2028   apr_status_t    rv;
2029   mod_chxj_config *conf;
2030   int             ii;
2031   char            *ext[] = {
2032           "jpg",
2033           "JPG",
2034           "jpeg",
2035           "JPEG",
2036           "png",
2037           "PNG",
2038           "bmp",
2039           "BMP",
2040           "gif",
2041           "GIF",
2042           "qrc",    /* QRCode出力用ファイルの拡張子 */
2043           "",
2044   };
2045   char     *fname = NULL;
2046   char     *idx;
2047   char     *filename_sv;
2048   int      do_ext_check = TRUE;
2049   int      next_ok      = FALSE;
2050
2051   DBG(r, "start chxj_trans_name()");
2052
2053   conf = chxj_get_module_config(r->per_dir_config, &chxj_module);
2054
2055   if (conf == NULL) {
2056     DBG(r, "end chxj_trans_name() conf is null[%s]", r->uri);
2057     return DECLINED;
2058   }
2059
2060   if (conf->image != CHXJ_IMG_ON) {
2061     DBG(r, "end chxj_trans_name() conf not found");
2062     return DECLINED;
2063   }
2064
2065
2066   DBG(r,"Match URI[%s]", r->uri);
2067
2068
2069   if (r->filename == NULL) 
2070     r->filename = apr_pstrdup(r->pool, r->uri);
2071
2072   filename_sv = NULL;
2073   if ((idx = strchr(r->filename, ':')) != NULL) 
2074     filename_sv = idx+1;
2075   else 
2076     filename_sv = r->filename;
2077
2078   DBG(r,"r->filename[%s]", filename_sv);
2079
2080   ccp = ap_document_root(r);
2081   if (ccp == NULL)
2082     return HTTP_INTERNAL_SERVER_ERROR;
2083
2084   docroot = apr_pstrdup(r->pool, ccp);
2085   len = strlen(docroot);
2086
2087   if (docroot[len-1] == '/') 
2088     docroot[len-1] = '\0';
2089
2090
2091   if (r->server->path 
2092   &&  *filename_sv == *r->server->path 
2093   &&  strncmp(filename_sv, r->server->path, r->server->pathlen) == 0)
2094     filename_sv = apr_pstrcat(r->pool, docroot, (filename_sv + r->server->pathlen), NULL);
2095   else
2096     filename_sv = apr_pstrcat(r->pool, docroot, filename_sv, NULL);
2097
2098   DBG(r,"URI[%s]", filename_sv);
2099
2100   do_ext_check = TRUE;
2101   for (ii=0; ii<(int)(sizeof(ext)/sizeof(ext[0])); ii++) {
2102     char* pos = strrchr(filename_sv, '.');
2103     if (pos && pos++) {
2104       if (strcasecmp(pos, ext[ii]) == 0) {
2105         do_ext_check = FALSE;
2106         fname = apr_psprintf(r->pool, "%s", filename_sv);
2107         break;
2108       }
2109     }
2110   }
2111
2112   if (do_ext_check) {
2113     for (ii=0; ii<(int)(sizeof(ext)/sizeof(ext[0])); ii++) {
2114       if (strlen(ext[ii]) == 0) {
2115         fname = apr_psprintf(r->pool, "%s", filename_sv);
2116       }
2117       else 
2118         fname = apr_psprintf(r->pool, "%s.%s", filename_sv, ext[ii]);
2119   
2120       DBG(r,"search [%s]", fname);
2121   
2122       rv = apr_stat(&st, fname, APR_FINFO_MIN, r->pool);
2123       if (rv == APR_SUCCESS) {
2124         if (st.filetype != APR_DIR)
2125           break;
2126       }
2127   
2128       fname = NULL;
2129     }
2130   }
2131   if (fname == NULL) {
2132     DBG(r,"NotFound [%s]", r->filename);
2133     return DECLINED;
2134   }
2135   for (ii=0; ii<(int)(sizeof(ext)/sizeof(ext[0])); ii++) {
2136     char* pos = strrchr(fname, '.');
2137     if (pos && pos++) {
2138       if (strcasecmp(pos, ext[ii]) == 0) {
2139         next_ok = TRUE;
2140         break;
2141       }
2142     }
2143   }
2144
2145   if (! next_ok)  {
2146     DBG(r,"NotFound [%s]", r->filename);
2147     return DECLINED;
2148   }
2149
2150   if (r->handler == NULL || strcasecmp(r->handler, "chxj-qrcode") != 0) {
2151     DBG(r,"Found [%s]", fname);
2152
2153     r->filename = apr_psprintf(r->pool, "%s", fname);
2154   
2155     if (strcasecmp("qrc", ext[ii]) == 0)
2156       r->handler = apr_psprintf(r->pool, "chxj-qrcode");
2157     else
2158       r->handler = apr_psprintf(r->pool, "chxj-picture");
2159   }
2160   DBG(r, "end chxj_trans_name()");
2161   return OK;
2162 }
2163
2164
2165
2166 static int
2167 s_chxj_trans_name2(request_rec *r)
2168 {
2169   apr_finfo_t     st;
2170   apr_status_t    rv;
2171   mod_chxj_config *conf;
2172   int             ii;
2173   char            *ext[] = {
2174           "jpg",
2175           "JPG",
2176           "jpeg",
2177           "JPEG",
2178           "png",
2179           "PNG",
2180           "bmp",
2181           "BMP",
2182           "gif",
2183           "GIF",
2184           "qrc",    /* QRCode出力用ファイルの拡張子 */
2185           "",
2186   };
2187   char     *fname = NULL;
2188   char     *filename_sv;
2189   int      do_ext_check = TRUE;
2190   int      next_ok      = FALSE;
2191
2192   DBG(r, "REQ[%X] start chxj_trans_name2()", (unsigned int)(apr_size_t)r);
2193
2194   conf = chxj_get_module_config(r->per_dir_config, &chxj_module);
2195
2196   if (conf == NULL) {
2197     DBG(r, "REQ[%X] end chxj_trans_name2() conf is null[%s]", (unsigned int)(apr_size_t)r, r->uri);
2198     return DECLINED;
2199   }
2200
2201   if (conf->image != CHXJ_IMG_ON) {
2202     DBG(r, "REQ[%X] end chxj_trans_name2() ImageEngineOff", (unsigned int)(apr_size_t)r);
2203     return DECLINED;
2204   }
2205
2206
2207   DBG(r,"Match URI[%s]", r->uri);
2208
2209   if (r->filename == NULL) {
2210     DBG(r, "REQ[%X] end chxj_trans_name2() r->filename is null", (unsigned int)(apr_size_t)r);
2211     return DECLINED;
2212   }
2213      
2214   filename_sv = r->filename;
2215
2216   DBG(r,"REQ[%x] r->filename[%s]", (unsigned int)(apr_size_t)r, filename_sv);
2217
2218   do_ext_check = TRUE;
2219   for (ii=0; ii<(int)(sizeof(ext)/sizeof(ext[0])); ii++) {
2220     char* pos = strrchr(filename_sv, '.');
2221     if (pos && pos++) {
2222       if (strcasecmp(pos, ext[ii]) == 0) {
2223         do_ext_check = FALSE;
2224         fname = apr_psprintf(r->pool, "%s", filename_sv);
2225         break;
2226       }
2227     }
2228   }
2229
2230   if (do_ext_check) {
2231     for (ii=0; ii<(int)(sizeof(ext)/sizeof(ext[0])); ii++) {
2232       if (strlen(ext[ii]) == 0) {
2233         fname = apr_psprintf(r->pool, "%s", filename_sv);
2234       }
2235       else 
2236         fname = apr_psprintf(r->pool, "%s.%s", filename_sv, ext[ii]);
2237   
2238       DBG(r,"search [%s]", fname);
2239   
2240       rv = apr_stat(&st, fname, APR_FINFO_MIN, r->pool);
2241       if (rv == APR_SUCCESS) {
2242         if (st.filetype != APR_DIR)
2243           break;
2244       }
2245   
2246       fname = NULL;
2247     }
2248   }
2249   if (fname == NULL) {
2250     DBG(r,"NotFound [%s]", r->filename);
2251     return DECLINED;
2252   }
2253   for (ii=0; ii<(int)(sizeof(ext)/sizeof(ext[0])); ii++) {
2254     char* pos = strrchr(fname, '.');
2255     if (pos && pos++) {
2256       if (strcasecmp(pos, ext[ii]) == 0) {
2257         next_ok = TRUE;
2258         break;
2259       }
2260     }
2261   }
2262
2263   if (! next_ok)  {
2264     DBG(r,"NotFound [%s]", r->filename);
2265     return DECLINED;
2266   }
2267
2268   if (r->handler == NULL || strcasecmp(r->handler, "chxj-qrcode") != 0) {
2269     DBG(r,"Found [%s]", fname);
2270
2271     r->filename = apr_psprintf(r->pool, "%s", fname);
2272   
2273     if (strcasecmp("qrc", ext[ii]) == 0)
2274       r->handler = apr_psprintf(r->pool, "chxj-qrcode");
2275     else
2276       r->handler = apr_psprintf(r->pool, "chxj-picture");
2277   }
2278   DBG(r, "REQ[%X] end chxj_trans_name()", (unsigned int)(apr_size_t)r);
2279   return OK;
2280 }
2281
2282
2283
2284 /**
2285  * It converts it from QUERYSTRING.
2286  *
2287  * @param r   [i]
2288  */
2289 static query_string_param_t *
2290 s_get_query_string_param(request_rec *r)
2291 {
2292   char *pair;
2293   char *name;
2294   char *value;
2295   char *pstate;
2296   char *vstate;
2297   char *s;
2298   query_string_param_t *param;
2299
2300   s = apr_pstrdup(r->pool, r->parsed_uri.query);
2301   param = apr_palloc(r->pool, sizeof(query_string_param_t));
2302   param->mode       = IMG_CONV_MODE_NORMAL;
2303   param->user_agent = NULL;
2304   param->ua_flag    = UA_USE;
2305   param->name       = NULL;
2306   param->offset     = 0;
2307   param->count      = 0;
2308   param->width      = 0;
2309   param->height     = 0;
2310
2311   if (s == NULL) return param;
2312
2313   for (;;) {
2314     if ((pair = apr_strtok(s, "&", &pstate)) == NULL) break;
2315     s = NULL;
2316
2317     name  = apr_strtok(pair, "=", &vstate);
2318     value = apr_strtok(NULL, "=", &vstate);
2319
2320     switch (*name) {
2321     case 'm':
2322     case 'M':
2323       if (value && (strcasecmp(name, "mode") == 0 || strcasecmp(name, "m") == 0)) {
2324
2325         switch (*value) {
2326         case 't':
2327         case 'T':
2328           if (strcasecmp(value, "thumbnail") == 0 || strcasecmp(value, "tb") == 0)
2329             param->mode = IMG_CONV_MODE_THUMBNAIL;
2330           break;
2331   
2332         case 'w':
2333         case 'W':
2334           if (strcasecmp(value, "WP") == 0 || strcasecmp(value, "WallPaper") == 0)
2335             param->mode = IMG_CONV_MODE_WALLPAPER;
2336           break;
2337   
2338         case 'e':
2339         case 'E':
2340           if (strcasecmp(value, "EZGET") == 0)
2341             param->mode = IMG_CONV_MODE_EZGET;
2342           break;
2343         default:
2344           break;
2345         }
2346       }
2347       break;
2348
2349     case 'u':
2350     case 'U':
2351       if (value && (strcasecmp(name, "ua") == 0 || strcasecmp(name, "user-agent") == 0)) {
2352         ap_unescape_url(value);
2353
2354         if ((*value == 'i' || *value == 'I') && strcasecmp(value, "IGN") == 0)
2355           param->ua_flag = UA_IGN;
2356
2357         param->user_agent = apr_pstrdup(r->pool, value);
2358       }
2359       break;
2360
2361     case 'n':
2362     case 'N':
2363       if (value && strcasecmp(name, "name") == 0)
2364         param->name = apr_pstrdup(r->pool, value);
2365       break;
2366
2367     case 'o':
2368     case 'O':
2369       if (value && strcasecmp(name, "offset") == 0 && (! chxj_chk_numeric(value)))
2370         param->offset = chxj_atoi(value);
2371
2372       break;
2373
2374     case 'c':
2375     case 'C':
2376       if (value && strcasecmp(name, "count") == 0 && (! chxj_chk_numeric(value)))
2377         param->count = chxj_atoi(value);
2378       break;
2379
2380     case 'w':
2381     case 'W':
2382       if (value && strcasecmp(name, "w") == 0 && (! chxj_chk_numeric(value)))
2383         param->width = chxj_atoi(value);
2384       break;
2385
2386     case 'h':
2387     case 'H':
2388       if (value && strcasecmp(name, "h") == 0 && (! chxj_chk_numeric(value)))
2389         param->height = chxj_atoi(value);
2390       break;
2391
2392     default:
2393       break;
2394     }
2395   }
2396
2397   if (param->mode == IMG_CONV_MODE_NORMAL && param->name)
2398     param->mode = IMG_CONV_MODE_WALLPAPER;
2399
2400   return param;
2401 }
2402
2403
2404 static char *
2405 s_add_comment_to_png(request_rec *r, char *data, apr_size_t *len)
2406 {
2407   char *result = NULL;
2408 #define PNG_COPYRIGHT_KEY "Copyright"
2409 #define PNG_COPYRIGHT_VAL "kddi_copyright=on,copy=NO"
2410 #define PNG_SIG_AND_IHDR_SZ (33)
2411   char *key = PNG_COPYRIGHT_KEY;
2412   apr_size_t klen = sizeof(PNG_COPYRIGHT_KEY)-1;
2413   char *val = PNG_COPYRIGHT_VAL;
2414   apr_size_t vlen = sizeof(PNG_COPYRIGHT_VAL)-1;
2415   apr_pool_t *pool;
2416   apr_size_t total_tEXt_size;
2417   apr_size_t tEXt_data_size;
2418   apr_size_t pos;
2419   apr_size_t ii;
2420   char *buf;
2421   char *valbuf;
2422   uint32_t crc;
2423   mod_chxj_config *conf = chxj_get_module_config(r->per_dir_config, &chxj_module);
2424
2425   DBG(r, "REQ[%X] start s_add_comment_to_png()",(unsigned int)(apr_size_t)r);
2426   if (conf->image_copyright) {
2427     apr_pool_create(&pool, r->pool);
2428
2429     valbuf = apr_psprintf(pool, "%s,%s", val, conf->image_copyright);
2430     vlen = strlen(valbuf);
2431   
2432     /* total_size = length + "tEXt" + "Copyright" + '\0' + val + crc */
2433     total_tEXt_size = 4 + 4 + klen + vlen + 1 + 4;
2434     result = apr_palloc(pool, total_tEXt_size + *len);
2435     if (!result) {
2436       DBG(r, "REQ[%X] memory allocation error.", (unsigned int)(apr_size_t)r);
2437       return NULL;
2438     }
2439     pos = PNG_SIG_AND_IHDR_SZ;
2440     memcpy(result, data, pos); /* 33 = SIGNATURE + IHDR */
2441     tEXt_data_size = klen + 1 + vlen;
2442     result[pos + 0] = tEXt_data_size >> 24;
2443     result[pos + 1] = tEXt_data_size >> 16;
2444     result[pos + 2] = tEXt_data_size >> 8;
2445     result[pos + 3] = tEXt_data_size;
2446     pos += 4;
2447     buf = apr_palloc(pool, 4 + klen + 1 + vlen);
2448     memcpy(&buf[0], "tEXt", 4); 
2449     memcpy(&buf[4], key, klen);
2450     buf[4+klen] = 0;
2451     memcpy(&buf[4+klen+1], valbuf, vlen);
2452     
2453     
2454     DBG(r, "REQ[%X] buf:[%s]", (unsigned int)(apr_size_t)r, buf);
2455   
2456     crc = 0xffffffff;
2457     for (ii = 0; ii < 4 + tEXt_data_size; ii++) {
2458       crc = AU_CRC_TBL[(crc ^ buf[ii]) & 0xff] ^ (crc >> 8);
2459     }
2460     crc ^= 0xffffffff;
2461     memcpy(&result[pos], buf, 4 + klen + 1 + vlen);
2462     pos += (4 + klen + 1 + vlen);
2463     result[pos + 0] = crc >> 24;
2464     result[pos + 1] = crc >> 16;
2465     result[pos + 2] = crc >> 8;
2466     result[pos + 3] = crc;
2467     pos += 4;
2468     memcpy(&result[pos], &data[PNG_SIG_AND_IHDR_SZ] , *len - PNG_SIG_AND_IHDR_SZ);
2469     *len = *len + total_tEXt_size;
2470     DBG(r, "REQ[%X] writebyte:[%d]", (unsigned int)(apr_size_t)r, (unsigned int)*len);
2471   }
2472   else {
2473     result = data;
2474   }
2475   DBG(r, "REQ[%X] end s_add_comment_to_png()",(unsigned int)(apr_size_t)r);
2476 #undef PNG_SIG_AND_IHDR_SZ
2477 #undef PNG_COPYRIGHT_KEY
2478 #undef PNG_COPYRIGHT_VAL
2479   return result;
2480 }
2481 /*
2482  * vim:ts=2 et
2483  */