OSDN Git Service

* version up for RELEASE
[modchxj/mod_chxj.git] / src / chxj_encoding.c
1 /*
2  * Copyright (C) 2005-2011 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 "mod_chxj.h"
18 #include "chxj_encoding.h"
19 #include "chxj_apply_convrule.h"
20 #include "chxj_url_encode.h"
21 #include "chxj_dump_string.h"
22 #include <errno.h>
23 #include <iconv.h>
24
25
26 char *
27 chxj_encoding(request_rec *r, const char *src, apr_size_t *len)
28 {
29   char                *obuf;
30   char                *ibuf;
31   char                *spos;
32   
33   iconv_t             cd;
34   size_t              result;
35   apr_size_t          ilen;
36   apr_size_t          olen;
37   mod_chxj_config     *dconf;
38   mod_chxj_req_config *req_conf;
39   chxjconvrule_entry  *entryp;
40   apr_pool_t          *pool;
41
42
43   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
44
45   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
46
47   if (dconf == NULL) {
48     DBG(r,"REQ[%X] none encoding.", TO_ADDR(r));
49     DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
50     return (char*)src;
51   }
52   if ((int)*len < 0) {
53     ERR(r,"REQ[%X] runtime exception: chxj_encoding(): invalid string size.[%d]", TO_ADDR(r),(int)*len);
54     DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
55     return (char *)apr_pstrdup(r->pool, "");
56   }
57
58   req_conf = chxj_get_module_config(r->request_config, &chxj_module);
59   /*-------------------------------------------------------------------------*/
60   /* already setup entryp if request_conf->user_agent is not null            */
61   /*-------------------------------------------------------------------------*/
62   if (req_conf->user_agent) {
63     entryp = req_conf->entryp;
64   }
65   else {
66     entryp = chxj_apply_convrule(r, dconf->convrules);
67   }
68   if (entryp->encoding == NULL) {
69     DBG(r,"REQ[%X] none encoding.", TO_ADDR(r));
70     DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
71     return (char *)src;
72   }
73
74   if (STRCASEEQ('n','N',"none", entryp->encoding)) {
75     DBG(r,"REQ[%X] none encoding.", TO_ADDR(r));
76     DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
77     return (char*)src;
78   }
79
80   apr_pool_create(&pool, r->pool);
81   ilen = *len;
82   ibuf = apr_palloc(pool, ilen+1);
83   if (ibuf == NULL) {
84     ERR(r,"REQ[%X] runtime exception: chxj_encoding(): Out of memory.",TO_ADDR(r));
85     DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
86     return (char *)src;
87   }
88   memset(ibuf, 0, ilen+1);
89   memcpy(ibuf, src, ilen);
90
91   olen = ilen * 4 + 1;
92   spos = obuf = apr_palloc(pool, olen);
93   if (obuf == NULL) {
94     DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
95     return ibuf;
96   }
97   DBG(r,"REQ[%X] encode convert [%s] -> [%s]", TO_ADDR(r), entryp->encoding, "CP932");
98
99   memset(obuf, 0, olen);
100   cd = iconv_open("CP932", entryp->encoding);
101   if (cd == (iconv_t)-1) {
102     if (EINVAL == errno) {
103       ERR(r,"REQ[%X] The conversion from %s to %s is not supported by the implementation.", TO_ADDR(r),entryp->encoding, "CP932");
104     }
105     else {
106       ERR(r,"REQ[%X] iconv open failed. from:[%s] to:[%s] errno:[%d]", TO_ADDR(r),entryp->encoding, "CP932", errno);
107     }
108     DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
109     return ibuf;
110   }
111   while (ilen > 0) {
112     result = iconv(cd, &ibuf, &ilen, &obuf, &olen);
113     if (result == (size_t)(-1)) {
114       if (E2BIG == errno) {
115         ERR(r, "REQ[%X] There is not sufficient room at *outbuf.",TO_ADDR(r));
116         break;
117       }
118       else if (EILSEQ == errno) {
119         ERR(r,"REQ[%X] %s:%d An invalid multibyte sequence has been encountered in the input. input:[%s]", TO_ADDR(r),__FILE__,__LINE__,ibuf);
120         chxj_convert_illegal_charactor_sequence(r, entryp, &ibuf, &ilen, &obuf, &olen);
121       }
122       else if (EINVAL == errno) {
123         ERR(r,"REQ[%X] An incomplete multibyte sequence has been encountered in the input. input:[%s]", TO_ADDR(r),ibuf);
124         break;
125       }
126     }
127   }
128   *len = strlen(spos);
129   iconv_close(cd);
130
131   chxj_dump_string(r, APLOG_MARK, "RESULT Convert Encoding", spos, *len);
132   DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
133   return spos;
134 }
135
136
137 void
138 chxj_convert_illegal_charactor_sequence(request_rec *r, chxjconvrule_entry  *entryp, char **ibuf, apr_size_t *ilen, char **obuf, apr_size_t *olen)
139 {
140   if (STRCASEEQ('u','U',"UTF-8", entryp->encoding) || STRCASEEQ('u','U',"UTF8", entryp->encoding)) {
141     if ((0xe0 & **ibuf) == 0xc0) {
142       /* 2byte charactor */
143       **obuf = '?';
144       *obuf += 1;
145       *olen -= 1;
146       *ibuf += 2;
147       DBG(r,"REQ[%X] passed 2byte.",TO_ADDR(r));
148     }
149     else if ((0xf0 & **ibuf) == 0xe0) {
150       /* 3byte charactor */
151       **obuf = '?';
152       *obuf += 1;
153       *olen -= 1;
154       *ibuf +=3;
155       DBG(r,"REQ[%X] passed 3byte.",TO_ADDR(r));
156     }
157     else if ((0xf8 & **ibuf) == 0xf0) {
158       /* 4byte charactor */
159       **obuf = '?';
160       *obuf += 1;
161       *olen -= 1;
162       *ibuf +=4;
163       DBG(r,"REQ[%X] passed 4byte.",TO_ADDR(r));
164     }
165     else if ((0xc0 & **ibuf) == 0x80) {
166       /* 1byte charactor */
167       **obuf = '?';
168       *obuf += 1;
169       *olen -= 1;
170       *ibuf += 1;
171       DBG(r,"REQ[%X] passed 1byte.",TO_ADDR(r));
172     }
173     else {
174       /* unknown charactor */
175       **obuf = '?';
176       *obuf += 1;
177       *olen -= 1;
178       *ibuf += 1;
179       DBG(r,"REQ[%X] passed 1byte.",TO_ADDR(r));
180     }
181   }
182   else if (STRCASEEQ('e','E', "EUCJP",               entryp->encoding)
183       ||   STRCASEEQ('c','C', "CSEUCPKDFMTJAPANESE", entryp->encoding)
184       ||   STRCASEEQ('e','E', "EUC-JISX0213",        entryp->encoding)
185       ||   STRCASEEQ('e','E', "EUC-JP-MS",           entryp->encoding)
186       ||   STRCASEEQ('e','E', "EUC-JP",              entryp->encoding)
187       ||   STRCASEEQ('e','E', "EUCJP-MS",            entryp->encoding)
188       ||   STRCASEEQ('e','E', "EUCJP-OPEN",          entryp->encoding)
189       ||   STRCASEEQ('e','E', "EUCJP-WIN",           entryp->encoding)
190       ||   STRCASEEQ('e','E', "EUCJP",               entryp->encoding)) {
191     if ((unsigned char)**ibuf == 0x8F) {
192       /* 3byte charactor */
193       **obuf = '?';
194       *obuf += 1;
195       *olen -= 1;
196       *ibuf +=3;
197       DBG(r,"REQ[%X] passed 3byte.",TO_ADDR(r));
198     }
199     else {
200       /* 2byte charactor */
201       **obuf = '?';
202       *obuf += 1;
203       *olen -= 1;
204       *ibuf += 2;
205       DBG(r,"REQ[%X] passed 2byte.",TO_ADDR(r));
206     }
207   }
208   else if (STRCASEEQ('c', 'C', "CP932",     entryp->encoding)
209       ||   STRCASEEQ('c', 'C', "CSIBM932",  entryp->encoding)
210       ||   STRCASEEQ('i', 'I', "IBM-932",   entryp->encoding)
211       ||   STRCASEEQ('i', 'I', "IBM932",    entryp->encoding)
212       ||   STRCASEEQ('m', 'M', "MS932",     entryp->encoding)
213       ||   STRCASEEQ('m', 'M', "MS_KANJI",  entryp->encoding)
214       ||   STRCASEEQ('s', 'S', "SJIS-OPEN", entryp->encoding)
215       ||   STRCASEEQ('s', 'S', "SJIS-WIN",  entryp->encoding)
216       ||   STRCASEEQ('s', 'S', "SJIS",      entryp->encoding)) {
217     if ( ( ((0x81 <= (unsigned char)**ibuf) && (0x9f >= (unsigned char)**ibuf))
218         || ((0xe0 <= (unsigned char)**ibuf) && (0xfc >= (unsigned char)**ibuf)))
219        &&
220        (  ((0x40 <= (unsigned char)*((*ibuf)+1)) && (0x7e >= (unsigned char)*((*ibuf)+1)))
221         ||((0x80 <= (unsigned char)*((*ibuf)+1)) && (0xfc >= (unsigned char)*((*ibuf)+1))))) {
222       /* 2byte charactor */
223       **obuf = '?';
224       *obuf += 1;
225       *olen -= 1;
226       *ibuf += 2;
227       DBG(r,"REQ[%X] passed 2byte.",TO_ADDR(r));
228     }
229     else {
230       /* 1byte charactor */
231       **obuf = '?';
232       *obuf += 1;
233       *olen -= 1;
234       *ibuf += 1;
235       DBG(r,"REQ[%X] passed 1byte.",TO_ADDR(r));
236     }
237   }
238   else {
239     /* unknown 1byte charactor */
240     **obuf = '?';
241     *obuf += 1;
242     *olen -= 1;
243     *ibuf += 1;
244     DBG(r,"REQ[%X] passed 1byte.",TO_ADDR(r));
245   }
246   if (ibuf && *ibuf) {
247     *ilen = strlen(*ibuf);
248     DBG(r,"REQ[%X] new len = [%" APR_SIZE_T_FMT "].", TO_ADDR(r),(apr_size_t)*ilen);
249   }
250 }
251
252
253 char *
254 chxj_rencoding(request_rec *r, const char *src, apr_size_t *len,const char *enc)
255 {
256   char                *obuf;
257   char                *ibuf;
258   char                *spos;
259   
260   iconv_t             cd;
261   size_t              result;
262   apr_size_t          ilen;
263   apr_size_t          olen;
264   mod_chxj_config     *dconf;
265   mod_chxj_req_config *req_conf;
266   chxjconvrule_entry  *entryp;
267
268   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
269
270   if ((int)*len < 0) {
271     ERR(r,"REQ[%X] runtime exception: chxj_rencoding(): invalid string size.[%d]",TO_ADDR(r),(int)*len);
272     DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
273     return (char *)apr_pstrdup(r->pool, "");
274   }
275
276   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
277   if (! dconf) {
278     DBG(r,"REQ[%X] none encoding.", TO_ADDR(r));
279     DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
280     return (char*)src;
281   }
282
283   req_conf = chxj_get_module_config(r->request_config, &chxj_module);
284   /*-------------------------------------------------------------------------*/
285   /* already setup entryp if request_conf->user_agent is not null            */
286   /*-------------------------------------------------------------------------*/
287   if (req_conf->user_agent) {
288     entryp = req_conf->entryp;
289   }
290   else {
291     entryp = chxj_apply_convrule(r, dconf->convrules);
292   }
293   if (! entryp->encoding) {
294     DBG(r,"REQ[%X] none encoding.",TO_ADDR(r));
295     DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
296     return (char*)src;
297   }
298
299   if (STRCASEEQ('n','N',"none", entryp->encoding)) {
300     DBG(r,"REQ[%X] none encoding.",TO_ADDR(r));
301     DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
302     return (char*)src;
303   }
304
305   ilen = *len;
306   ibuf = apr_palloc(r->pool, ilen+1);
307   if (! ibuf) {
308     DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
309     return (char*)src;
310   }
311
312   memset(ibuf, 0,   ilen+1);
313   memcpy(ibuf, src, ilen+0);
314
315   olen = ilen * 4 + 1;
316   spos = obuf = apr_palloc(r->pool, olen);
317   if (! obuf) {
318     DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
319     return ibuf;
320   }
321   char *from_enc = (char *)enc;
322   if (!enc){
323     from_enc = "CP932";
324   }
325   if (strcasecmp(enc,"Shift_JIS") == 0){
326     from_enc = "CP932";
327   }
328   DBG(r,"REQ[%X] encode convert [%s] -> [%s]", TO_ADDR(r),from_enc, entryp->encoding);
329   memset(obuf, 0, olen);
330
331   cd = iconv_open(entryp->encoding, from_enc);
332   if (cd == (iconv_t)-1) {
333     if (EINVAL == errno) {
334       ERR(r,"REQ[%X] The conversion from %s to %s is not supported by the implementation.", TO_ADDR(r),"CP932", entryp->encoding);
335     }
336     DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
337     return ibuf;
338   }
339
340   while (ilen > 0) {
341     result = iconv(cd, &ibuf, &ilen, &obuf, &olen);
342     if (result == (size_t)(-1)) {
343       if (E2BIG == errno) {
344         ERR(r,"REQ[%X] There is not sufficient room at *outbuf",TO_ADDR(r));
345         break;
346       }
347       else if (EILSEQ == errno) {
348         ERR(r,"REQ[%X] An invalid multibyte sequence has been encountered in the input. input:[%s]", TO_ADDR(r),ibuf);
349         chxj_convert_illegal_charactor_sequence(r, entryp, &ibuf, &ilen, &obuf, &olen);
350       }
351       else if (EINVAL == errno) {
352         ERR(r,"REQ[%X] An incomplete multibyte sequence has been encountered in the input. input:[%s]", TO_ADDR(r),ibuf);
353         break;
354       }
355     }
356   }
357
358   *len = strlen(spos);
359   iconv_close(cd);
360
361   chxj_dump_string(r, APLOG_MARK, "RESULT Convert REncoding", spos, *len);
362   DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
363
364   return spos;
365 }
366
367
368 char *
369 chxj_encoding_parameter(request_rec *r, const char *value, int xmlflag)
370 {
371   char *src;
372   char *src_sv;
373   char *pstat;
374   char *spos;
375   char *pair;
376   char *key;
377   char *val;
378   char *vstat;
379   char *param;
380   char *anchor_pos;
381   char *anchor = NULL;
382
383   int   use_amp_flag;
384   
385   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
386
387   src = apr_pstrdup(r->pool, value);
388
389   anchor_pos = strchr(src, '#');
390   if (anchor_pos) {
391     anchor_pos++;
392     anchor = apr_pstrdup(r->pool, anchor_pos);
393     anchor_pos--;
394     *anchor_pos = 0;
395   }
396
397   spos = strchr(src, '?');
398   if (!spos) {
399     DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
400     if (anchor_pos) {
401       return apr_pstrcat(r->pool, src, "#", anchor, NULL);
402     } else {
403       return src;
404     }
405   }
406   *spos++ = 0;
407
408   src_sv = apr_pstrdup(r->pool, src);
409   param  = apr_palloc(r->pool, 1);
410   param[0] = 0;
411
412   for (;;) {
413     apr_size_t len;
414     char *sep_pos;
415
416     use_amp_flag = (xmlflag) ? 1 : 0;
417
418     pair = apr_strtok(spos, "&", &pstat);
419     spos = NULL;
420     if (!pair) break;
421     if (strncasecmp(pair, "amp;", 4) == 0) {
422       pair += 4;
423       use_amp_flag = 1;
424     }
425     sep_pos = strchr(pair, '=');
426     if (pair == sep_pos) {
427       key = apr_pstrdup(r->pool, "");
428     }
429     else {
430       key = apr_strtok(pair, "=", &vstat);
431       pair = NULL;
432     }
433     if (key) {
434       apr_size_t klen = (apr_size_t)strlen(key);
435       key = chxj_url_decode(r->pool, key);
436       len = (apr_size_t)strlen(key);
437       if (klen != len) {
438         key = chxj_encoding(r, key, &len);
439         key = chxj_url_encode(r->pool, key);
440       }
441 #if 0  /* XXX:2009/4/10 */
442       key = chxj_url_encode(r->pool, key);
443 #endif
444     }
445     val = apr_strtok(pair, "=", &vstat);
446     if (! val && sep_pos) {
447       val = apr_pstrdup(r->pool, "");
448     }
449     if (val) {
450       apr_size_t vlen = (apr_size_t)strlen(val);
451       val = chxj_url_decode(r->pool, val);
452       len = (apr_size_t)strlen(val);
453       if (vlen != len) {
454         val = chxj_encoding(r, val, &len);
455         val = chxj_url_encode(r->pool, val);
456       }
457 #if 0  /* XXX:2009/4/10 */
458       val = chxj_url_encode(r->pool, val);
459 #endif
460       if (strlen(param) == 0) {
461         param = apr_pstrcat(r->pool, param, key, "=", val, NULL);
462       }
463       else {
464         if (use_amp_flag) {
465           param = apr_pstrcat(r->pool, param, "&amp;", key, "=", val, NULL);
466         }
467         else {
468           param = apr_pstrcat(r->pool, param, "&", key, "=", val, NULL);
469         }
470       }
471     }
472     else {
473       if (strlen(param) == 0) {
474         param = apr_pstrcat(r->pool, param, key,  NULL);
475       }
476       else {
477         if (use_amp_flag) {
478           param = apr_pstrcat(r->pool, param, "&amp;", key, NULL);
479         }
480         else {
481           param = apr_pstrcat(r->pool, param, "&", key, NULL);
482         }
483       }
484     }
485   }
486   DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
487
488   if (anchor_pos) {
489     return apr_pstrcat(r->pool, src_sv, "?", param, "#", anchor, NULL);
490   } else {
491     return apr_pstrcat(r->pool, src_sv, "?", param, NULL);
492   }
493 }
494
495
496 char *
497 chxj_iconv(request_rec *r, apr_pool_t *pool, const char *src, apr_size_t *len, const char *from, const char *to)
498 {
499   char                *obuf;
500   char                *ibuf;
501   char                *spos;
502   
503   iconv_t             cd;
504   size_t              result;
505   apr_size_t          ilen;
506   apr_size_t          olen;
507
508
509   if ((int)*len < 0) {
510     ERR(r,"REQ[%X] runtime exception: chxj_iconv(): invalid string size.[%d]", TO_ADDR(r),(int)*len);
511     return (char *)apr_pstrdup(pool, "");
512   }
513
514   ilen = *len;
515   ibuf = apr_palloc(pool, ilen+1);
516   if (ibuf == NULL) {
517     ERR(r,"REQ[%X] runtime exception: chxj_iconv(): Out of memory.",TO_ADDR(r));
518     return (char *)src;
519   }
520   memset(ibuf, 0, ilen+1);
521   memcpy(ibuf, src, ilen);
522
523   olen = ilen * 4 + 1;
524   spos = obuf = apr_palloc(pool, olen);
525   if (obuf == NULL) {
526     ERR(r,"REQ[%X] %s:%d runtime exception: chxj_iconv(): Out of memory", TO_ADDR(r),APLOG_MARK);
527     return ibuf;
528   }
529   memset(obuf, 0, olen);
530   cd = iconv_open(to, from);
531   if (cd == (iconv_t)-1) {
532     if (EINVAL == errno) {
533       ERR(r,"REQ[%X] The conversion from %s to %s is not supported by the implementation.", TO_ADDR(r),from, to);
534     }
535     else {
536       ERR(r,"REQ[%X] iconv open failed. from:[%s] to:[%s] errno:[%d]", TO_ADDR(r),from, to, errno);
537     }
538     return ibuf;
539   }
540   while (ilen > 0) {
541     result = iconv(cd, &ibuf, &ilen, &obuf, &olen);
542     if (result == (size_t)(-1)) {
543       if (E2BIG == errno) {
544         ERR(r,"REQ[%X] There is not sufficient room at *outbuf.",TO_ADDR(r));
545       }
546       else if (EILSEQ == errno) {
547         ERR(r,"REQ[%X] An invalid multibyte sequence has been encountered in the input. input:[%s]", TO_ADDR(r),ibuf);
548       }
549       else if (EINVAL == errno) {
550         ERR(r,"REQ[%X] An incomplete multibyte sequence has been encountered in the input. input:[%s]", TO_ADDR(r),ibuf);
551       }
552       break;
553     }
554   }
555   *len = strlen(spos);
556   iconv_close(cd);
557
558   return spos;
559 }
560 /*
561  * vim:ts=2 et
562  */