1 /*
2  * Part of Very Secure FTPd
3  * Licence: GPL v2
4  * Author: Dmitriy Balashov
5  * charconv.c
6  */
7 
8 #include "builddefs.h"
9 #include "charconv.h"
10 #include "tunables.h"
11 #include "session.h"
12 #include "str.h"
13 #include "sysutil.h"
14 
15 //#include "char_maps/utf8.map"
16 #ifdef VSFTP_CHARCONV_SUPPORT_CYRILLIC
17 #include "char_maps/cyrillic.map"
18 #endif
19 #ifdef VSFTP_CHARCONV_SUPPORT_WESTERN
20 #include "char_maps/western.map"
21 #endif
22 #ifdef VSFTP_CHARCONV_SUPPORT_CENTRAL
23 #include "char_maps/central.map"
24 #endif
25 #ifdef VSFTP_CHARCONV_SUPPORT_SOUTERN
26 #include "char_maps/soutern.map"
27 #endif
28 #ifdef VSFTP_CHARCONV_SUPPORT_NORDIC
29 #include "char_maps/nordic.map"
30 #endif
31 
32 /* Tables mapping supported codepage names to runtime variables */
33 static struct available_charsets
34 {
35   const char* p_charset_name;
36   int p_variable;
37 }
38 available_charsets_array[] =
39 {
40   { VSFTP_CP_NONE      , VSFTP_C_NONE      },
41   { VSFTP_CP_UTF_8     , VSFTP_C_UTF8      },
42   { VSFTP_CP_UTF8      , VSFTP_C_UTF8      },
43   // Cyrillic
44 #ifdef VSFTP_CHARCONV_SUPPORT_CYRILLIC
45   { VSFTP_CP_WIN_1251  , VSFTP_C_1251      },
46   { VSFTP_CP_WIN1251   , VSFTP_C_1251      },
47   { VSFTP_CP_CP1251    , VSFTP_C_1251      },
48   { VSFTP_CP_1251      , VSFTP_C_1251      },
49   { VSFTP_CP_KOI8_R    , VSFTP_C_878R      },
50   { VSFTP_CP_KOI8R     , VSFTP_C_878R      },
51   { VSFTP_CP_CP878     , VSFTP_C_878R      },
52   { VSFTP_CP_878       , VSFTP_C_878R      },
53   { VSFTP_CP_CP878R    , VSFTP_C_878R      },
54   { VSFTP_CP_878R      , VSFTP_C_878R      },
55   { VSFTP_CP_KOI8_U    , VSFTP_C_878U      },
56   { VSFTP_CP_KOI8U     , VSFTP_C_878U      },
57   { VSFTP_CP_CP878U    , VSFTP_C_878U      },
58   { VSFTP_CP_878U      , VSFTP_C_878U      },
59   { VSFTP_CP_IBM866    , VSFTP_C_866       },
60   { VSFTP_CP_CP866     , VSFTP_C_866       },
61   { VSFTP_CP_866       , VSFTP_C_866       },
62   { VSFTP_CP_ISO8859_5 , VSFTP_C_ISO5      },
63   { VSFTP_CP_ISO5      , VSFTP_C_ISO5      },
64 #endif
65   // Western European
66 #ifdef VSFTP_CHARCONV_SUPPORT_WESTERN
67   { VSFTP_CP_ISO8859_1 , VSFTP_C_ISO1      },
68   { VSFTP_CP_ISO1      , VSFTP_C_ISO1      },
69   { VSFTP_CP_LATIN1    , VSFTP_C_ISO1      },
70   { VSFTP_CP_ISO8859_15, VSFTP_C_ISO15     },
71   { VSFTP_CP_ISO15     , VSFTP_C_ISO15     },
72   { VSFTP_CP_LATIN9    , VSFTP_C_ISO15     },
73   { VSFTP_CP_WIN_1252  , VSFTP_C_1252      },
74   { VSFTP_CP_WIN1252   , VSFTP_C_1252      },
75   { VSFTP_CP_CP1252    , VSFTP_C_1252      },
76   { VSFTP_CP_1252      , VSFTP_C_1252      },
77 #endif
78   // Central European
79 #ifdef VSFTP_CHARCONV_SUPPORT_CENTRAL
80   { VSFTP_CP_ISO8859_2 , VSFTP_C_ISO2      },
81   { VSFTP_CP_ISO2      , VSFTP_C_ISO2      },
82   { VSFTP_CP_LATIN2    , VSFTP_C_ISO2      },
83   { VSFTP_CP_ISO8859_16, VSFTP_C_ISO16     },
84   { VSFTP_CP_ISO16     , VSFTP_C_ISO16     },
85   { VSFTP_CP_WIN_1250  , VSFTP_C_1250      },
86   { VSFTP_CP_WIN1250   , VSFTP_C_1250      },
87   { VSFTP_CP_CP1250    , VSFTP_C_1250      },
88   { VSFTP_CP_1250      , VSFTP_C_1250      },
89 #endif
90   // Soutern European
91 #ifdef VSFTP_CHARCONV_SUPPORT_SOUTERN
92   { VSFTP_CP_ISO8859_3 , VSFTP_C_ISO3      },
93   { VSFTP_CP_ISO3      , VSFTP_C_ISO3      },
94   { VSFTP_CP_LATIN3    , VSFTP_C_ISO3      },
95 #endif
96   // Nordic
97 #ifdef VSFTP_CHARCONV_SUPPORT_NORDIC
98   { VSFTP_CP_ISO8859_4 , VSFTP_C_ISO4      },
99   { VSFTP_CP_ISO4      , VSFTP_C_ISO4      },
100   { VSFTP_CP_LATIN4    , VSFTP_C_ISO4      },
101   { VSFTP_CP_ISO8859_10, VSFTP_C_ISO10     },
102   { VSFTP_CP_ISO10     , VSFTP_C_ISO10     },
103   { VSFTP_CP_LATIN6    , VSFTP_C_ISO10     },
104 #endif
105   { 0, 0 }
106 };
107 
108 /* Available convertions */
109 static struct available_convertions
110 {
111   int local;
112   int remote;
113   int localCharset;
114   int remoteCharset;
115 }
116 available_convertions_array[] =
117 { // Cyrillic
118 #ifdef VSFTP_CHARCONV_SUPPORT_CYRILLIC
119   { VSFTP_C_UTF8      , VSFTP_C_1251      , VSFTP_CS_UTF8CYR   , VSFTP_CS_1251      },
120   { VSFTP_C_UTF8      , VSFTP_C_878R      , VSFTP_CS_UTF8CYR   , VSFTP_CS_878R      },
121   { VSFTP_C_UTF8      , VSFTP_C_878U      , VSFTP_CS_UTF8CYR   , VSFTP_CS_878U      },
122   { VSFTP_C_UTF8      , VSFTP_C_866       , VSFTP_CS_UTF8CYR   , VSFTP_CS_866       },
123   { VSFTP_C_UTF8      , VSFTP_C_ISO5      , VSFTP_CS_UTF8CYR   , VSFTP_CS_ISO5      },
124   { VSFTP_C_1251      , VSFTP_C_UTF8      , VSFTP_CS_1251      , VSFTP_CS_UTF8CYR   },
125   { VSFTP_C_1251      , VSFTP_C_878R      , VSFTP_CS_1251      , VSFTP_CS_878R      },
126   { VSFTP_C_1251      , VSFTP_C_878U      , VSFTP_CS_1251      , VSFTP_CS_878U      },
127   { VSFTP_C_1251      , VSFTP_C_866       , VSFTP_CS_1251      , VSFTP_CS_866       },
128   { VSFTP_C_1251      , VSFTP_C_ISO5      , VSFTP_CS_1251      , VSFTP_CS_ISO5      },
129   { VSFTP_C_878R      , VSFTP_C_UTF8      , VSFTP_CS_878R      , VSFTP_CS_UTF8CYR   },
130   { VSFTP_C_878R      , VSFTP_C_1251      , VSFTP_CS_878R      , VSFTP_CS_1251      },
131   { VSFTP_C_878R      , VSFTP_C_878U      , VSFTP_CS_878R      , VSFTP_CS_878U      },
132   { VSFTP_C_878R      , VSFTP_C_866       , VSFTP_CS_878R      , VSFTP_CS_866       },
133   { VSFTP_C_878R      , VSFTP_C_ISO5      , VSFTP_CS_878R      , VSFTP_CS_ISO5      },
134   { VSFTP_C_866       , VSFTP_C_UTF8      , VSFTP_CS_866       , VSFTP_CS_UTF8CYR   },
135   { VSFTP_C_866       , VSFTP_C_1251      , VSFTP_CS_866       , VSFTP_CS_1251      },
136   { VSFTP_C_866       , VSFTP_C_878R      , VSFTP_CS_866       , VSFTP_CS_878R      },
137   { VSFTP_C_866       , VSFTP_C_878U      , VSFTP_CS_866       , VSFTP_CS_878U      },
138   { VSFTP_C_866       , VSFTP_C_ISO5      , VSFTP_CS_866       , VSFTP_CS_ISO5      },
139   { VSFTP_C_ISO5      , VSFTP_C_UTF8      , VSFTP_CS_ISO5      , VSFTP_CS_UTF8CYR   },
140   { VSFTP_C_ISO5      , VSFTP_C_1251      , VSFTP_CS_ISO5      , VSFTP_CS_1251      },
141   { VSFTP_C_ISO5      , VSFTP_C_878R      , VSFTP_CS_ISO5      , VSFTP_CS_878R      },
142   { VSFTP_C_ISO5      , VSFTP_C_878U      , VSFTP_CS_ISO5      , VSFTP_CS_878U      },
143   { VSFTP_C_ISO5      , VSFTP_C_866       , VSFTP_CS_ISO5      , VSFTP_CS_866       },
144 #endif
145   // Western European
146 #ifdef VSFTP_CHARCONV_SUPPORT_WESTERN
147   { VSFTP_C_UTF8      , VSFTP_C_ISO1      , VSFTP_CS_UTF8WEST  , VSFTP_CS_ISO1      },
148   { VSFTP_C_UTF8      , VSFTP_C_ISO15     , VSFTP_CS_UTF8WEST  , VSFTP_CS_ISO15     },
149   { VSFTP_C_UTF8      , VSFTP_C_1252      , VSFTP_CS_UTF8WEST  , VSFTP_CS_1252      },
150   { VSFTP_C_ISO1      , VSFTP_C_UTF8      , VSFTP_CS_ISO1      , VSFTP_CS_UTF8WEST  },
151   { VSFTP_C_ISO1      , VSFTP_C_ISO15     , VSFTP_CS_ISO1      , VSFTP_CS_ISO15     },
152   { VSFTP_C_ISO1      , VSFTP_C_1252      , VSFTP_CS_ISO1      , VSFTP_CS_1252      },
153   { VSFTP_C_ISO15     , VSFTP_C_UTF8      , VSFTP_CS_ISO15     , VSFTP_CS_UTF8WEST  },
154   { VSFTP_C_ISO15     , VSFTP_C_ISO1      , VSFTP_CS_ISO15     , VSFTP_CS_ISO1      },
155   { VSFTP_C_ISO15     , VSFTP_C_1252      , VSFTP_CS_ISO15     , VSFTP_CS_1252      },
156   { VSFTP_C_1252      , VSFTP_C_UTF8      , VSFTP_CS_1252      , VSFTP_CS_UTF8WEST  },
157   { VSFTP_C_1252      , VSFTP_C_ISO1      , VSFTP_CS_1252      , VSFTP_CS_ISO1      },
158   { VSFTP_C_1252      , VSFTP_C_ISO15     , VSFTP_CS_1252      , VSFTP_CS_ISO15     },
159 #endif
160   // Central European
161 #ifdef VSFTP_CHARCONV_SUPPORT_CENTRAL
162   { VSFTP_C_UTF8      , VSFTP_C_ISO2      , VSFTP_CS_UTF8CENT  , VSFTP_CS_ISO2      },
163   { VSFTP_C_UTF8      , VSFTP_C_ISO16     , VSFTP_CS_UTF8CENT  , VSFTP_CS_ISO16     },
164   { VSFTP_C_UTF8      , VSFTP_C_1250      , VSFTP_CS_UTF8CENT  , VSFTP_CS_1250      },
165   { VSFTP_C_ISO2      , VSFTP_C_UTF8      , VSFTP_CS_ISO2      , VSFTP_CS_UTF8CENT  },
166   { VSFTP_C_ISO2      , VSFTP_C_ISO16     , VSFTP_CS_ISO2      , VSFTP_CS_ISO16     },
167   { VSFTP_C_ISO2      , VSFTP_C_1250      , VSFTP_CS_ISO2      , VSFTP_CS_1250      },
168   { VSFTP_C_ISO16     , VSFTP_C_UTF8      , VSFTP_CS_ISO16     , VSFTP_CS_UTF8CENT  },
169   { VSFTP_C_ISO16     , VSFTP_C_ISO2      , VSFTP_CS_ISO16     , VSFTP_CS_ISO2      },
170   { VSFTP_C_ISO16     , VSFTP_C_1250      , VSFTP_CS_ISO16     , VSFTP_CS_1250      },
171   { VSFTP_C_1250      , VSFTP_C_UTF8      , VSFTP_CS_1250      , VSFTP_CS_UTF8CENT  },
172   { VSFTP_C_1250      , VSFTP_C_ISO2      , VSFTP_CS_1250      , VSFTP_CS_ISO2      },
173   { VSFTP_C_1250      , VSFTP_C_ISO16     , VSFTP_CS_1250      , VSFTP_CS_ISO16     },
174 #endif
175   // Soutern European
176 #ifdef VSFTP_CHARCONV_SUPPORT_SOUTERN
177   { VSFTP_C_UTF8      , VSFTP_C_ISO3      , VSFTP_CS_UTF8SOUT  , VSFTP_CS_ISO3      },
178   { VSFTP_C_ISO3      , VSFTP_C_UTF8      , VSFTP_CS_ISO3      , VSFTP_CS_UTF8SOUT  },
179 #endif
180   // Nordic
181 #ifdef VSFTP_CHARCONV_SUPPORT_SOUTERN
182   { VSFTP_C_UTF8      , VSFTP_C_ISO4      , VSFTP_CS_UTF8NORD  , VSFTP_CS_ISO4      },
183   { VSFTP_C_UTF8      , VSFTP_C_ISO10     , VSFTP_CS_UTF8NORD  , VSFTP_CS_ISO10     },
184   { VSFTP_C_ISO4      , VSFTP_C_ISO10     , VSFTP_CS_ISO4      , VSFTP_CS_ISO10     },
185   { VSFTP_C_ISO4      , VSFTP_C_UTF8      , VSFTP_CS_ISO4      , VSFTP_CS_UTF8NORD  },
186   { VSFTP_C_ISO10     , VSFTP_C_ISO4      , VSFTP_CS_ISO10     , VSFTP_CS_ISO4      },
187   { VSFTP_C_ISO10     , VSFTP_C_UTF8      , VSFTP_CS_ISO10     , VSFTP_CS_UTF8NORD  },
188 #endif
189   { 0, 0, 0, 0 }
190 };
191 
192 map_ptr map_array[] =
193 {
194   0,
195 #ifdef VSFTP_CHARCONV_SUPPORT_CYRILLIC
196   codepage_utf8cyr_array, codepage_win1251_array, codepage_koi8r_array,
197   codepage_ibm866_array, codepage_iso5_array, codepage_koi8u_array,
198 #else
199   0, 0, 0, 0, 0, 0,
200 #endif
201 
202 #ifdef VSFTP_CHARCONV_SUPPORT_WESTERN
203   codepage_utf8west_array, codepage_iso1_array, codepage_iso15_array,
204   codepage_win1252_array,
205 #else
206   0, 0, 0, 0,
207 #endif
208 
209 #ifdef VSFTP_CHARCONV_SUPPORT_CENTRAL
210   codepage_utf8cent_array, codepage_iso2_array, codepage_iso16_array,
211   codepage_win1250_array,
212 #else
213   0, 0, 0, 0,
214 #endif
215 
216 #ifdef VSFTP_CHARCONV_SUPPORT_SOUTERN
217   codepage_utf8sout_array, codepage_iso3_array,
218 #else
219   0, 0,
220 #endif
221 
222 #ifdef VSFTP_CHARCONV_SUPPORT_NORDIC
223   codepage_utf8nord_array, codepage_iso4_array, codepage_iso10_array,
224 #else
225   0, 0, 0,
226 #endif
227 
228   0
229 };
230 
231 /* Initial table for work with unprintable chars */
232 /*
233 map_ptr init_array[] =
234 {
235   0, 0,
236   //codepage_utf8_array,
237 
238 #ifdef VSFTP_CHARCONV_SUPPORT_CYRILLIC
239   codepage_win1251_array, codepage_koi8r_array, codepage_ibm866_array,
240   codepage_iso5_array, codepage_koi8u_array,
241 #else
242   0, 0, 0, 0, 0,
243 #endif
244 
245 #ifdef VSFTP_CHARCONV_SUPPORT_WESTERN
246   codepage_iso1_array, codepage_iso15_array, codepage_win1252_array,
247 #else
248   0, 0, 0,
249 #endif
250 
251 #ifdef VSFTP_CHARCONV_SUPPORT_CENTRAL
252   codepage_iso2_array, codepage_iso16_array, codepage_win1250_array,
253 #else
254   0, 0, 0,
255 #endif
256 
257 #ifdef VSFTP_CHARCONV_SUPPORT_SOUTERN
258   codepage_iso3_array,
259 #else
260   0,
261 #endif
262 
263 #ifdef VSFTP_CHARCONV_SUPPORT_NORDIC
264   codepage_iso4_array, codepage_iso10_array,
265 #else
266   0, 0,
267 #endif
268 
269   0
270 };
271 */
272 
273 map_ptr localMap = 0, remoteMap = 0;
274 map_ptr localTbl = 0, remoteTbl = 0;
275 
276 void char_convertion(struct mystr* p_str, int direction, char unprintable);
277 void init_tables(map_ptr* map, map_ptr* table, int indexx);
278 static int char_len_utf8(unsigned int s);
279 static unsigned int bsearch_index(map_ptr map, unsigned int low, unsigned int high, unsigned int char_code);
280 
vsf_charconv_charset_name(int code)281 const char* vsf_charconv_charset_name(int code)
282 {
283   int i = 0;
284   while (available_charsets_array[i].p_charset_name && available_charsets_array[i].p_variable != code) i++;
285   return available_charsets_array[i].p_charset_name;
286 }
287 
vsf_charconv_codepage(const char * p_str)288 int vsf_charconv_codepage(const char* p_str)
289 {
290   const struct available_charsets* charsets = available_charsets_array;
291 
292   while (charsets->p_charset_name != 0)
293   {
294     if (str_equal_str(charsets->p_charset_name, p_str))
295     {
296       return charsets->p_variable;
297     }
298     charsets++;
299   }
300 
301   return 0;
302 }
303 
vsf_charconv_avail_convertion(int localCode,int remoteCode)304 int vsf_charconv_avail_convertion(int localCode, int remoteCode)
305 {
306   const struct available_convertions* aconv = available_convertions_array;
307 
308   while (aconv->local != 0)
309   {
310     if (localCode == aconv->local && remoteCode == aconv->remote)
311     {
312       init_tables(&localMap, &localTbl, aconv->localCharset);
313       init_tables(&remoteMap, &remoteTbl, aconv->remoteCharset);
314       return 1;
315     }
316     aconv++;
317   }
318 
319   return 0;
320 }
321 
vsf_charconv_init_local_codepage(int localCode)322 void vsf_charconv_init_local_codepage(int localCode)
323 {
324   if (!localCode || localCode == VSFTP_C_UTF8)
325   {
326     return;
327   }
328 
329   init_tables(&localMap, &localTbl, localCode);
330 
331 /*
332   localTbl = init_array[localCode];
333   localMap =  vsf_sysutil_malloc((localTbl[0].order + 1) * sizeof(_codepage_map));
334   localMap[0].char_code = localTbl[0].char_code;
335   localMap[0].order     = localTbl[0].order;
336 
337   int indexx = 1;
338   while (localTbl[indexx].char_code)
339   {
340     if (localTbl[indexx].order)
341     {
342       localMap[localTbl[indexx].order].char_code = localTbl[indexx].char_code;
343       localMap[localTbl[indexx].order].order     = indexx;
344     }
345     indexx++;
346   }
347 */
348 
349   return;
350 }
351 
vsf_charconv_convert(struct vsf_session * p_sess,struct mystr * p_str,int direction)352 void vsf_charconv_convert(struct vsf_session* p_sess, struct mystr* p_str, int direction)
353 {
354   if (!p_sess->enable_convertion || !p_sess->remote_charset || !tunable_local_codepage) return;
355 
356   char_convertion(p_str, direction, '?');
357 }
358 
vsf_charconv_replace_unprintable(struct mystr * p_str,char new_char)359 void vsf_charconv_replace_unprintable(struct mystr* p_str, char new_char)
360 {
361   if (localMap) char_convertion(p_str, VSFTP_CONVDIRECT_UNPRINTABLE, new_char);
362 }
363 
init_tables(map_ptr * map,map_ptr * table,int indexx)364 void init_tables(map_ptr* map, map_ptr* table, int indexx)
365 {
366   *table = map_array[indexx];
367 
368   if (*map) vsf_sysutil_free(*map);
369 
370   *map = vsf_sysutil_malloc(((*table)[0].order + 1) * sizeof(_codepage_map));
371   (*map)[0].char_code = (*table)[0].char_code;
372   (*map)[0].order     = (*table)[0].order;
373 
374   indexx = 1;
375   while ((*table)[indexx].char_code)
376   {
377     if ((*table)[indexx].order)
378     {
379       (*map)[(*table)[indexx].order].char_code = (*table)[indexx].char_code;
380       (*map)[(*table)[indexx].order].order     = indexx;
381     }
382     indexx++;
383   }
384 
385   return;
386 }
387 
char_len_utf8(unsigned int s)388 static int char_len_utf8(unsigned int s)
389 {
390   int len = 1;
391        if ((s & 0x80) == 0x00) len = 1;
392   else if ((s & 0xe0) == 0xc0) len = 2;
393   else if ((s & 0xf0) == 0xe0) len = 3;
394   else if ((s & 0xf8) == 0xf0) len = 4;
395 //  else if ((s & 0xfc) == 0xf8) len = 5;
396 //  else if ((s & 0xfe) == 0xce) len = 6;
397   return (len);
398 }
399 
bsearch_index(map_ptr map,unsigned int low,unsigned int high,unsigned int char_code)400 static unsigned int bsearch_index(map_ptr map, unsigned int low, unsigned int high, unsigned int char_code)
401 {
402   unsigned int m, l = low, r = high;
403 
404   m = (l + r) >> 1;
405   while ((m != 0) && (map[m].char_code != char_code))
406   {
407     if (map[m].char_code < char_code) l = m + 1;
408     else
409     if (map[m].char_code > char_code) r = m - 1;
410     if (l > r)
411       return 0;
412     else
413       m = (l + r) >> 1;
414   }
415 
416   if (m) m = map[m].order;
417   return m;
418 }
419 
char_convertion(struct mystr * p_str,int direction,char unprintable)420 void char_convertion(struct mystr* p_str, int direction, char unprintable)
421 {
422   const char* srcbuf;
423   unsigned int srclen;
424   char* dstbuf;
425   map_ptr src, dst;
426   unsigned int sl;
427   unsigned int srcpos = 0, dstpos = 0;
428   unsigned int char_code = 0;
429 
430   srclen = str_getlen(p_str);     // Len of source string
431   srcbuf = str_getbuf(p_str);
432 
433   if (direction == VSFTP_CONVDIRECT_FORWARD)
434   {
435     src = localMap;
436     dst = remoteTbl;
437   }
438   else
439   if (direction == VSFTP_CONVDIRECT_BACKWARD)
440   {
441     src = remoteMap;
442     dst = localTbl;
443   }
444   else
445   {
446     src = localMap;
447     dst = localTbl;
448   }
449 
450   if (!src || !dst)
451   {
452     return;
453   }
454 
455   dstbuf = vsf_sysutil_malloc(srclen * dst[0].char_code + dst[0].char_code);
456 
457   while (srcpos < srclen)
458   {
459     char_code = (unsigned char)srcbuf [srcpos++];
460     if (src[0].char_code > 1)
461     {
462       sl = char_len_utf8(char_code);
463       while (sl-- > 1)
464       {
465         char_code = (char_code << 8) | (unsigned char)srcbuf [srcpos++];
466       }
467     }
468 
469     if (char_code > 127)
470     {
471       sl = bsearch_index(src, 1, src[0].order, char_code);
472       char_code = 0;
473       if (sl) char_code = dst[sl].char_code;
474     }
475 
476     if (char_code == 13 || char_code == 10)
477     {
478       char_code = 0;
479     }
480     else
481     if (char_code < 32 && char_code != 9)
482     {
483       char_code = (unsigned int)unprintable;
484     }
485 
486     if (char_code > 0 || direction != VSFTP_CONVDIRECT_UNPRINTABLE)
487     {
488       if (char_code & 0xff000000) dstbuf [dstpos++] = (char)((char_code >> 24) & 0xff);
489       if (char_code & 0x00ff0000) dstbuf [dstpos++] = (char)((char_code >> 16) & 0xff);
490       if (char_code & 0x0000ff00) dstbuf [dstpos++] = (char)((char_code >>  8) & 0xff);
491       if (char_code & 0x000000ff) dstbuf [dstpos++] = (char)((char_code      ) & 0xff);
492     }
493   }
494 
495   dstbuf[dstpos] = '\0';
496 
497   str_alloc_text(p_str, dstbuf);
498   vsf_sysutil_free(dstbuf);
499 }
500