--- Makefile.orig Sat Jul 31 19:07:33 2004 +++ Makefile Tue Nov 9 23:11:51 2004 @@ -15,6 +15,11 @@ DPADD= ${LIBUTIL} ${LIBCRYPT} LDADD= -lutil -lcrypt +.if WITH_ICONV +CFLAGS+=-DWITH_ICONV -I/usr/local/include +LDADD+= -liconv -L/usr/local/lib +.endif + # XXX Kluge! Conversation mechanism needs to be fixed. DPADD+= ${LIBOPIE} ${LIBMD} LDADD+= -lopie -lmd --- ftpcmd.y.orig Sat Jul 31 19:03:17 2004 +++ ftpcmd.y Tue Nov 9 23:14:41 2004 @@ -72,6 +72,10 @@ #include #include +#ifdef WITH_ICONV +#include +#endif + #include "extern.h" #include "pathnames.h" @@ -98,6 +102,12 @@ extern int noretr; extern int noguestretr; extern char *typenames[]; /* defined in included from ftpd.c */ +#ifdef WITH_ICONV +extern int do_iconv; +extern iconv_t conv_rtol; +extern char *local_charset, *remote_charset; +char cbuf2[512]; +#endif off_t restart_point; @@ -487,13 +497,27 @@ } | LIST check_login CRLF { - if ($2) + if ($2) { +#ifdef WITH_ICONV + do_iconv = 1; +#endif retrieve(_PATH_LS " -lgA", ""); +#ifdef WITH_ICONV + do_iconv = 0; +#endif + } } | LIST check_login SP pathstring CRLF { - if ($2) + if ($2) { +#ifdef WITH_ICONV + do_iconv = 1; +#endif retrieve(_PATH_LS " -lgA %s", $4); +#ifdef WITH_ICONV + do_iconv = 0; +#endif + } free($4); } | STAT check_login SP pathname CRLF @@ -1164,6 +1188,17 @@ #include +void log_hexdump(unsigned char *text, unsigned char *data) { + unsigned char temp[2048], temp2[10]; + unsigned char *p; + + sprintf(temp, "%s: ", text); + for (p = data; *p; p++) { + sprintf(temp2, "%02x ", *p); + strcat(temp, temp2); + } + syslog(LOG_DEBUG, temp); +} /* * getline - a hacked up version of fgets to ignore TELNET escape codes. */ @@ -1193,6 +1228,7 @@ sigaddset(&sset, SIGURG); sigprocmask(SIG_BLOCK, &sset, &osset); while ((c = getc(iop)) != EOF) { +#ifndef WITH_ICONV c &= 0377; if (c == IAC) { if ((c = getc(iop)) == EOF) @@ -1219,11 +1255,14 @@ continue; /* ignore command */ } } +#endif *cs++ = c; if (--n <= 0 || c == '\n') break; } +#ifndef WITH_ICONV got_eof: +#endif sigprocmask(SIG_SETMASK, &osset, NULL); if (c == EOF && cs == s) return (NULL); @@ -1244,6 +1283,7 @@ --len; } syslog(LOG_DEBUG, "command: %.*s", len, s); + log_hexdump("command", s); } } return (s); @@ -1261,6 +1301,7 @@ dologout(1); } + static int yylex(void) { @@ -1269,18 +1310,43 @@ struct tab *p; int n; char c; - +#ifdef WITH_ICONV + char *in, *out; + size_t in_bytes, out_bytes; +#endif for (;;) { switch (state) { case CMD: (void) signal(SIGALRM, toolong); (void) alarm((unsigned) timeout); +#ifdef WITH_ICONV + if (getline(local_charset != NULL ? cbuf2 : cbuf, sizeof(cbuf)-1, stdin) == NULL) { +#else if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { +#endif reply(221, "You could at least say goodbye."); dologout(0); } (void) alarm(0); +#ifdef WITH_ICONV + if (local_charset != NULL) { + iconv(conv_rtol, NULL, NULL, NULL, NULL); /* reset converter */ + in = cbuf2; out = cbuf; + in_bytes = strlen(cbuf2); out_bytes = sizeof cbuf; + cbuf[0] = '\0'; + n = iconv(conv_rtol, (const char **) &in, &in_bytes, &out, &out_bytes); + if (n < 0) { + syslog(LOG_ERR, "%s -> %s conversion error: n=%d, errno=%d, at %d (%s)", + remote_charset, local_charset, n, errno, in - cbuf2, in); + strcpy(cbuf, cbuf2); + } else { + *out = '\0'; + } + log_hexdump("cbuf2", cbuf2); + log_hexdump("cbuf ", cbuf); + } +#endif #ifdef SETPROCTITLE if (strncasecmp(cbuf, "PASS", 4) != 0) setproctitle("%s: %s", proctitle, cbuf); --- ftpd.8.orig Sat Jul 3 01:28:48 2004 +++ ftpd.8 Tue Nov 9 23:11:51 2004 @@ -48,6 +48,7 @@ .Op Fl T Ar maxtimeout .Op Fl t Ar timeout .Op Fl u Ar umask +.Op Fl i Ar local:remote .Sh DESCRIPTION The .Nm @@ -104,6 +105,8 @@ .It Fl h Disable printing host-specific information, such as the server software version or hostname, in server messages. +.It Fl i +Perform local <-> remote charset conversion for directory listings and file access. .It Fl l Each successful and failed .Xr ftp 1 --- ftpd.c.orig Sat Jan 22 19:40:11 2005 +++ ftpd.c Tue Feb 1 00:27:59 2005 @@ -101,7 +101,13 @@ #include +#ifdef WITH_ICONV +#include +static char version[] = "Version 6.00LS+iconv by Rojer"; +#else static char version[] = "Version 6.00LS"; +#endif + #undef main extern off_t restart_point; @@ -137,6 +143,7 @@ int form; int stru; /* avoid C keyword */ int mode; +int do_iconv; int usedefault = 1; /* for data transfers */ int pdata = -1; /* for passive mode */ int readonly=0; /* Server is in readonly mode. */ @@ -247,6 +254,13 @@ static char *doublequote(char *); static int *socksetup(int, char *, const char *); + +#ifdef WITH_ICONV +iconv_t conv_ltor, conv_rtol; +char *local_charset, *remote_charset; +#endif + + int main(int argc, char *argv[], char **envp) { @@ -287,7 +301,12 @@ openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); while ((ch = getopt(argc, argv, - "46a:AdDEhlmMoOp:P:rRSt:T:u:UvW")) != -1) { +#ifdef WITH_ICONV + "46a:AdDEhi:lmMoOp:P:rRSt:T:u:UvW" +#else + "46a:AdDEhlmMoOp:P:rRSt:T:u:UvW" +#endif + )) != -1) { switch (ch) { case '4': family = (family == AF_INET6) ? AF_UNSPEC : AF_INET; @@ -321,6 +340,22 @@ hostinfo = 0; break; +#ifdef WITH_ICONV + case 'i': + if ((cp = strchr(optarg, ':')) != NULL) { + local_charset = strdup(optarg); + if (local_charset != NULL) { + cp = strchr(local_charset, ':'); + *cp = '\0'; + remote_charset = cp + 1; + } else { + warnx("strdup failed. not enough memory?!"); + } + } else { + warnx("invalid charset conversion specification for -i"); + } + break; +#endif case 'l': logging++; /* > 1 == extra logging */ break; @@ -406,6 +441,19 @@ inithosts(); #endif +#ifdef WITH_ICONV + /* initialize charset convertors */ + if (local_charset != NULL) { + conv_rtol = iconv_open(local_charset, remote_charset); + conv_ltor = iconv_open(remote_charset, local_charset); + + if (conv_rtol == (iconv_t)(-1) || conv_rtol == (iconv_t)(-1)) { + syslog(LOG_ERR, "failed to setup charset convertors (%s <-> %s), charset conversion disabled", + local_charset, remote_charset); + local_charset = remote_charset = NULL; + } + } +#endif if (daemon_mode) { int *ctl_sock, fd, maxfd = -1, nfds, i; fd_set defreadfds, readfds; @@ -1973,6 +2021,52 @@ return (file); } +#ifdef WITH_ICONV +off_t line_by_line_with_iconv(FILE *from, FILE*to) { + int c, cp; + char lbin[BUFSIZ], lbout[BUFSIZ]; + char *inbuf, *outbuf, *buf; + int i = 0; + off_t bytes = 0; + size_t in_bytes, out_bytes; + + iconv(conv_ltor, NULL, NULL, NULL, NULL); + + while (1) { + c = getc(from); + if (c == '\n' || c == EOF) { + in_bytes = i; out_bytes = sizeof lbout; + inbuf = lbin; outbuf = lbout; + cp = iconv(conv_ltor, + (const char **) &inbuf, &in_bytes, + &outbuf, &out_bytes); + if (cp >= 0) { + /* write out all that was converted */ + for(buf=lbout; + out_bytes < sizeof lbout; + out_bytes++, buf++, bytes++) + putc(*buf, to); + } else { + syslog(LOG_ERR, "%s -> %s conversion error: cp=%d, errno=%d, at %d (%s)", + local_charset, remote_charset, cp, errno, inbuf - lbin, inbuf); + iconv(conv_ltor, NULL, NULL, NULL, NULL); /* reset converter after error */ + } + if (c == '\n') { + if (cp >= 0) { + putc('\r', to); + putc('\n', to); + } + i = 0; + } else { + break; + } + } else { + lbin[i++] = c; + } + } + return bytes; +} +#endif /* * Tranfer the contents of "instr" to "outstr" peer using the appropriate * encapsulation of the data subject to Mode, Structure, and Type. @@ -1990,18 +2084,26 @@ switch (type) { case TYPE_A: - cp = '\0'; - while ((c = getc(instr)) != EOF) { - if (recvurg) - goto got_oob; - byte_count++; - if (c == '\n' && cp != '\r') { - if (ferror(outstr)) - goto data_err; - (void) putc('\r', outstr); +#ifdef WITH_ICONV + if (do_iconv && local_charset != NULL) { + byte_count += line_by_line_with_iconv(instr, outstr); + } else +#endif + { + syslog(LOG_DEBUG, "beginning conventional type a transfer"); + cp = '\0'; + while ((c = getc(instr)) != EOF) { + if (recvurg) + goto got_oob; + byte_count++; + if (c == '\n' && cp != '\r') { + if (ferror(outstr)) + goto data_err; + (void) putc('\r', outstr); + } + (void) putc(c, outstr); + cp = c; } - (void) putc(c, outstr); - cp = c; } if (recvurg) goto got_oob; @@ -2031,6 +2133,8 @@ cnt = offset = 0; + syslog(LOG_DEBUG, "beginning regular binary data transfer"); + while (filesize > 0) { err = sendfile(filefd, netfd, offset, 0, (struct sf_hdtr *) NULL, &cnt, 0); @@ -2074,9 +2178,19 @@ return (-1); } - while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && - write(netfd, buf, cnt) == cnt) - byte_count += cnt; +#ifdef WITH_ICONV + if (do_iconv && local_charset != NULL) { /* conversion only applies to command (ls) output */ + syslog(LOG_DEBUG, "beginning binary iconved transfer"); + byte_count += line_by_line_with_iconv(instr, outstr); + cnt = 0; + } else +#endif + { + syslog(LOG_DEBUG, "beginning old-fashioned, non-translated binary transfer"); + while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && + write(netfd, buf, cnt) == cnt) + byte_count += cnt; + } transflag = 0; (void)free(buf); if (cnt != 0) {