OSDN Git Service

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