--- Makefile.orig Sat Jul 31 19:07:33 2004 +++ Makefile Sun Feb 27 20:02:34 2005 @@ -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 Feb 19 15:00:49 2005 +++ ftpcmd.y Sun Feb 27 20:02:34 2005 @@ -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; @@ -486,13 +496,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 @@ -1162,6 +1186,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. */ @@ -1191,6 +1226,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) @@ -1217,11 +1253,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); @@ -1242,6 +1281,7 @@ --len; } syslog(LOG_DEBUG, "command: %.*s", len, s); +/* log_hexdump("command", s); */ } } return (s); @@ -1259,6 +1299,7 @@ dologout(1); } + static int yylex(void) { @@ -1267,18 +1308,45 @@ 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(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.c.orig Sat Feb 19 15:19:18 2005 +++ ftpd.c Sun Feb 27 21:40:02 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. */ @@ -266,6 +273,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) { @@ -306,7 +320,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; @@ -340,6 +359,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; @@ -425,6 +460,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; @@ -2020,6 +2068,78 @@ } while (ret == EOF); \ } while (0) +#ifdef WITH_ICONV + +/* line_by_line_with_iconv - line-buffered ASCII (\n->\r\n) transfer with iconv() applied to each line. + returns: -1 - aborted transfer + -2 - data error + N>0 - number of bytes transferred +*/ +static int +line_by_line_with_iconv(FILE *from, FILE*to) { + int c, cp; + char lbin[BUFSIZ*2], lbout[BUFSIZ*4]; + char *inbuf, *outbuf, *buf; + int i = 0; + off_t bytes = 0; + size_t in_bytes, out_bytes; + + /* reset the converter */ + iconv(conv_ltor, NULL, NULL, NULL, NULL); + + while (1) { + c = getc(from); + CHECKOOB(return (-1)) + if (c == EOF && ferror(from)) { /* resume after OOB */ + clearerr(from); + continue; + } + if (c != '\n' && c != EOF) + lbin[i++] = c; + + if (c == '\n' || + (c == EOF && feof(from)) || + i == sizeof lbin) { + if (i > 0) { + 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++) { + FTPD_PUTC(*buf, to, data_err); + } + } else { + syslog(LOG_ERR, "%s -> %s conversion error: c=%d, cp=%d, errno=%d, at %d (%s)", + local_charset, remote_charset, c, cp, errno, inbuf - lbin, inbuf); + iconv(conv_ltor, NULL, NULL, NULL, NULL); /* reset converter after error */ + } + } + if (c == '\n') { + if (cp >= 0) { + if (i == 0 || lbin[i] != '\r') + FTPD_PUTC('\r', to, data_err); + FTPD_PUTC('\n', to, data_err); + } + i = 0; + } else { + if (c == EOF) + break; + /* the char did not get into the buffer, carry it on */ + lbin[i=0] = c; + } + } + } + return bytes; +data_err: + return -2; +} +#endif + /* * Tranfer the contents of "instr" to "outstr" peer using the appropriate * encapsulation of the data subject to Mode, Structure, and Type. @@ -2029,7 +2149,7 @@ static int send_data(FILE *instr, FILE *outstr, size_t blksize, off_t filesize, int isreg) { - int c, cp, filefd, netfd; + int c, cp, filefd, netfd, n; char *buf; STARTXFER; @@ -2037,30 +2157,42 @@ switch (type) { case TYPE_A: - cp = EOF; - for (;;) { - c = getc(instr); - CHECKOOB(return (-1)) - else if (c == EOF && ferror(instr)) - goto file_err; - if (c == EOF) { - if (ferror(instr)) { /* resume after OOB */ - clearerr(instr); - continue; - } - if (feof(instr)) /* EOF */ - break; - syslog(LOG_ERR, "Internal: impossible condition" - " on file after getc()"); - goto file_err; +#ifdef WITH_ICONV + if (do_iconv && local_charset != NULL) { + n = line_by_line_with_iconv(instr, outstr); + switch (n) { + case -1: return -1; /* propagate abort */ + case -2: goto data_err; + default: byte_count += n; } - if (c == '\n' && cp != '\r') { - FTPD_PUTC('\r', outstr, data_err); + } else +#endif + { + cp = EOF; + for (;;) { + c = getc(instr); + CHECKOOB(return (-1)) + else if (c == EOF && ferror(instr)) + goto file_err; + if (c == EOF) { + if (ferror(instr)) { /* resume after OOB */ + clearerr(instr); + continue; + } + if (feof(instr)) /* EOF */ + break; + syslog(LOG_ERR, "Internal: impossible condition" + " on file after getc()"); + goto file_err; + } + if (c == '\n' && cp != '\r') { + FTPD_PUTC('\r', outstr, data_err); + byte_count++; + } + FTPD_PUTC(c, outstr, data_err); byte_count++; + cp = c; } - FTPD_PUTC(c, outstr, data_err); - byte_count++; - cp = c; } #ifdef notyet /* BSD stdio isn't ready for that */ while (fflush(outstr) == EOF) { @@ -2094,6 +2226,8 @@ cnt = offset = 0; + syslog(LOG_DEBUG, "beginning regular binary data transfer"); + while (filesize > 0) { err = sendfile(filefd, netfd, offset, 0, NULL, &cnt, 0); @@ -2129,12 +2263,23 @@ } oldway: +#ifdef WITH_ICONV + if (do_iconv && local_charset != NULL) { /* conversion only applies to command (ls) output */ + syslog(LOG_DEBUG, "beginning binary iconved transfer"); + n = line_by_line_with_iconv(instr, outstr); + switch (n) { + case -1: return -1; /* propagate abort */ + case -2: goto data_err; + default: byte_count += n; + } + } else +#endif + { if ((buf = malloc(blksize)) == NULL) { ENDXFER; reply(451, "Ran out of memory."); return (-1); } - for (;;) { int cnt, len; char *bp; @@ -2163,8 +2308,9 @@ byte_count += cnt; } } - ENDXFER; free(buf); + } /* ifdef with_iconv */ + ENDXFER; reply(226, "Transfer complete."); return (0); default: --- ftpd.8.orig Sat Jul 3 01:28:48 2004 +++ ftpd.8 Sun Feb 27 20:02:34 2005 @@ -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