Index: configure =================================================================== --- configure (revision 1) +++ configure (revision 15) @@ -923,6 +923,8 @@ --with-tls Enable SSL/TLS support (experimental, needs OpenSSL) --with-certfile= certificate file (default: /etc/ssl/private/pure-ftpd.pem) + --with-rfc2640 Enable RFC 2640 behavior of transfering utf-8 + directory and file names (experimental, needs iconv) --with-rendezvous Enable Rendezvous support on MacOS X (experimental) Some influential environment variables: @@ -6694,6 +6696,156 @@ done + +for ac_header in iconv.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## -------------------------------- ## +## Report this to bugs@pureftpd.org ## +## -------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + echo "$as_me:$LINENO: checking POSIX termios" >&5 echo $ECHO_N "checking POSIX termios... $ECHO_C" >&6 if test "${ac_cv_sys_posix_termios+set}" = set; then @@ -20687,6 +20839,102 @@ fi fi fi; + + +# Check whether --with-rfc2640 or --without-rfc2640 was given. +if test "${with_rfc2640+set}" = set; then + withval="$with_rfc2640" + if test "x$withval" = "xyes" ; then + with_rfc2640="yes" + fi +fi; + +if test "x$with_rfc2640" = "xyes" ; then + if test "x$ac_cv_header_iconv_h" != "xyes" ; then + { { echo "$as_me:$LINENO: error: iconv headers not found." >&5 +echo "$as_me: error: iconv headers not found." >&2;} + { (exit 1); exit 1; }; } + fi + +echo "$as_me:$LINENO: checking for iconv in -liconv" >&5 +echo $ECHO_N "checking for iconv in -liconv... $ECHO_C" >&6 +if test "${ac_cv_lib_iconv_iconv+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-liconv $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char iconv (); +int +main () +{ +iconv (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_iconv_iconv=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_iconv_iconv=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_iconv_iconv" >&5 +echo "${ECHO_T}$ac_cv_lib_iconv_iconv" >&6 +if test $ac_cv_lib_iconv_iconv = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBICONV 1 +_ACEOF + + LIBS="-liconv $LIBS" + +fi + + +cat >>confdefs.h <<\_ACEOF +#define WITH_RFC2640 +_ACEOF + +fi # Check whether --with-rendezvous or --without-rendezvous was given. Index: src/messages_en.h =================================================================== --- src/messages_en.h (revision 1) +++ src/messages_en.h (revision 15) @@ -218,3 +218,4 @@ #define MSG_TLS_WEAK "SSL/TLS: Cipher too weak" #define MSG_TLS_NEEDED "Sorry, cleartext sessions are not accepted on this server.\n" \ "Please reconnect using SSL/TLS security mechanisms." +#define MSG_ILLEGAL_CHARSET "Illegal charset" Index: src/messages_it.h =================================================================== --- src/messages_it.h (revision 1) +++ src/messages_it.h (revision 15) @@ -218,3 +218,4 @@ #define MSG_TLS_WEAK "SSL/TLS: Cifratura troppo debole" #define MSG_TLS_NEEDED "Spiacente, su questo server non sono ammesse sessioni in chiaro.\n" \ "Ricollegati usando i sistemi di sicurezza SSL/TLS." +#define MSG_ILLEGAL_CHARSET "Illegal charset" Index: src/messages_sk.h =================================================================== --- src/messages_sk.h (revision 1) +++ src/messages_sk.h (revision 15) @@ -218,3 +218,4 @@ #define MSG_TLS_WEAK "SSL/TLS: Sifra je prilis slaba" #define MSG_TLS_NEEDED "Prepacte, ale nesifrovane spojenia nie su povolene na tomto serveri.\n" \ "Pri najblizsom spojeni pouzite zabezpecovacie mechanizmy SSL/TLS." +#define MSG_ILLEGAL_CHARSET "Illegal charset" Index: src/messages_zh_cn.h =================================================================== --- src/messages_zh_cn.h (revision 1) +++ src/messages_zh_cn.h (revision 15) @@ -218,3 +218,4 @@ #define MSG_TLS_WEAK "SSL/TLS: ÃÜÂë²»×ã" #define MSG_TLS_NEEDED "±§Ç¸£¬´Ë·þÎñÆ÷²»½ÓÊÜÃ÷ÎÄʽÁ¬½Ó¡£\n" \ "ÇëʹÓÃSSL/TLSµÄ°²È«»úÖÆÖØÐÂÁ¬½Ó¡£" +#define MSG_ILLEGAL_CHARSET "Illegal charset" Index: src/ftp_parser.c =================================================================== --- src/ftp_parser.c (revision 1) +++ src/ftp_parser.c (revision 15) @@ -16,6 +16,10 @@ # include #endif +#ifdef WITH_RFC2640 +extern char * charset_fs2client(char *string); +#endif + static void antiidle(void) { if (noopidle == (time_t) -1) { @@ -181,12 +185,64 @@ } #endif +#ifdef WITH_RFC2640 +char * charset_client2fs(char *string) +{ + char *output, *outstr; + size_t inlen, outlen; + iconv_t cd; + + inlen = strlen(string) * sizeof(char); + outlen = (strlen(string) * 4 + 1) * sizeof(char); + outstr = output = (char *) malloc (outlen); + + if(outstr == NULL) + return NULL; + + memset(output, 0, outlen); + + if(utf8 && strcmp(charset_fs, "utf-8") != 0) + { + cd = iconv_open(charset_fs, "utf-8"); + if(cd == (iconv_t) -1) + { + strncpy(output, string, outlen); + } else { + if(iconv(cd, (const char* *) &string, &inlen, &outstr, &outlen) == (size_t) -1) + { + strncpy(output, string, outlen); + } + } + } else if (!utf8 && strcmp(charset_fs, charset_c) != 0) { + cd = iconv_open(charset_fs, charset_c); + if(cd == (iconv_t) -1) + { + strncpy(output, string, outlen); + } else { + if(iconv(cd, (const char* *) &string, &inlen, &outstr, &outlen) == (size_t) -1) + { + strncpy(output, string, outlen); + } + } + } else { + /* encoding of client is the same as file system */ + strncpy(output, string, outlen); + } + + return output; +} +#endif + void parser(void) { char *arg; #ifndef MINIMAL char *sitearg; #endif +#ifdef WITH_RFC2640 + char *narg = NULL; + char *nwd = NULL; +#endif size_t n; for (;;) { @@ -265,6 +321,10 @@ cmd, strcmp(cmd, "pass") ? arg : "<*>"); #endif } +#ifdef WITH_RFC2640 + narg = charset_client2fs(arg); + arg = narg; +#endif /* * antiidle() is called with dummy commands, usually used by clients * who are wanting extra idle time. We give them some, but not too much. @@ -346,6 +406,9 @@ } else if (!strcmp(cmd, "feat")) { dofeat(); goto wayout; + } else if (!strcmp(cmd, "opts")) { + doopts(arg); + goto wayout; #endif } else if (!strcmp(cmd, "stru")) { dostru(arg); @@ -402,7 +465,13 @@ #endif } else if (!strcmp(cmd, "pwd") || !strcmp(cmd, "xpwd")) { antiidle(); +#ifdef WITH_RFC2640 + nwd = charset_fs2client(wd); + addreply(257, "\"%s\" " MSG_IS_YOUR_CURRENT_LOCATION, nwd); + free(nwd); +#else addreply(257, "\"%s\" " MSG_IS_YOUR_CURRENT_LOCATION, wd); +#endif goto wayout; } else if (!strcmp(cmd, "cdup") || !strcmp(cmd, "xcup")) { docwd(".."); @@ -609,6 +678,13 @@ } noopidle = (time_t) -1; wayout: +#ifdef WITH_RFC2640 + if(narg != NULL) + { + free(narg); + narg = NULL; + } +#endif #ifdef THROTTLING if (throttling_delay != 0UL) { usleep2(throttling_delay); Index: src/messages_pl.h =================================================================== --- src/messages_pl.h (revision 1) +++ src/messages_pl.h (revision 15) @@ -218,3 +218,4 @@ #define MSG_TLS_WEAK "SSL/TLS: Cipher too weak" #define MSG_TLS_NEEDED "Sorry, cleartext sessions are not accepted on this server.\n" \ "Please reconnect using SSL/TLS security mechanisms." +#define MSG_ILLEGAL_CHARSET "Illegal charset" Index: src/messages_no.h =================================================================== --- src/messages_no.h (revision 1) +++ src/messages_no.h (revision 15) @@ -218,3 +218,4 @@ #define MSG_TLS_WEAK "SSL/TLS: Krypteringsnøkkelen er for svak" #define MSG_TLS_NEEDED "Beklager, klartekst-forbindelser er ikke tillatt på denne serveren.\n" \ "Vær vennlig å koble til på nytt ved bruk av SSL/TLS-sikkerhetsmekanismer." +#define MSG_ILLEGAL_CHARSET "Illegal charset" Index: src/messages_ro.h =================================================================== --- src/messages_ro.h (revision 1) +++ src/messages_ro.h (revision 15) @@ -218,3 +218,4 @@ #define MSG_TLS_WEAK "SSL/TLS: Sistem de criptare prea slab" #define MSG_TLS_NEEDED "Sesiunile in text clar nu sint acceptate pe acest server.\n" \ "Reconectati-va utilizind mecanismele de securitate SSL/TLS." +#define MSG_ILLEGAL_CHARSET "Illegal charset" Index: src/ftpd_p.h =================================================================== --- src/ftpd_p.h (revision 1) +++ src/ftpd_p.h (revision 15) @@ -60,7 +60,11 @@ }; static const char *GETOPT_OPTIONS = - "0146Aa:bc:" + "0146" +#ifdef WITH_RFC2640 + "8:9:" +#endif + "Aa:bc:" #ifndef NO_STANDALONE "BC:" #endif @@ -110,6 +114,10 @@ { "logpid", 0, NULL, '1' }, { "ipv4only", 0, NULL, '4' }, { "ipv6only", 0, NULL, '6' }, +#ifdef WITH_RFC2640 + { "fscharset", 1, NULL, '8' }, + { "clientcharset", 1, NULL, '9' }, +#endif { "chrooteveryone", 0, NULL, 'A' }, { "trustedgid", 1, NULL, 'a' }, { "brokenclientscompatibility", 0, NULL, 'b' }, Index: src/messages_tr.h =================================================================== --- src/messages_tr.h (revision 1) +++ src/messages_tr.h (revision 15) @@ -218,3 +218,4 @@ #define MSG_TLS_WEAK "SSL/TLS: Cipher too weak" #define MSG_TLS_NEEDED "Sorry, cleartext sessions are not accepted on this server.\n" \ "Please reconnect using SSL/TLS security mechanisms." +#define MSG_ILLEGAL_CHARSET "Illegal charset" Index: src/messages_ru.h =================================================================== --- src/messages_ru.h (revision 1) +++ src/messages_ru.h (revision 15) @@ -218,3 +218,4 @@ #define MSG_TLS_WEAK "SSL/TLS: ëÌÀÞ ÎÅÎÁÄÅÖÅÎ" #define MSG_TLS_NEEDED "éÚ×ÉÎÉÔÅ, ÎÅÚÁÝÉÝÅÎÎÙÅ ÐÏÄËÌÀÞÅÎÉÑ ÚÁÐÒÅÝÅÎÙ ÎÁ ÓÅÒ×ÅÒÅ.\n" \ "ðÏÄËÌÀÞÉÔÅÓØ ÅÝÅ ÒÁÚ, ÉÓÐÏÌØÚÕÑ ÛÉÆÒÏ×ÁÎÉÅ SSL/TLS." +#define MSG_ILLEGAL_CHARSET "Illegal charset" Index: src/ftpd.c =================================================================== --- src/ftpd.c (revision 1) +++ src/ftpd.c (revision 15) @@ -38,6 +38,11 @@ # include #endif +#ifdef WITH_RFC2640 +# include +extern char * charset_fs2client(char *string); +#endif + #ifndef HAVE_SYS_FSUID_H void disablesignals(void) { @@ -1475,6 +1480,9 @@ #else int ngroups_max = 1; /* use a sane default */ #endif +#ifdef WITH_RFC2640 + char *nwd = NULL; +#endif if (loggedin != 0) { if (guest != 0) { @@ -1752,9 +1760,21 @@ if (chdir(wd)) { _EXIT(EXIT_FAILURE); } +#ifdef WITH_RFC2640 + nwd = charset_fs2client(wd); + addreply(230, MSG_CURRENT_RESTRICTED_DIR_IS, nwd); + free(nwd); +#else addreply(230, MSG_CURRENT_RESTRICTED_DIR_IS, wd); +#endif } else { +#ifdef WITH_RFC2640 + nwd = charset_fs2client(wd); + addreply(230, MSG_CURRENT_RESTRICTED_DIR_IS, nwd); + free(nwd); +#else addreply(230, MSG_CURRENT_DIR_IS, wd); +#endif } logfile(LOG_INFO, MSG_IS_NOW_LOGGED_IN, account); #ifdef FTPWHO @@ -1790,6 +1810,9 @@ #endif const char *where; char buffer[MAXPATHLEN + 256U]; +#ifdef WITH_RFC2640 + char *nwd = NULL; +#endif if (loggedin == 0) { goto kaboom; @@ -1899,7 +1922,13 @@ } } } +#ifdef WITH_RFC2640 + nwd = charset_fs2client(wd); + addreply(257, "\"%s\" " MSG_IS_YOUR_CURRENT_LOCATION, nwd); + free(nwd); +#else addreply(250, MSG_CURRENT_DIR_IS, wd); +#endif } static void iptropize(const struct sockaddr_storage *ss) @@ -3313,8 +3342,14 @@ # define FEAT_ESTA CRLF " ESTA" # define FEAT_ESTP CRLF " ESTP" #endif + +#ifdef WITH_RFC2640 +# define FEAT_UTF8 CRLF " UTF8" +#else +# define FEAT_UTF8 "" +#endif - char feat[] = FEAT FEAT_DEBUG FEAT_TVFS FEAT_ESTP FEAT_PASV FEAT_ESTA FEAT_TLS; + char feat[] = FEAT FEAT_DEBUG FEAT_TVFS FEAT_ESTP FEAT_PASV FEAT_ESTA FEAT_TLS FEAT_UTF8; if (disallow_passive != 0) { feat[sizeof FEAT FEAT_DEBUG FEAT_TVFS FEAT_ESTP] = 0; @@ -4827,6 +4862,9 @@ #ifndef NO_GETOPT_LONG int option_index = 0; #endif +#ifdef WITH_RFC2640 + iconv_t iconv_test; +#endif int fodder; int bypass_ipv6 = 0; struct passwd *pw; @@ -4914,6 +4952,30 @@ no_ipv4 = 1; break; } +#ifdef WITH_RFC2640 + case '8': { + iconv_test = iconv_open (optarg, "utf-8"); + if(iconv_test == (iconv_t) -1) + { + die (421, LOG_ERR, MSG_CONF_ERR ": " MSG_ILLEGAL_CHARSET ": %s", optarg); + } + iconv_close(iconv_test); + charset_fs = (char *) malloc ((strlen(optarg) + 1) * sizeof(char)); + strcpy (charset_fs, optarg); + break; + } + case '9': { + iconv_test = iconv_open (optarg, "utf-8"); + if(iconv_test == (iconv_t) -1) + { + die (421, LOG_ERR, MSG_CONF_ERR ": " MSG_ILLEGAL_CHARSET ": %s", optarg); + } + iconv_close(iconv_test); + charset_c = (char *) malloc ((strlen(optarg) + 1) * sizeof(char)); + strcpy(charset_c, optarg); + break; + } +#endif case '1': { log_pid = LOG_PID; break; @@ -5468,6 +5530,29 @@ logfile(LOG_WARNING, MSG_ILLEGAL_OPTION); } } +#ifdef WITH_RFC2640 + if (charset_fs == NULL) + charset_fs = "utf-8"; + if (charset_c == NULL) + charset_c = "utf-8"; + /* Test if charset_fs can be convert into charset_c, and vice versa */ + if (strcmp(charset_fs, charset_c) != 0) + { + iconv_test = iconv_open (charset_fs, charset_c); + if(iconv_test == (iconv_t) -1) + { + die (421, LOG_ERR, MSG_CONF_ERR ": " MSG_ILLEGAL_CHARSET ": %s", optarg); + } + iconv_close(iconv_test); + iconv_test = iconv_open (charset_c, charset_fs); + if(iconv_test == (iconv_t) -1) + { + die (421, LOG_ERR, MSG_CONF_ERR ": " MSG_ILLEGAL_CHARSET ": %s", optarg); + } + iconv_close(iconv_test); + } +#endif + if (first_authentications == NULL) { if ((first_authentications = malloc(sizeof *first_authentications)) == NULL) { die_mem(); @@ -5560,3 +5645,36 @@ return 0; } + +#ifndef MINIMAL +void doopts(char *args) +{ + char *cmdopts; + + cmdopts = strchr(args, ' '); + if(cmdopts != NULL) + cmdopts++; + +#ifdef WITH_RFC2640 + if(strncasecmp("utf8 ", args, 5) == 0) + { + if(cmdopts == NULL) // No command options + addreply_noformat(501, "OPTS UTF8: " MSG_MISSING_ARG); + else if(strncasecmp("on", cmdopts, 2) == 0) + { + utf8 = 1; + addreply_noformat(200, "OK"); + } + else if(strncasecmp("off", cmdopts, 3) == 0) + { + utf8 = 0; + addreply_noformat(200, "OK"); + } + return; + } +#endif + + // Syntax Error + addreply_noformat(500, MSG_UNKNOWN_COMMAND); +} +#endif Index: src/messages_cs_cz.h =================================================================== --- src/messages_cs_cz.h (revision 1) +++ src/messages_cs_cz.h (revision 15) @@ -218,3 +218,4 @@ #define MSG_TLS_WEAK "SSL/TLS: Cipher too weak" #define MSG_TLS_NEEDED "Sorry, cleartext sessions are not accepted on this server.\n" \ "Please reconnect using SSL/TLS security mechanisms." +#define MSG_ILLEGAL_CHARSET "Illegal charset" Index: src/ftpd.h =================================================================== --- src/ftpd.h (revision 1) +++ src/ftpd.h (revision 15) @@ -334,6 +334,7 @@ void doestp(void); #endif void dopasv(int); +void doopts(char *args); void dochmod(char *name, mode_t mode); void error(int n, const char *msg); void domode(const char *arg); @@ -718,6 +719,10 @@ # define _EXIT(X) do { delete_atomic_file(); ftpwho_exit(X); } while(0) #else # define _EXIT(X) do { delete_atomic_file(); _exit(X); } while(0) +#endif + +#ifdef WITH_RFC2640 +# include #endif #include "bsd-realpath.h" Index: src/messages_pt_br.h =================================================================== --- src/messages_pt_br.h (revision 1) +++ src/messages_pt_br.h (revision 15) @@ -218,3 +218,4 @@ #define MSG_TLS_WEAK "SSL/TLS: Criptografia muito fraca" #define MSG_TLS_NEEDED "Desculpe, sessões sem criptografia não são aceitas neste servidor.\n" \ "Por favor reconecte-se usando mecanismos de segurança SSL/TLS." +#define MSG_ILLEGAL_CHARSET "Illegal charset" Index: src/messages_es.h =================================================================== --- src/messages_es.h (revision 1) +++ src/messages_es.h (revision 15) @@ -218,3 +218,4 @@ #define MSG_TLS_WEAK "SSL/TLS: Cifrado demasiado débil" #define MSG_TLS_NEEDED "Lo siento. No se aceptan sesiones sin cifrar en este servidor.\n" \ "Por favor, vuelva a conectar utilizando mecanismos de seguridad SSL/TLS." +#define MSG_ILLEGAL_CHARSET "Illegal charset" Index: src/messages_kr.h =================================================================== --- src/messages_kr.h (revision 1) +++ src/messages_kr.h (revision 15) @@ -218,3 +218,4 @@ #define MSG_TLS_WEAK "SSL/TLS: Cipher too weak" #define MSG_TLS_NEEDED "Sorry, cleartext sessions are not accepted on this server.\n" \ "Please reconnect using SSL/TLS security mechanisms." +#define MSG_ILLEGAL_CHARSET "Illegal charset" Index: src/messages_da.h =================================================================== --- src/messages_da.h (revision 1) +++ src/messages_da.h (revision 15) @@ -218,3 +218,4 @@ #define MSG_TLS_WEAK "SSL/TLS: Cipher too weak" #define MSG_TLS_NEEDED "Sorry, cleartext sessions are not accepted on this server.\n" \ "Please reconnect using SSL/TLS security mechanisms." +#define MSG_ILLEGAL_CHARSET "Illegal charset" Index: src/messages_de.h =================================================================== --- src/messages_de.h (revision 1) +++ src/messages_de.h (revision 15) @@ -218,3 +218,4 @@ #define MSG_TLS_WEAK "SSL/TLS: Verschlüsselung zu schwach" #define MSG_TLS_NEEDED "Sorry, unverschlüsselte Sitzungen werden auf diesem Server nicht akzeptiert.\n" \ "Bitte verbinden Sie sich mittels SSL/TLS-Verschlüsselung" +#define MSG_ILLEGAL_CHARSET "Illegal charset" Index: src/messages_sv.h =================================================================== --- src/messages_sv.h (revision 1) +++ src/messages_sv.h (revision 15) @@ -218,3 +218,4 @@ #define MSG_TLS_WEAK "SSL/TLS: Krypteringen är för svag" #define MSG_TLS_NEEDED "Tyvärr är sessioner i klartext inte tillåtna på denna server.\n" \ "Vänligen återanslut och använd SSL/TLS för ökad säkerhet." +#define MSG_ILLEGAL_CHARSET "Illegal charset" Index: src/messages_zh_tw.h =================================================================== --- src/messages_zh_tw.h (revision 1) +++ src/messages_zh_tw.h (revision 15) @@ -218,3 +218,4 @@ #define MSG_TLS_WEAK "SSL/TLS: ±K½X¤£¨¬" #define MSG_TLS_NEEDED "©êºp¡A¦¹¦øªA¾¹¤£±µ¨ü©ú¤å¦¡³s½u¡C\n" \ "½Ð¨Ï¥ÎSSL/TLSªº¦w¥þ¾÷¨î­«·s³s½u¡C" +#define MSG_ILLEGAL_CHARSET "Illegal charset" Index: src/messages_fr.h =================================================================== --- src/messages_fr.h (revision 1) +++ src/messages_fr.h (revision 15) @@ -218,3 +218,4 @@ #define MSG_TLS_WEAK "SSL/TLS: Chiffrement trop faible" #define MSG_TLS_NEEDED "Desole, les sessions en clair ne sont pas acceptees sur ce serveur.\n" \ "Veuillez vous reconnecter en utilisant les mechanismes de securite SSL/TLS." +#define MSG_ILLEGAL_CHARSET "Illegal charset" Index: src/messages_nl.h =================================================================== --- src/messages_nl.h (revision 1) +++ src/messages_nl.h (revision 15) @@ -218,3 +218,4 @@ #define MSG_TLS_WEAK "SSL/TLS: Cipher too weak" #define MSG_TLS_NEEDED "Sorry, cleartext sessions are not accepted on this server.\n" \ "Please reconnect using SSL/TLS security mechanisms." +#define MSG_ILLEGAL_CHARSET "Illegal charset" Index: src/messages_hu.h =================================================================== --- src/messages_hu.h (revision 1) +++ src/messages_hu.h (revision 15) @@ -218,3 +218,4 @@ #define MSG_TLS_WEAK "SSL/TLS: Titkosítás túl gyenge" #define MSG_TLS_NEEDED "Hiba, cleartext belépési forma nem engedélyezett.\n" \ "Kérem kapcsolódjon újra SSL/TLS biztonsági módot használva." +#define MSG_ILLEGAL_CHARSET "Illegal charset" Index: src/ls.c =================================================================== --- src/ls.c (revision 1) +++ src/ls.c (revision 15) @@ -399,11 +399,65 @@ return rval; } +#ifdef WITH_RFC2640 +char * charset_fs2client(char *string) +{ + iconv_t cd; + char *outbuf, *output; + + size_t buflen = (strlen(string) * 4 + 1) * sizeof(char); + size_t inlen = strlen(string) * sizeof(char); + outbuf = output = (char *) malloc(buflen); /* long buffer */ + + if(outbuf == NULL) + return NULL; + + memset(outbuf, 0, buflen); + + if(utf8 && strcmp(charset_fs, "utf-8") != 0) + { + cd = iconv_open("utf-8", charset_fs); + if(cd != (iconv_t) -1) + { + if(iconv(cd, (const char* *) &string, &inlen, &outbuf, &buflen) == (size_t) -1) + { + strncpy(output, string, strlen(string)); + } + iconv_close(cd); + } else { + strncpy(output, string, strlen(string)); + } + } else if (!utf8 && strcmp(charset_c, charset_fs) != 0) { + cd = iconv_open(charset_c, charset_fs); + { + if(cd != (iconv_t) -1) + { + if(iconv(cd, (const char* *) &string, &inlen, &outbuf, &buflen) == (size_t) -1) + { + strncpy(output, string, strlen(string)); + } + iconv_close(cd); + } else { + strncpy(output, string, strlen(string)); + } + } + } else { + /* the charset of client is the same as charset of filesystem */ + strncpy(output, string, strlen(string)); + } + + return output; +} +#endif + static void outputfiles(int f) { unsigned int n; struct filename *p; struct filename *q; +#ifdef WITH_RFC2640 + char *c_buf; /* buffer with charset of client */ +#endif if (!head) { return; @@ -472,7 +526,13 @@ pad[1] = '\n'; pad[2] = 0; } +#ifdef WITH_RFC2640 + c_buf = charset_fs2client(q->line); + wrstr(f, c_buf); + free(c_buf); +#else wrstr(f, q->line); +#endif wrstr(f, pad); q = q->right; free(tmp); Index: src/messages_fr_funny.h =================================================================== --- src/messages_fr_funny.h (revision 1) +++ src/messages_fr_funny.h (revision 15) @@ -218,3 +218,4 @@ #define MSG_TLS_WEAK "SSL/TLS: C'est quoi ce chiffrement de tafiole ?" #define MSG_TLS_NEEDED "Ecoute-moi connard : t'as 10 secondes pour configurer\n" \ "ton client de merde en SSL/TLS. En attendant, degage." +#define MSG_ILLEGAL_CHARSET "Illegal charset" Index: src/globals.h =================================================================== --- src/globals.h (revision 1) +++ src/globals.h (revision 15) @@ -172,3 +172,9 @@ GLOBAL0(char *atomic_prefix); #endif + +#ifdef WITH_RFC2640 +GLOBAL(char utf8, 0); /* 0: ascii 1: utf-8 */ +GLOBAL(char *charset_fs, NULL); +GLOBAL(char *charset_c, NULL); +#endif Index: README.UTF-8 =================================================================== --- README.UTF-8 (revision 0) +++ README.UTF-8 (revision 15) @@ -0,0 +1,37 @@ + +* Summary + + The utf-8 patch let pure-ftpd support SOME PARTS of RFC 2640. + It added a command ``OPTS'', and enhance the ``FEAT'' command. + But it didn't implement the LANG command. + + To use this patch, you must specify the encoding of your file system + (where the ftp daemon running on) and the default encoding of client, + if they don't support RFC 2640. + + If a client claimed to use utf-8 encoding, the patch will convert + strings provided by the client into the encoding of file system, + and vice versa. If the client didn't claim to use utf-8 encoding, + the patch will send strings to the client with its charset, which + is configured by ftp administrator. + + Now, SmartFTP (http://www.smartftp.org), FileZilla + (http://filezilla.sourceforge.net/), lftp (http://lftp.yar.ru/) has + implemented RFC 2640. + +* Complication + + Add --with-rfc2640=yes into your arguments of configure. You must install + libiconv (http://www.gnu.org/software/libiconv/) first. + For example, + # ./configure --with-rfc2640=yes + +* Parameters + + -8 charset of file system (default: utf-8) + -9 charset of client (default: utf-8) + +* Any Suggestions + + Please mail to jnlin@csie.nctu.edu.tw. Any suggestions are welcomed. + Index: pure-ftpd.spec =================================================================== --- pure-ftpd.spec (revision 1) +++ pure-ftpd.spec (revision 15) @@ -28,6 +28,7 @@ %define con_tls 0 %define con_sysquotas 1 %define con_rendezvous 0 +%define con_rfc2640 0 %define prefixdef /usr/local %define sysconfdef /etc @@ -67,6 +68,7 @@ %{?with_boring:%define con_boring 1} %{?with_privsep:%define con_privsep 1} %{?with_tls:%define con_tls 1} +%{?with_rfc2640:%define con_rfc2640 1} %{?with_sysquotas:%define con_sysquotas 1} %{?with_rendezvous:%define con_rendezvous 1} @@ -189,6 +191,9 @@ %if %{con_tls} --with-tls \ %endif +%if %{con_rfc2640} + --with-rfc2640 +%endif %if %{con_sysquotas} --with-sysquotas \ %endif Index: configure.ac =================================================================== --- configure.ac (revision 1) +++ configure.ac (revision 15) @@ -72,6 +72,7 @@ AC_CHECK_HEADERS(utime.h) AC_CHECK_HEADERS(openssl/ssl.h) AC_CHECK_HEADERS(CoreFoundation/CoreFoundation.h) +AC_CHECK_HEADERS(iconv.h) AC_SYS_POSIX_TERMIOS if test "x$ac_cv_sys_posix_termios" = "xyes"; then @@ -1237,6 +1238,20 @@ fi fi ]) +AC_ARG_WITH(rfc2640, +[AS_HELP_STRING(--with-rfc2640,Enable RFC 2640 behavior of transfering utf-8 directory and file names (experimental, needs iconv))], +[ if test "x$withval" = "xyes" ; then + with_rfc2640="yes" + fi ]) + +if test "x$with_rfc2640" = "xyes" ; then + if test "x$ac_cv_header_iconv_h" != "xyes" ; then + AC_MSG_ERROR(iconv headers not found.) + fi + AC_CHECK_LIB(iconv, iconv) + AC_DEFINE(WITH_RFC2640,,[Enable RFC2640 behavior]) +fi + AC_ARG_WITH(rendezvous, [AS_HELP_STRING(--with-rendezvous,Enable Rendezvous support on MacOS X (experimental))], [ if test "x$withval" = "xyes" ; then Index: configuration-file/pure-ftpd.conf.in =================================================================== --- configuration-file/pure-ftpd.conf.in (revision 1) +++ configuration-file/pure-ftpd.conf.in (revision 15) @@ -434,3 +434,8 @@ # By default, both IPv4 and IPv6 are enabled. # IPV6Only yes + +# Define charset of local filesystem and remote client if they don't use UTF-8 + +# FileSystemCharset big5 +# ClientCharset big5 Index: configuration-file/pure-config.pl.in =================================================================== --- configuration-file/pure-config.pl.in (revision 1) +++ configuration-file/pure-config.pl.in (revision 15) @@ -48,6 +48,8 @@ ); my %string_switch_for = ( + FileSystemCharset => "-8", + ClientCharset => "-9", SyslogFacility => "-f", FortunesFile => "-F", ForcePassiveIP => "-P", Index: config.h.in =================================================================== --- config.h.in (revision 1) +++ config.h.in (revision 15) @@ -758,6 +758,9 @@ /* Enable TLS */ #undef WITH_TLS +/* Enable RFC2640 behavior */ +#undef WITH_RFC2640 + /* with uploadscript */ #undef WITH_UPLOAD_SCRIPT Index: pure-ftpd.spec.in =================================================================== --- pure-ftpd.spec.in (revision 1) +++ pure-ftpd.spec.in (revision 15) @@ -28,6 +28,7 @@ %define con_tls 0 %define con_sysquotas 1 %define con_rendezvous 0 +%define con_rfc2640 0 %define prefixdef /usr/local %define sysconfdef /etc @@ -67,6 +68,7 @@ %{?with_boring:%define con_boring 1} %{?with_privsep:%define con_privsep 1} %{?with_tls:%define con_tls 1} +%{?with_rfc2640:%define con_rfc2640 1} %{?with_sysquotas:%define con_sysquotas 1} %{?with_rendezvous:%define con_rendezvous 1} @@ -189,6 +191,9 @@ %if %{con_tls} --with-tls \ %endif +%if %{con_rfc2640} + --with-rfc2640 +%endif %if %{con_sysquotas} --with-sysquotas \ %endif