TShopping

 找回密碼
 註冊
搜索
查看: 1654|回復: 3

[討論] FTP Server/Client 在 UTF-8 下的解決方案

[複製鏈接]
發表於 2008-11-28 16:50:56 | 顯示全部樓層 |閱讀模式
 
Push to Facebook Push to Plurk Push to Twitter 
前幾天 "不小心" 把自己家裡的主機內的檔案名, 都改成 UTF-8 來處理, 在 samba 上頭是可以很正常的處理, 不過.... 發現在使用 ftp 時, 問題就不小了.

如果是支援 UTF-8 的 ftp client, 是可以正常運作, 如 gftp, smartftp....

不過, 在用了一天的 smartftp 之後, 實在發現和之前用 filezilla 的習慣大不相同, 用起來實在不習慣.

原本打算動手改 filezilla 的程式碼.... 只是, 在 windows 下頭, 似乎沒有 iconv 可以用... 又懶的去找 win32 可以用的函式... 所以, 就直接改 server 上的 vsftpd 來配合非 UTF-8 的 ftp client.

修改的構想主要來自 proftpd 的 iconv patch:
http://home.h01.itscom.net/para/software/misc/proftpd-iconv/index-e.html

我是修改 Debian 的 vsftpd 2.0.1 的版本.
patch 如下:



  1. diff -Nur vsftpd-2.0.1/features.c vsftpd-2.0.1.patched/features.c
  2. --- vsftpd-2.0.1/features.c 2004-07-02 19:22:45.000000000 +0800
  3. +++ vsftpd-2.0.1.patched/features.c 2005-02-22 12:21:56.000000000 +0800
  4. @@ -25,6 +25,7 @@
  5.    vsf_cmdio_write_raw(p_sess, " EPSV\r\n");
  6.    vsf_cmdio_write_raw(p_sess, " MDTM\r\n");
  7.    vsf_cmdio_write_raw(p_sess, " PASV\r\n");
  8. +  vsf_cmdio_write_raw(p_sess, " ICNV\r\n");
  9.    if (tunable_ssl_enable)
  10.    {
  11.      vsf_cmdio_write_raw(p_sess, " PBSZ\r\n");
  12. diff -Nur vsftpd-2.0.1/ftpcmdio.c vsftpd-2.0.1.patched/ftpcmdio.c
  13. --- vsftpd-2.0.1/ftpcmdio.c 2004-07-02 19:23:02.000000000 +0800
  14. +++ vsftpd-2.0.1.patched/ftpcmdio.c 2005-02-22 11:48:44.000000000 +0800
  15. @@ -7,6 +7,9 @@
  16.   * Routines applicable to reading and writing the FTP command stream.
  17.   */

  18. +#include <stdlib.h>
  19. +#include <string.h>
  20. +#include "utility.h"
  21. #include "ftpcmdio.h"
  22. #include "ftpcodes.h"
  23. #include "str.h"
  24. @@ -170,6 +173,16 @@
  25.    control_getline(p_cmd_str, p_sess);
  26.    str_split_char(p_cmd_str, p_arg_str, ' ');
  27.    str_upper(p_cmd_str);
  28. +  if (!str_isempty(p_arg_str)) {
  29. +      char *tmp_str;
  30. +
  31. +      tmp_str = remote2local(str_getbuf(p_arg_str));
  32. +      if (tmp_str != NULL) {
  33. +  str_empty(p_arg_str);
  34. +          str_append_text(p_arg_str, tmp_str);
  35. +  vsf_sysutil_free(tmp_str);
  36. +      }
  37. +  }
  38.    if (tunable_log_ftp_protocol)
  39.    {
  40.      static struct mystr s_log_str;
  41. diff -Nur vsftpd-2.0.1/ls.c vsftpd-2.0.1.patched/ls.c
  42. --- vsftpd-2.0.1/ls.c 2004-07-02 19:23:34.000000000 +0800
  43. +++ vsftpd-2.0.1.patched/ls.c 2005-02-22 11:48:56.000000000 +0800
  44. @@ -7,6 +7,9 @@
  45.   * Would you believe, code to handle directory listing.
  46.   */

  47. +#include <stdlib.h>
  48. +#include <string.h>
  49. +#include "utility.h"
  50. #include "ls.h"
  51. #include "access.h"
  52. #include "str.h"
  53. @@ -363,6 +366,7 @@
  54.                 const struct vsf_sysutil_statbuf* p_stat)
  55. {
  56.    static struct mystr s_tmp_str;
  57. +  char *tmp_filename;
  58.    filesize_t size = vsf_sysutil_statbuf_get_size(p_stat);
  59.    /* Permissions */
  60.    str_alloc_text(p_str, vsf_sysutil_statbuf_get_perms(p_stat));
  61. @@ -432,7 +436,13 @@
  62.                                                        tunable_use_localtime));
  63.    str_append_char(p_str, ' ');
  64.    /* Filename */
  65. -  str_append_str(p_str, p_filename_str);
  66. +  tmp_filename = local2remote(str_getbuf(p_filename_str));
  67. +  if (tmp_filename == NULL)
  68. +    str_append_str(p_str, p_filename_str);
  69. +  else {
  70. +    str_append_text(p_str, tmp_filename);
  71. +    vsf_sysutil_free(tmp_filename);
  72. +  }
  73.    str_append_text(p_str, "\r\n");
  74. }

  75. diff -Nur vsftpd-2.0.1/parseconf.c vsftpd-2.0.1.patched/parseconf.c
  76. --- vsftpd-2.0.1/parseconf.c 2004-07-02 19:23:56.000000000 +0800
  77. +++ vsftpd-2.0.1.patched/parseconf.c 2005-02-22 11:49:12.000000000 +0800
  78. @@ -94,6 +94,7 @@
  79.    { "ssl_sslv3", &tunable_sslv3 },
  80.    { "ssl_tlsv1", &tunable_tlsv1 },
  81.    { "tilde_user_enable", &tunable_tilde_user_enable },
  82. +  { "enable_iconv", &tunable_enable_iconv },
  83.    { 0, 0 }
  84. };

  85. @@ -158,6 +159,8 @@
  86.    { "rsa_cert_file", &tunable_rsa_cert_file },
  87.    { "dsa_cert_file", &tunable_dsa_cert_file },
  88.    { "ssl_ciphers", &tunable_ssl_ciphers },
  89. +  { "local_charset", &tunable_local_charset },
  90. +  { "remote_charset", &tunable_remote_charset },
  91.    { 0, 0 }
  92. };

  93. diff -Nur vsftpd-2.0.1/postlogin.c vsftpd-2.0.1.patched/postlogin.c
  94. --- vsftpd-2.0.1/postlogin.c 2004-07-02 19:24:01.000000000 +0800
  95. +++ vsftpd-2.0.1.patched/postlogin.c 2005-02-22 15:54:14.000000000 +0800
  96. @@ -5,6 +5,9 @@
  97.   * postlogin.c
  98.   */

  99. +#include <stdlib.h>
  100. +#include <string.h>
  101. +#include "utility.h"
  102. #include "postlogin.h"
  103. #include "session.h"
  104. #include "oneprocess.h"
  105. @@ -157,6 +160,29 @@
  106.      {
  107.        handle_pwd(p_sess);
  108.      }
  109. +    else if (str_equal_text(&p_sess->ftp_cmd_str, "ICNV"))
  110. +    {
  111. +      str_upper(&p_sess->ftp_arg_str);
  112. +      if (str_equal_text(&p_sess->ftp_arg_str, "ON"))
  113. +      {
  114. +        tunable_enable_iconv = 1;
  115. +        vsf_cmdio_write(p_sess, FTP_CWDOK, "enable iconv().");
  116. +      }
  117. +      else if (str_equal_text(&p_sess->ftp_arg_str, "OFF"))
  118. +      {
  119. +        tunable_enable_iconv = 0;
  120. +        vsf_cmdio_write(p_sess, FTP_CWDOK, "disable iconv().");
  121. +      }
  122. +      else {
  123. +        if (tunable_enable_iconv) {
  124. +          vsf_cmdio_write(p_sess, FTP_CWDOK, "iconv() enabled.");
  125. +        }
  126. +        else
  127. +        {
  128. +          vsf_cmdio_write(p_sess, FTP_CWDOK, "iconv() disabled.");
  129. +        }
  130. +      }
  131. +    }
  132.      else if (str_equal_text(&p_sess->ftp_cmd_str, "CWD") ||
  133.               str_equal_text(&p_sess->ftp_cmd_str, "XCWD"))
  134.      {
  135. @@ -404,6 +430,7 @@
  136. static void
  137. handle_pwd(struct vsf_session* p_sess)
  138. {
  139. +  char *tmp_str;
  140.    static struct mystr s_cwd_buf_mangle_str;
  141.    static struct mystr s_pwd_res_str;
  142.    str_getcwd(&s_cwd_buf_mangle_str);
  143. @@ -411,7 +438,13 @@
  144.    str_replace_text(&s_cwd_buf_mangle_str, "\"", "\"\"");
  145.    /* Enclose pathname in quotes */
  146.    str_alloc_text(&s_pwd_res_str, "\"");
  147. -  str_append_str(&s_pwd_res_str, &s_cwd_buf_mangle_str);
  148. +  tmp_str = local2remote(str_getbuf(&s_cwd_buf_mangle_str));
  149. +  if (tmp_str == NULL)
  150. +    str_append_str(&s_pwd_res_str, &s_cwd_buf_mangle_str);
  151. +  else {
  152. +    str_append_text(&s_pwd_res_str, tmp_str);
  153. +    vsf_sysutil_free(tmp_str);

  154. +  }
  155.    str_append_text(&s_pwd_res_str, "\"");
  156.    vsf_cmdio_write_str(p_sess, FTP_PWDOK, &s_pwd_res_str);
  157. }
  158. @@ -435,6 +468,24 @@
  159.    }
  160.    else
  161.    {
  162. +    if (tunable_enable_iconv) {
  163. +      char *tmp_str;
  164. +
  165. +      tmp_str = local2remote(str_getbuf(&p_sess->ftp_arg_str));
  166. +      if (tmp_str != NULL) {
  167. +        str_empty(&p_sess->ftp_arg_str);
  168. +        str_append_text(&p_sess->ftp_arg_str, tmp_str);
  169. +        vsf_sysutil_free(tmp_str);
  170. +        retval = str_chdir(&p_sess->ftp_arg_str);
  171. +        if (retval == 0)
  172. +        {
  173. +          /* Handle any messages */
  174. +          vsf_banner_dir_changed(p_sess, FTP_CWDOK);
  175. +          vsf_cmdio_write(p_sess, FTP_CWDOK, "Directory successfully changed.");
  176. +  return;
  177. +        }
  178. +      }
  179. +    }
  180.      vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to change directory.");
  181.    }
  182. }
  183. @@ -640,8 +691,29 @@
  184.    opened_file = str_open(&p_sess->ftp_arg_str, kVSFSysStrOpenReadOnly);
  185.    if (vsf_sysutil_retval_is_error(opened_file))
  186.    {
  187. -    vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to open file.");
  188. -    return;
  189. +    if (tunable_enable_iconv) {
  190. +      char *tmp_str;
  191. +
  192. +      tmp_str = local2remote(str_getbuf(&p_sess->ftp_arg_str));
  193. +      if (tmp_str != NULL) {
  194. + str_empty(&p_sess->ftp_arg_str);
  195. + str_append_text(&p_sess->ftp_arg_str, tmp_str);
  196. + vsf_sysutil_free(tmp_str);
  197. + opened_file = str_open(&p_sess->ftp_arg_str, kVSFSysStrOpenReadOnly);
  198. + if (vsf_sysutil_retval_is_error(opened_file)) {
  199. +          vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to open file.");
  200. +          return;
  201. + }
  202. +      }
  203. +      else {
  204. +        vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to open file.");
  205. +        return;
  206. +      }
  207. +    }
  208. +    else {
  209. +      vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to open file.");
  210. +      return;
  211. +    }
  212.    }
  213.    vsf_sysutil_fstat(opened_file, &s_p_statbuf);
  214.    /* No games please */
  215. @@ -1623,7 +1695,7 @@
  216.    vsf_cmdio_write_raw(p_sess,
  217. " RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD\r\n");
  218.    vsf_cmdio_write_raw(p_sess,
  219. -" XPWD XRMD\r\n");
  220. +" XPWD XRMD ICNV\r\n");
  221.    vsf_cmdio_write(p_sess, FTP_HELP, "Help OK.");
  222. }

  223. diff -Nur vsftpd-2.0.1/tunables.c vsftpd-2.0.1.patched/tunables.c
  224. --- vsftpd-2.0.1/tunables.c 2004-07-02 19:26:17.000000000 +0800
  225. +++ vsftpd-2.0.1.patched/tunables.c 2005-02-22 12:17:52.000000000 +0800
  226. @@ -66,6 +66,7 @@
  227. int tunable_sslv3 = 0;
  228. int tunable_tlsv1 = 1;
  229. int tunable_tilde_user_enable = 0;
  230. +int tunable_enable_iconv = 0;

  231. unsigned int tunable_accept_timeout = 60;
  232. unsigned int tunable_connect_timeout = 60;
  233. @@ -115,4 +116,7 @@
  234. const char* tunable_rsa_cert_file = "/usr/share/ssl/certs/vsftpd.pem";
  235. const char* tunable_dsa_cert_file = 0;
  236. const char* tunable_ssl_ciphers = "DES-CBC3-SHA";
  237. +const char* tunable_local_charset = "UTF-8";
  238. +const char* tunable_remote_charset = "BIG5";
  239. +

  240. diff -Nur vsftpd-2.0.1/tunables.h vsftpd-2.0.1.patched/tunables.h
  241. --- vsftpd-2.0.1/tunables.h 2004-06-29 07:08:31.000000000 +0800
  242. +++ vsftpd-2.0.1.patched/tunables.h 2005-02-22 11:50:27.000000000 +0800
  243. @@ -62,6 +62,7 @@
  244. extern int tunable_sslv3;                     /* Allow SSLv3 */
  245. extern int tunable_tlsv1;                     /* Allow TLSv1 */
  246. extern int tunable_tilde_user_enable;         /* Support e.g. ~chris */
  247. +extern int tunable_enable_iconv;      /* Convert filename use iconv */

  248. /* Integer/numeric defines */
  249. extern unsigned int tunable_accept_timeout;
  250. @@ -110,6 +111,8 @@
  251. extern const char* tunable_rsa_cert_file;
  252. extern const char* tunable_dsa_cert_file;
  253. extern const char* tunable_ssl_ciphers;
  254. +extern const char* tunable_local_charset;
  255. +extern const char* tunable_remote_charset;

  256. #endif /* VSF_TUNABLES_H */

  257. diff -Nur vsftpd-2.0.1/utility.c vsftpd-2.0.1.patched/utility.c
  258. --- vsftpd-2.0.1/utility.c 2004-07-02 19:26:30.000000000 +0800
  259. +++ vsftpd-2.0.1.patched/utility.c 2005-02-22 14:05:46.000000000 +0800
  260. @@ -5,6 +5,13 @@

  261.   * utility.c
  262.   */

  263. +#include <stdarg.h>
  264. +#include <iconv.h>
  265. +#include <stdlib.h>
  266. +#include <stdio.h>
  267. +#include <unistd.h>
  268. +#include <string.h>
  269. +#include "tunables.h"
  270. #include "utility.h"
  271. #include "sysutil.h"
  272. #include "str.h"
  273. @@ -50,3 +57,71 @@
  274.    vsf_sysutil_exit(0);
  275. }

  276. +char *
  277. +local2remote(const char *buf)
  278. +{
  279. +  char *in_ptr;
  280. +  char *out_ptr;
  281. +  size_t inbytesleft, outbytesleft;
  282. +  char *p;
  283. +  iconv_t ic;
  284. +
  285. +  if (tunable_enable_iconv == 0) return NULL;
  286. +  ic = iconv_open(tunable_remote_charset, tunable_local_charset);
  287. +  if (ic == (iconv_t)(-1)) return NULL;
  288. +  iconv(ic, NULL, NULL, NULL, NULL);
  289. +
  290. +  inbytesleft = strlen(buf);
  291. +  outbytesleft = inbytesleft * 6;
  292. +  p = vsf_sysutil_malloc(outbytesleft+1);
  293. +
  294. +  in_ptr = buf;
  295. +  out_ptr = p;
  296. +  while (inbytesleft) {
  297. +    if (iconv(ic, &in_ptr, &inbytesleft, &out_ptr, &outbytesleft) == (size_t)(-1)) {
  298. +      iconv_close(ic);
  299. +      vsf_sysutil_free(p);
  300. +      return NULL;
  301. +    }
  302. +  }
  303. +
  304. +  *out_ptr = 0;
  305. +
  306. +  iconv_close(ic);
  307. +  return p;
  308. +}
  309. +
  310. +char *
  311. +remote2local(const char *buf)
  312. +{
  313. +  char *in_ptr;
  314. +  char *out_ptr;
  315. +  size_t inbytesleft, outbytesleft;
  316. +  char *p;
  317. +  iconv_t ic;
  318. +
  319. +  if (tunable_enable_iconv == 0) return NULL;
  320. +  ic = iconv_open(tunable_local_charset, tunable_remote_charset);
  321. +  if (ic == (iconv_t)(-1)) return NULL;
  322. +  iconv(ic, NULL, NULL, NULL, NULL);
  323. +
  324. +  inbytesleft = strlen(buf);
  325. +  outbytesleft = inbytesleft * 6;
  326. +  p = vsf_sysutil_malloc(outbytesleft+1);
  327. +
  328. +  in_ptr = buf;
  329. +  out_ptr = p;
  330. +  while (inbytesleft) {
  331. +    if (iconv(ic, &in_ptr, &inbytesleft, &out_ptr, &outbytesleft) == (size_t)(-1)) {
  332. +      iconv_close(ic);
  333. +      vsf_sysutil_free(p);
  334. +      return NULL;
  335. +    }
  336. +  }
  337. +
  338. +  *out_ptr = 0;
  339. +
  340. +  iconv_close(ic);
  341. +  return p;
  342. +}
  343. +
  344. diff -Nur vsftpd-2.0.1/utility.h vsftpd-2.0.1.patched/utility.h
  345. --- vsftpd-2.0.1/utility.h 2004-04-16 06:46:29.000000000 +0800
  346. +++ vsftpd-2.0.1.patched/utility.h 2005-02-22 11:47:51.000000000 +0800
  347. @@ -40,5 +40,7 @@
  348.   */
  349. void vsf_exit(const char* p_text);

  350. +char *local2remote(const char *buf);
  351. +char *remote2local(const char *buf);
  352. #endif

複製代碼



可以到這兒抓
http://www.teatime.com.tw/~tommy/linux/vsftpd_iconv.patch

如果是使用 Debian Sarge, 也可以直接用這個:
http://www.teatime.com.tw/~tommy/debian/vsftpd_2.0.1-11_i386.deb

在使用這個 patch 之後, 你可以在 vsftpd.conf 中指定下面三個參數

  1. enable_iconv=YES
  2. local_charset=UTF-8
  3. remote_charset=BIG5
複製代碼




local_charset 是主機檔案名稱使用的編碼. 內定為 UTF-8.
remote_charset 是 ftp client 的編碼. 內定為 BIG5.
enable_iconv 預設是關閉的, 也就是所有的動作應該都與沒加上這個 patch 時一樣. 如果設為 true/yes 時, 就會使用 iconv 來轉換檔名.

動作如下:
1. 所有的 client 送來的指令, 參數, 檔案或路徑名稱, 會使用 iconv 由 remote_charset 轉成 local_charset. 所以, 實際存取是使用 local_charset 的編碼.
2. 在 ls 指令時, 要送出檔名之前, 會用 iconv 由 local_charset 轉成 remote_charset 再送出.
3. 如果轉碼的動作是失敗的, 就不做轉碼的動作.
4. 加上一個 icnv on/off 指令 (非 ftp 標準), 可以讓 client 決定是否打開這個功能.
5. 主機上頭的檔案存取, 如果在轉成 local_charset 時, 存取失敗的話, 就假設該檔案或路徑為 remote_charset 的編碼格式, 再試一次.

以上述主機使用 UTF-8, client 使用 Big5 的情形下, 不管主機上頭的檔案路徑名稱是用 UTF-8 或 Big5, 都會轉成 Big5 送到 client 上頭, 所以當我們使用像 filezilla 之類不支援 UTF-8 的程式時, 所收到的都會是 Big5 編碼的資料.
如果我們使用支援 UTF-8 的 client 時, 如 gftp, 會發現, 反而原本在 UTF-8 的檔名, 在轉成 Big5 之後, 反而看不到正確的名稱. 此時, 可以使用上述的 icnv off 指令 (gftp 使用 site icnv off, 其他程式可能用 quote icnv off) 把轉碼的動作關閉, 就可以正常看到 UTF-8 編碼的檔案.

而在檔案抓取或更換目錄時, 會先用 UTF-8 試一次, 如果失敗, 再用 Big5 試一次. 以確定主機上頭的檔名無論是 Big5 或 UTF-8 都可以被存取.
而在檔案儲存或建立目錄時, 主機上只會使用 UTF-8 來編碼.

 

臉書網友討論
發表於 2011-9-17 00:43:37 | 顯示全部樓層
樓主福如東海,萬壽無疆!  

版主招募中

您需要登錄後才可以回帖 登錄 | 註冊 |

本版積分規則



Archiver|手機版|小黑屋|免責聲明|TShopping

GMT+8, 2016-12-10 05:28 , Processed in 0.056257 second(s), 19 queries .

本論壇言論純屬發表者個人意見,與 TShopping綜合論壇 立場無關 如有意見侵犯了您的權益 請寫信聯絡我們。

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回復 返回頂部 返回列表