diff -Nur vsftpd-2.0.1/features.c vsftpd-2.0.1.patched/features.c
--- vsftpd-2.0.1/features.c	2004-07-02 19:22:45.000000000 +0800
+++ vsftpd-2.0.1.patched/features.c	2005-03-03 16:20:42.000000000 +0800
@@ -11,6 +11,7 @@
 #include "ftpcodes.h"
 #include "ftpcmdio.h"
 #include "tunables.h"
+#include "str.h"
 
 void
 handle_feat(struct vsf_session* p_sess)
@@ -25,6 +26,15 @@
   vsf_cmdio_write_raw(p_sess, " EPSV\r\n");
   vsf_cmdio_write_raw(p_sess, " MDTM\r\n");
   vsf_cmdio_write_raw(p_sess, " PASV\r\n");
+  if (tunable_enable_iconv)
+  {
+    static struct mystr s_temp_str;
+
+    str_alloc_text(&s_temp_str, tunable_remote_charset);
+    str_upper(&s_temp_str);
+    if (str_equal_text(&s_temp_str, "UTF-8"))
+      vsf_cmdio_write_raw(p_sess, " UTF8\r\n");
+  }
   if (tunable_ssl_enable)
   {
     vsf_cmdio_write_raw(p_sess, " PBSZ\r\n");
diff -Nur vsftpd-2.0.1/ftpcmdio.c vsftpd-2.0.1.patched/ftpcmdio.c
--- vsftpd-2.0.1/ftpcmdio.c	2004-07-02 19:23:02.000000000 +0800
+++ vsftpd-2.0.1.patched/ftpcmdio.c	2005-02-22 11:48:44.000000000 +0800
@@ -7,6 +7,9 @@
  * Routines applicable to reading and writing the FTP command stream.
  */
 
+#include <stdlib.h>
+#include <string.h>
+#include "utility.h"
 #include "ftpcmdio.h"
 #include "ftpcodes.h"
 #include "str.h"
@@ -170,6 +173,16 @@
   control_getline(p_cmd_str, p_sess);
   str_split_char(p_cmd_str, p_arg_str, ' ');
   str_upper(p_cmd_str);
+  if (!str_isempty(p_arg_str)) {
+      char *tmp_str;
+
+      tmp_str = remote2local(str_getbuf(p_arg_str));
+      if (tmp_str != NULL) {
+	  str_empty(p_arg_str);
+          str_append_text(p_arg_str, tmp_str);
+	  vsf_sysutil_free(tmp_str);
+      }
+  }
   if (tunable_log_ftp_protocol)
   {
     static struct mystr s_log_str;
diff -Nur vsftpd-2.0.1/ls.c vsftpd-2.0.1.patched/ls.c
--- vsftpd-2.0.1/ls.c	2004-07-02 19:23:34.000000000 +0800
+++ vsftpd-2.0.1.patched/ls.c	2005-02-22 11:48:56.000000000 +0800
@@ -7,6 +7,9 @@
  * Would you believe, code to handle directory listing.
  */
 
+#include <stdlib.h>
+#include <string.h>
+#include "utility.h"
 #include "ls.h"
 #include "access.h"
 #include "str.h"
@@ -363,6 +366,7 @@
                const struct vsf_sysutil_statbuf* p_stat)
 {
   static struct mystr s_tmp_str;
+  char *tmp_filename;
   filesize_t size = vsf_sysutil_statbuf_get_size(p_stat);
   /* Permissions */
   str_alloc_text(p_str, vsf_sysutil_statbuf_get_perms(p_stat));
@@ -432,7 +436,13 @@
                                                       tunable_use_localtime));
   str_append_char(p_str, ' ');
   /* Filename */
-  str_append_str(p_str, p_filename_str);
+  tmp_filename = local2remote(str_getbuf(p_filename_str));
+  if (tmp_filename == NULL)
+    str_append_str(p_str, p_filename_str);
+  else {
+    str_append_text(p_str, tmp_filename);
+    vsf_sysutil_free(tmp_filename);
+  }
   str_append_text(p_str, "\r\n");
 }
 
diff -Nur vsftpd-2.0.1/parseconf.c vsftpd-2.0.1.patched/parseconf.c
--- vsftpd-2.0.1/parseconf.c	2004-07-02 19:23:56.000000000 +0800
+++ vsftpd-2.0.1.patched/parseconf.c	2005-02-22 11:49:12.000000000 +0800
@@ -94,6 +94,7 @@
   { "ssl_sslv3", &tunable_sslv3 },
   { "ssl_tlsv1", &tunable_tlsv1 },
   { "tilde_user_enable", &tunable_tilde_user_enable },
+  { "enable_iconv", &tunable_enable_iconv },
   { 0, 0 }
 };
 
@@ -158,6 +159,8 @@
   { "rsa_cert_file", &tunable_rsa_cert_file },
   { "dsa_cert_file", &tunable_dsa_cert_file },
   { "ssl_ciphers", &tunable_ssl_ciphers },
+  { "local_charset", &tunable_local_charset },
+  { "remote_charset", &tunable_remote_charset },
   { 0, 0 }
 };
 
diff -Nur vsftpd-2.0.1/postlogin.c vsftpd-2.0.1.patched/postlogin.c
--- vsftpd-2.0.1/postlogin.c	2004-07-02 19:24:01.000000000 +0800
+++ vsftpd-2.0.1.patched/postlogin.c	2005-03-04 21:23:39.000000000 +0800
@@ -5,6 +5,9 @@
  * postlogin.c
  */
 
+#include <stdlib.h>
+#include <string.h>
+#include "utility.h"
 #include "postlogin.h"
 #include "session.h"
 #include "oneprocess.h"
@@ -404,6 +407,7 @@
 static void
 handle_pwd(struct vsf_session* p_sess)
 {
+  char *tmp_str;
   static struct mystr s_cwd_buf_mangle_str;
   static struct mystr s_pwd_res_str;
   str_getcwd(&s_cwd_buf_mangle_str);
@@ -411,7 +415,13 @@
   str_replace_text(&s_cwd_buf_mangle_str, "\"", "\"\"");
   /* Enclose pathname in quotes */
   str_alloc_text(&s_pwd_res_str, "\"");
-  str_append_str(&s_pwd_res_str, &s_cwd_buf_mangle_str);
+  tmp_str = local2remote(str_getbuf(&s_cwd_buf_mangle_str));
+  if (tmp_str == NULL)
+    str_append_str(&s_pwd_res_str, &s_cwd_buf_mangle_str);
+  else {
+    str_append_text(&s_pwd_res_str, tmp_str);
+    vsf_sysutil_free(tmp_str);
+  }
   str_append_text(&s_pwd_res_str, "\"");
   vsf_cmdio_write_str(p_sess, FTP_PWDOK, &s_pwd_res_str);
 }
@@ -435,6 +445,24 @@
   }
   else
   {
+    if (tunable_enable_iconv) {
+      char *tmp_str;
+
+      tmp_str = local2remote(str_getbuf(&p_sess->ftp_arg_str));
+      if (tmp_str != NULL) {
+        str_empty(&p_sess->ftp_arg_str);
+        str_append_text(&p_sess->ftp_arg_str, tmp_str);
+        vsf_sysutil_free(tmp_str);
+        retval = str_chdir(&p_sess->ftp_arg_str);
+        if (retval == 0)
+        {
+          /* Handle any messages */
+          vsf_banner_dir_changed(p_sess, FTP_CWDOK);
+          vsf_cmdio_write(p_sess, FTP_CWDOK, "Directory successfully changed.");
+	  return;
+        }
+      }
+    }
     vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to change directory.");
   }
 }
@@ -612,6 +640,7 @@
   static struct mystr s_mark_str;
   static struct vsf_sysutil_statbuf* s_p_statbuf;
   struct vsf_transfer_ret trans_ret;
+  char *tmp_str;
   int retval;
   int remote_fd;
   int opened_file;
@@ -640,8 +669,29 @@
   opened_file = str_open(&p_sess->ftp_arg_str, kVSFSysStrOpenReadOnly);
   if (vsf_sysutil_retval_is_error(opened_file))
   {
-    vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to open file.");
-    return;
+    if (tunable_enable_iconv) {
+      char *tmp_str;
+
+      tmp_str = local2remote(str_getbuf(&p_sess->ftp_arg_str));
+      if (tmp_str != NULL) {
+	str_empty(&p_sess->ftp_arg_str);
+	str_append_text(&p_sess->ftp_arg_str, tmp_str);
+	vsf_sysutil_free(tmp_str);
+	opened_file = str_open(&p_sess->ftp_arg_str, kVSFSysStrOpenReadOnly);
+	if (vsf_sysutil_retval_is_error(opened_file)) {
+          vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to open file.");
+          return;
+	}
+      }
+      else {
+        vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to open file.");
+        return;
+      }
+    }
+    else {
+      vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to open file.");
+      return;
+    }
   }
   vsf_sysutil_fstat(opened_file, &s_p_statbuf);
   /* No games please */
@@ -674,7 +724,13 @@
     str_append_text(&s_mark_str, "BINARY");
   }
   str_append_text(&s_mark_str, " mode data connection for ");
-  str_append_str(&s_mark_str, &p_sess->ftp_arg_str);
+  tmp_str = local2remote(str_getbuf(&p_sess->ftp_arg_str));
+  if (tmp_str != NULL) {
+    str_append_text(&s_mark_str, tmp_str);
+    vsf_sysutil_free(tmp_str);
+  }
+  else
+    str_append_str(&s_mark_str, &p_sess->ftp_arg_str);
   str_append_text(&s_mark_str, " (");
   str_append_filesize_t(&s_mark_str,
                         vsf_sysutil_statbuf_get_size(s_p_statbuf));
@@ -1102,6 +1158,25 @@
   retval = str_rmdir(&p_sess->ftp_arg_str);
   if (retval != 0)
   {
+    if (tunable_enable_iconv) {
+      char *tmp_str;
+
+      tmp_str = local2remote(str_getbuf(&p_sess->ftp_arg_str));
+      if (tmp_str != NULL) {
+        str_empty(&p_sess->ftp_arg_str);
+        str_append_text(&p_sess->ftp_arg_str, tmp_str);
+        vsf_sysutil_free(tmp_str);
+        retval = str_rmdir(&p_sess->ftp_arg_str);
+        if (retval == 0)
+        {
+          /* Handle any messages */
+          vsf_banner_dir_changed(p_sess, FTP_CWDOK);
+          vsf_cmdio_write(p_sess, FTP_FILEFAIL,
+                    "Remove directory operation failed.");
+          return;
+        }
+      }
+    }
     vsf_cmdio_write(p_sess, FTP_FILEFAIL,
                     "Remove directory operation failed.");
   }
@@ -1129,6 +1204,24 @@
   retval = str_unlink(&p_sess->ftp_arg_str);
   if (retval != 0)
   {
+    if (tunable_enable_iconv) {
+      char *tmp_str;
+
+      tmp_str = local2remote(str_getbuf(&p_sess->ftp_arg_str));
+      if (tmp_str != NULL) {
+        str_empty(&p_sess->ftp_arg_str);
+        str_append_text(&p_sess->ftp_arg_str, tmp_str);
+        vsf_sysutil_free(tmp_str);
+        retval = str_unlink(&p_sess->ftp_arg_str);
+        if (retval == 0)
+        {
+          /* Handle any messages */
+          vsf_banner_dir_changed(p_sess, FTP_CWDOK);
+          vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Delete operation failed.");
+          return;
+        }
+      }
+    }
     vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Delete operation failed.");
   }
   else
@@ -1180,6 +1273,24 @@
   }
   else
   {
+    if (tunable_enable_iconv) {
+      char *tmp_str;
+
+      tmp_str = local2remote(str_getbuf(&p_sess->ftp_arg_str));
+      if (tmp_str != NULL) {
+        str_empty(&p_sess->ftp_arg_str);
+        str_append_text(&p_sess->ftp_arg_str, tmp_str);
+        vsf_sysutil_free(tmp_str);
+        retval = str_stat(&p_sess->ftp_arg_str, &p_statbuf);
+        if (retval == 0)
+        {
+          /* Handle any messages */
+          str_copy(&p_sess->rnfr_filename_str, &p_sess->ftp_arg_str);
+          vsf_cmdio_write(p_sess, FTP_RNFROK, "Ready for RNTO.");
+          return;
+        }
+      }
+    }
     vsf_log_start_entry(p_sess, kVSFLogEntryRename);
     str_copy(&p_sess->log_str, &p_sess->ftp_arg_str);
     prepend_path_to_filename(&p_sess->log_str);
@@ -1360,6 +1471,26 @@
   retval = str_stat(&p_sess->ftp_arg_str, &s_p_statbuf);
   if (retval != 0 || !vsf_sysutil_statbuf_is_regfile(s_p_statbuf))
   {
+    if (tunable_enable_iconv) {
+      char *tmp_str;
+
+      tmp_str = local2remote(str_getbuf(&p_sess->ftp_arg_str));
+      if (tmp_str != NULL) {
+        str_empty(&p_sess->ftp_arg_str);
+        str_append_text(&p_sess->ftp_arg_str, tmp_str);
+        vsf_sysutil_free(tmp_str);
+        retval = str_stat(&p_sess->ftp_arg_str, &s_p_statbuf);
+        if (retval == 0 && vsf_sysutil_statbuf_is_regfile(s_p_statbuf))
+        {
+          /* Handle any messages */
+          static struct mystr s_size_res_str;
+          str_alloc_filesize_t(&s_size_res_str,
+                               vsf_sysutil_statbuf_get_size(s_p_statbuf));
+          vsf_cmdio_write_str(p_sess, FTP_SIZEOK, &s_size_res_str);
+          return;
+        }
+      }
+    }
     vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Could not get file size.");
   }
   else
@@ -1431,6 +1562,24 @@
   retval = str_chmod(&s_chmod_file_str, perms);
   if (vsf_sysutil_retval_is_error(retval))
   {
+    if (tunable_enable_iconv) {
+      char *tmp_str;
+
+      tmp_str = local2remote(str_getbuf(&s_chmod_file_str));
+      if (tmp_str != NULL) {
+        str_empty(&s_chmod_file_str);
+        str_append_text(&s_chmod_file_str, tmp_str);
+        vsf_sysutil_free(tmp_str);
+        retval = str_chmod(&s_chmod_file_str, perms);
+        if (vsf_sysutil_retval_is_error(retval))
+        {
+          /* Handle any messages */
+          vsf_log_do_log(p_sess, 1);
+          vsf_cmdio_write(p_sess, FTP_CHMODOK, "SITE CHMOD command ok.");
+          return;
+        }
+      }
+    }
     vsf_cmdio_write(p_sess, FTP_FILEFAIL, "SITE CHMOD command failed.");
   }
   else
@@ -1505,6 +1654,34 @@
     retval = str_stat(&p_sess->ftp_arg_str, &s_p_statbuf);
     if (retval != 0 || !vsf_sysutil_statbuf_is_regfile(s_p_statbuf))
     {
+      if (tunable_enable_iconv) {
+        char *tmp_str;
+
+        tmp_str = local2remote(str_getbuf(&p_sess->ftp_arg_str));
+        if (tmp_str != NULL) {
+          str_empty(&p_sess->ftp_arg_str);
+          str_append_text(&p_sess->ftp_arg_str, tmp_str);
+          vsf_sysutil_free(tmp_str);
+          retval = str_stat(&p_sess->ftp_arg_str, &s_p_statbuf);
+          if (retval == 0 && vsf_sysutil_statbuf_is_regfile(s_p_statbuf))
+          {
+            /* Handle any messages */
+            retval = vsf_sysutil_setmodtime(
+              str_getbuf(&p_sess->ftp_arg_str), modtime, tunable_use_localtime);
+            if (retval != 0)
+            {
+              vsf_cmdio_write(p_sess, FTP_FILEFAIL,
+                              "Could not set file modification time.");
+            }
+            else
+            {
+              vsf_cmdio_write(p_sess, FTP_MDTMOK,
+                              "File modification time set.");
+            }
+            return;
+          }
+        }
+      }
       vsf_cmdio_write(p_sess, FTP_FILEFAIL,
                       "Could not set file modification time.");
     }
@@ -1623,7 +1800,7 @@
   vsf_cmdio_write_raw(p_sess,
 " RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD\r\n");
   vsf_cmdio_write_raw(p_sess,
-" XPWD XRMD\r\n");
+" XPWD XRMD ICNV\r\n");
   vsf_cmdio_write(p_sess, FTP_HELP, "Help OK.");
 }
 
diff -Nur vsftpd-2.0.1/tunables.c vsftpd-2.0.1.patched/tunables.c
--- vsftpd-2.0.1/tunables.c	2004-07-02 19:26:17.000000000 +0800
+++ vsftpd-2.0.1.patched/tunables.c	2005-02-22 12:17:52.000000000 +0800
@@ -66,6 +66,7 @@
 int tunable_sslv3 = 0;
 int tunable_tlsv1 = 1;
 int tunable_tilde_user_enable = 0;
+int tunable_enable_iconv = 0;
 
 unsigned int tunable_accept_timeout = 60;
 unsigned int tunable_connect_timeout = 60;
@@ -115,4 +116,7 @@
 const char* tunable_rsa_cert_file = "/usr/share/ssl/certs/vsftpd.pem";
 const char* tunable_dsa_cert_file = 0;
 const char* tunable_ssl_ciphers = "DES-CBC3-SHA";
+const char* tunable_local_charset = "UTF-8";
+const char* tunable_remote_charset = "BIG5";
+
 
diff -Nur vsftpd-2.0.1/tunables.h vsftpd-2.0.1.patched/tunables.h
--- vsftpd-2.0.1/tunables.h	2004-06-29 07:08:31.000000000 +0800
+++ vsftpd-2.0.1.patched/tunables.h	2005-02-22 11:50:27.000000000 +0800
@@ -62,6 +62,7 @@
 extern int tunable_sslv3;                     /* Allow SSLv3 */
 extern int tunable_tlsv1;                     /* Allow TLSv1 */
 extern int tunable_tilde_user_enable;         /* Support e.g. ~chris */
+extern int tunable_enable_iconv;	      /* Convert filename use iconv */
 
 /* Integer/numeric defines */
 extern unsigned int tunable_accept_timeout;
@@ -110,6 +111,8 @@
 extern const char* tunable_rsa_cert_file;
 extern const char* tunable_dsa_cert_file;
 extern const char* tunable_ssl_ciphers;
+extern const char* tunable_local_charset;
+extern const char* tunable_remote_charset;
 
 #endif /* VSF_TUNABLES_H */
 
diff -Nur vsftpd-2.0.1/utility.c vsftpd-2.0.1.patched/utility.c
--- vsftpd-2.0.1/utility.c	2004-07-02 19:26:30.000000000 +0800
+++ vsftpd-2.0.1.patched/utility.c	2005-02-22 14:05:46.000000000 +0800
@@ -5,6 +5,13 @@
  * utility.c
  */
 
+#include <stdarg.h>
+#include <iconv.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include "tunables.h"
 #include "utility.h"
 #include "sysutil.h"
 #include "str.h"
@@ -50,3 +57,71 @@
   vsf_sysutil_exit(0);
 }
 
+char *
+local2remote(const char *buf)
+{
+  char *in_ptr;
+  char *out_ptr;
+  size_t inbytesleft, outbytesleft;
+  char *p;
+  iconv_t ic;
+
+  if (tunable_enable_iconv == 0) return NULL;
+  ic = iconv_open(tunable_remote_charset, tunable_local_charset);
+  if (ic == (iconv_t)(-1)) return NULL;
+  iconv(ic, NULL, NULL, NULL, NULL);
+
+  inbytesleft = strlen(buf);
+  outbytesleft = inbytesleft * 6;
+  p = vsf_sysutil_malloc(outbytesleft+1);
+
+  in_ptr = buf;
+  out_ptr = p;
+  while (inbytesleft) {
+    if (iconv(ic, &in_ptr, &inbytesleft, &out_ptr, &outbytesleft) == (size_t)(-1)) {
+      iconv_close(ic);
+      vsf_sysutil_free(p);
+      return NULL;
+    }
+  }
+
+  *out_ptr = 0;
+
+  iconv_close(ic);
+  return p;
+}
+
+char *
+remote2local(const char *buf)
+{
+  char *in_ptr;
+  char *out_ptr;
+  size_t inbytesleft, outbytesleft;
+  char *p;
+  iconv_t ic;
+
+  if (tunable_enable_iconv == 0) return NULL;
+  ic = iconv_open(tunable_local_charset, tunable_remote_charset);
+  if (ic == (iconv_t)(-1)) return NULL;
+  iconv(ic, NULL, NULL, NULL, NULL);
+
+  inbytesleft = strlen(buf);
+  outbytesleft = inbytesleft * 6;
+  p = vsf_sysutil_malloc(outbytesleft+1);
+
+  in_ptr = buf;
+  out_ptr = p;
+  while (inbytesleft) {
+    if (iconv(ic, &in_ptr, &inbytesleft, &out_ptr, &outbytesleft) == (size_t)(-1)) {
+      iconv_close(ic);
+      vsf_sysutil_free(p);
+      return NULL;
+    }
+  }
+
+  *out_ptr = 0;
+
+  iconv_close(ic);
+  return p;
+}
+
diff -Nur vsftpd-2.0.1/utility.h vsftpd-2.0.1.patched/utility.h
--- vsftpd-2.0.1/utility.h	2004-04-16 06:46:29.000000000 +0800
+++ vsftpd-2.0.1.patched/utility.h	2005-02-22 11:47:51.000000000 +0800
@@ -40,5 +40,7 @@
  */
 void vsf_exit(const char* p_text);
 
+char *local2remote(const char *buf);
+char *remote2local(const char *buf);
 #endif
 
