OSDN Git Service

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