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