OSDN Git Service

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