1 /*
2  * ProFTPD - FTP server daemon
3  * Copyright (c) 2006-2016 The ProFTPD Project team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18  *
19  * As a special exemption, The ProFTPD Project team and other respective
20  * copyright holders give permission to link this program with OpenSSL, and
21  * distribute the resulting executable, without including the source code for
22  * OpenSSL in the source distribution.
23  */
24 
25 /* UTF8 encoding/decoding */
26 
27 #include "conf.h"
28 
29 #ifdef PR_USE_NLS
30 
31 #ifdef HAVE_ICONV_H
32 # include <iconv.h>
33 #endif
34 
35 #ifdef HAVE_LANGINFO_H
36 # include <langinfo.h>
37 #endif
38 
39 #ifdef HAVE_ICONV_H
40 static iconv_t decode_conv = (iconv_t) -1;
41 static iconv_t encode_conv = (iconv_t) -1;
42 
utf8_convert(iconv_t conv,char * inbuf,size_t * inbuflen,char * outbuf,size_t * outbuflen)43 static int utf8_convert(iconv_t conv, char *inbuf, size_t *inbuflen,
44     char *outbuf, size_t *outbuflen) {
45 # ifdef HAVE_ICONV
46   char *start = inbuf;
47 
48   while (inbuflen > 0) {
49     size_t nconv = iconv(conv, &inbuf, inbuflen, &outbuf, outbuflen);
50 
51     if (nconv == (size_t) -1) {
52       if (errno == EINVAL) {
53         memmove(start, inbuf, *inbuflen);
54         continue;
55 
56       } else
57         return -1;
58     }
59 
60     break;
61   }
62   return 0;
63 # else
64   errno = ENOSYS;
65   return -1;
66 # endif /* HAVE_ICONV */
67 }
68 #endif /* !HAVE_ICONV_H */
69 
utf8_free(void)70 int utf8_free(void) {
71 # ifdef HAVE_ICONV
72   int res;
73 
74   /* Close the iconv handles. */
75   res = iconv_close(encode_conv);
76   if (res < 0)
77     return -1;
78 
79   res = iconv_close(decode_conv);
80   if (res < 0)
81     return -1;
82 
83   return 0;
84 # else
85   errno = ENOSYS;
86   return -1;
87 # endif
88 }
89 
utf8_init(void)90 int utf8_init(void) {
91   const char *local_charset;
92 
93 #ifdef HAVE_NL_LANGINFO
94   /* Look up the current charset.  If there's a problem, default to
95    * UCS-2.
96    */
97   local_charset = nl_langinfo(CODESET);
98   if (!local_charset) {
99     local_charset = "C";
100     pr_trace_msg("utf8", 1,
101       "unable to determine locale, defaulting to 'C' for UTF8 conversion");
102 
103   } else {
104     pr_trace_msg("utf8", 1, "converting UTF8 to local character set '%s'",
105       local_charset);
106   }
107 #else
108   local_charset = "C";
109   pr_trace_msg("utf8", 1,
110     "nl_langinfo(3) not supported, defaulting to using 'C' for UTF8 "
111     "conversion");
112 #endif /* HAVE_NL_LANGINFO */
113 
114 # ifdef HAVE_ICONV
115   /* Get the iconv handles. */
116   encode_conv = iconv_open("UTF-8", local_charset);
117   if (encode_conv == (iconv_t) -1)
118     return -1;
119 
120   decode_conv = iconv_open(local_charset, "UTF-8");
121   if (decode_conv == (iconv_t) -1)
122     return -1;
123 
124   return 0;
125 # else
126   errno = ENOSYS;
127   return -1;
128 # endif /* HAVE_ICONV */
129 }
130 
pr_utf8_decode(pool * p,const char * in,size_t inlen,size_t * outlen)131 char *pr_utf8_decode(pool *p, const char *in, size_t inlen, size_t *outlen) {
132 #ifdef HAVE_ICONV_H
133   size_t inbuflen, outbuflen;
134   char *inbuf, outbuf[PR_TUNABLE_PATH_MAX*2], *res = NULL;
135 
136   if (!p || !in || !outlen) {
137     errno = EINVAL;
138     return NULL;
139   }
140 
141   if (decode_conv == (iconv_t) -1) {
142     errno = EPERM;
143     return NULL;
144   }
145 
146   inbuf = pcalloc(p, inlen);
147   memcpy(inbuf, in, inlen);
148   inbuflen = inlen;
149 
150   outbuflen = sizeof(outbuf);
151 
152   if (utf8_convert(decode_conv, inbuf, &inbuflen, outbuf, &outbuflen) < 0)
153     return NULL;
154 
155   *outlen = sizeof(outbuf) - outbuflen;
156   res = pcalloc(p, *outlen);
157   memcpy(res, outbuf, *outlen);
158 
159   return res;
160 #else
161   pr_trace_msg("utf8", 1, "missing iconv support, no UTF8 decoding possible");
162   return pstrdup(p, in);
163 #endif /* !HAVE_ICONV_H */
164 }
165 
pr_utf8_encode(pool * p,const char * in,size_t inlen,size_t * outlen)166 char *pr_utf8_encode(pool *p, const char *in, size_t inlen, size_t *outlen) {
167 #ifdef HAVE_ICONV_H
168   size_t inbuflen, outbuflen;
169   char *inbuf, outbuf[PR_TUNABLE_PATH_MAX*2], *res;
170 
171   if (!p || !in || !outlen) {
172     errno = EINVAL;
173     return NULL;
174   }
175 
176   if (encode_conv == (iconv_t) -1) {
177     errno = EPERM;
178     return NULL;
179   }
180 
181   inbuf = pcalloc(p, inlen);
182   memcpy(inbuf, in, inlen);
183   inbuflen = inlen;
184 
185   outbuflen = sizeof(outbuf);
186 
187   if (utf8_convert(encode_conv, inbuf, &inbuflen, outbuf, &outbuflen) < 0)
188     return NULL;
189 
190   *outlen = sizeof(outbuf) - outbuflen;
191   res = pcalloc(p, *outlen);
192   memcpy(res, outbuf, *outlen);
193 
194   return res;
195 #else
196   pr_trace_msg("utf8", 1, "missing iconv support, no UTF8 encoding possible");
197   return pstrdup(p, in);
198 #endif /* !HAVE_ICONV_H */
199 }
200 
201 #endif /* PR_USE_NLS */
202