--- Makefile.orig Sun Oct 10 22:28:58 2004 +++ Makefile Wed Oct 13 03:07:17 2004 @@ -14,6 +14,11 @@ DPADD= ${LIBMD} ${LIBCRYPT} ${LIBUTIL} LDADD= -lmd -lcrypt -lutil +.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} LDADD+= -lopie --- ftpcmd.y.orig Wed Mar 10 02:34:38 2004 +++ ftpcmd.y Wed Oct 13 02:39:40 2004 @@ -71,6 +71,10 @@ #include #include +#ifdef WITH_ICONV +#include +#endif + #include "extern.h" #include "pathnames.h" @@ -97,6 +101,11 @@ extern int noretr; extern int noguestretr; extern char *typenames[]; /* defined in included from ftpd.c */ +#ifdef WITH_ICONV +extern iconv_t conv_rtol; +extern char *local_charset, *remote_charset; +char cbuf2[512]; +#endif off_t restart_point; @@ -1192,6 +1201,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) @@ -1218,6 +1228,7 @@ continue; /* ignore command */ } } +#endif *cs++ = c; if (--n <= 0 || c == '\n') break; @@ -1268,18 +1279,39 @@ 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); + } + } +#endif #ifdef SETPROCTITLE if (strncasecmp(cbuf, "PASS", 4) != 0) setproctitle("%s: %s", proctitle, cbuf); --- ftpd.8.orig Wed Oct 13 02:59:56 2004 +++ ftpd.8 Wed Oct 13 03:03:23 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 @@ -103,6 +104,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 Wed Mar 10 02:35:38 2004 +++ ftpd.c Wed Oct 13 02:32:42 2004 @@ -100,7 +100,13 @@ #include +#ifdef WITH_ICONV +#include +static char version[] = "Version 6.00LS+iconv"; +#else static char version[] = "Version 6.00LS"; +#endif + #undef main extern off_t restart_point; @@ -259,6 +265,11 @@ 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 + static char * curdir(void) { @@ -288,6 +299,7 @@ sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; + #ifdef OLD_SETPROCTITLE /* * Save start and extent of argv for setproctitle. @@ -300,7 +312,12 @@ 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; @@ -333,7 +350,22 @@ case 'h': 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; @@ -425,7 +457,19 @@ * necessary for anonymous ftp's that chroot and can't do it later. */ openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); - +#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; @@ -1960,7 +2004,52 @@ type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 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. @@ -1978,18 +2067,25 @@ 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 (local_charset != NULL && !isreg) { + byte_count += line_by_line_with_iconv(instr, outstr); + } else +#endif + { + 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; @@ -2050,10 +2146,16 @@ perror_reply(451, "Local resource failure: malloc"); return (-1); } - - while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && - write(netfd, buf, cnt) == cnt) - byte_count += cnt; +#ifdef WITH_ICONV + if (local_charset != NULL && !isreg) { /* conversion only applies to command (ls) output */ + byte_count += line_by_line_with_iconv(instr, outstr); + cnt = 0; + } else +#endif + { + 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) {