diff -r 000000000000 -r c6b0df440bee genericunixprotocols/ftpsrv/src/ftpd.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/genericunixprotocols/ftpsrv/src/ftpd.cpp Tue Mar 02 10:33:16 2010 +0530 @@ -0,0 +1,2060 @@ +// +// Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// + +/* + * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * FTP server. + */ + + +#include +#include +#include +#include +#include +#include + +#define FTP_NAMES +#include +#include +#include +#include +#include +#include /* for CHAR_BIT */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pathnames.h" +#include "extern.h" + +// Symbian includes + +#include +#include + +static char version[]="v1.0.1000"; + +extern off_t restart_point; +extern char cbuf[]; + +struct sockaddr_in server_addr; +struct sockaddr_in ctrl_addr; +struct sockaddr_in data_source; +struct sockaddr_in data_dest; +struct sockaddr_in his_addr; +struct sockaddr_in pasv_addr; + +in_addr server_in_addr; + +int daemon_mode = 0; +int data; +jmp_buf errcatch, urgcatch; +int logged_in; +struct passwd *pw; +int debug = 0; +int timeout = 900; /* timeout after 15 minutes of inactivity */ +int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ +int logging = 0; +int replicate = 0; +int high_data_ports = 0; +int anon_only = 0; +int multihome = 0; +int guest; +int stats; +int statfd = -1; +int portcheck = 1; +int dochroot; +int type; +int form; +int stru; /* avoid C keyword */ +int mode; +int doutmp = 0; /* update utmp file */ +int usedefault = 1; /* for data transfers */ +int pdata = -1; /* for passive mode */ +int port = 21; /* default FTP port */ +//sig_atomic_t transflag; +int transflag; +off_t file_size; +off_t byte_count; +#if !defined(CMASK) || CMASK == 0 +#undef CMASK +#define CMASK 027 +#endif +int defumask = CMASK; /* default umask value */ +char tmpline[7]; +char hostname[]="Symbian"; +char* remotehost=hostname; +char* dhostname=hostname; +char *guestpw; +static char ttyline[20]; +char *tty = ttyline; /* for klogin */ +static struct utmp utmp; /* for utmp */ + +TDriveNumber drive; + +char *ident = NULL; + +int root = 1; +FILE* logFile; +/* + * Timeout intervals for retrying connections + * to hosts that don't accept PORT cmds. This + * is a kludge, but given the problems with TCP... + */ +#define SWAITMAX 90 /* wait at most 90 seconds */ +#define SWAITINT 5 /* interval between retries */ + +int swaitmax = SWAITMAX; +int swaitint = SWAITINT; + +#ifdef HASSETPROCTITLE +char proctitle[BUFSIZ]; /* initial part of title */ +#endif /* HASSETPROCTITLE */ + +#define LOG(message) \ + if (logging/* > 1*/) { \ + fprintf(logFile, "LOG_INFO %s\r\n", message); \ + fflush(logFile); } + +#define LOGCMD(cmd, file) \ + if (logging/* > 1*/) { \ + fprintf(logFile, "LOG_INFO %s %s\r\n", cmd, file); \ + fflush(logFile); } +#define LOGCMD2(cmd, file1, file2) \ + if (logging/* > 1*/) { \ + fprintf(logFile, "LOG_INFO %s %s %s\r\n", cmd, file1, file2); \ + fflush(logFile); } +#define LOGBYTES(cmd, file, cnt) \ + if (logging/* > 1*/) { \ + if (cnt == (off_t)-1) \ + fprintf(logFile, "LOG_INFO %s %s\r\n", cmd, file); \ + else \ + fprintf(logFile, "LOG_INFO %s %s = %qd bytes\r\n", cmd, file, (quad_t)(cnt)); \ + fflush(logFile) ; \ + } + +static void ack __P((const char *)); +static FILE *dataconn __P((const char *, off_t, const char *)); +static void dolog __P((struct sockaddr_in *)); +static void end_login __P((void)); +static FILE *getdatasock __P((const char *)); +static int guniquefd __P((const char *, char **)); +static int receive_data __P((FILE *, FILE *)); +static void replydirname __P((const char *, const char *)); +static void send_data __P((FILE *, FILE *, off_t, off_t, int)); + +#if defined(TCPWRAPPERS) +static int check_host __P((struct sockaddr_in *)); +#endif + +in_addr getServerAddress(); +TDriveNumber getSystemDrive(); + +void logxfer __P((const char *, off_t, time_t)); + +#define MAXUSERNAME 64 +char * username[MAXUSERNAME]; +char * userhome[MAXPATHLEN]; + +#undef IP_PORTRANGE + +int +main(int argc, char *argv[], char **envp) +{ + int ch, on = 1; + socklen_t addrlen; + char *cp, line[LINE_MAX]; + FILE *fd; + const char *argstr = "RAdDhlMSt:T:u:UvP:"; + + tzset(); /* in case no timezone database in ~ftp */ + + /* set this here so klogin can use it... */ + (void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid()); + + while ((ch = getopt(argc, argv, argstr)) != -1) { + switch (ch) { + case 'A': + anon_only = 1; + break; + + case 'd': + debug = 1; + break; + + case 'D': + + for(int i=1 /*no name*/; i 1 == extra logging */ + break; + + case 'M': + multihome = 1; + break; + + case 'S': + stats = 1; + break; + + case 't': + timeout = atoi(optarg); + if (maxtimeout < timeout) + maxtimeout = timeout; + break; + + case 'T': + maxtimeout = atoi(optarg); + if (timeout > maxtimeout) + timeout = maxtimeout; + break; + + case 'u': + { + long val = 0; + + val = strtol(optarg, &optarg, 8); + if (*optarg != '\0' || val < 0 || (val & ~ACCESSPERMS)) + printf("bad value for -u"); + else + defumask = val; + break; + } + + case 'U': + doutmp = 1; + break; + + case 'v': + debug = 1; + break; + + default: + printf("unknown flag -%c ignored", optopt); + scanf(""); + break; + } + } + + (void) freopen(_PATH_DEVNULL, "w", stderr); + + /* + * LOG_NDELAY sets up the logging connection immediately, + * necessary for anonymous ftp's that chroot and can't do it later. + */ +#ifndef LOG_FTP +#define LOG_FTP LOG_DAEMON +#endif + + int ctl_sock, fd2; + + server_in_addr = getServerAddress(); + + /* + * Open a socket, bind it to the FTP port, and start + * listening. + */ + ctl_sock = socket(AF_INET, SOCK_STREAM, 0); + if (ctl_sock < 0) { + User::Panic(_L("socket error"),0); + LOG("socket error"); + exit(1); + } + + setsockopt(ctl_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); + + server_addr.sin_family = 0; + server_addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_port = port; + if (bind(ctl_sock, (struct sockaddr *)&server_addr, + sizeof(server_addr))) { + User::Panic(_L("bind error"),0); + LOG("bind error"); + exit(1); + } + if (listen(ctl_sock, 32) < 0) { + User::Panic(_L("listen error"),0); + LOG("listen error"); + exit(1); + } + addrlen = sizeof(his_addr); + fd2 = accept(ctl_sock, (struct sockaddr *)&his_addr, + &addrlen); + + (void) dup2(fd2, 0); + (void) dup2(fd2, 1); + close(ctl_sock); + close(fd2); + + + drive = getSystemDrive(); + + if(logging) + { + char logFileName[MAXPATHLEN]; + + char driveLetter = 'A'+drive; + + sprintf(logFileName,"%c:\\ftpd_%d.log",driveLetter,getpid()); + logFile = fopen(logFileName,"w"); + + fprintf(logFile, "** Log file for the FTP instance with PID %d **\r\n",getpid()); + fflush(logFile); + } + + + addrlen = sizeof(ctrl_addr); + if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { + User::Panic(_L("getsockname error"),0); + exit(1); + } + + //clone to accept new connections + if(replicate) + posix_spawn(NULL,argv[0],NULL,NULL,argv,envp); + + data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); + + dolog(&his_addr); + /* + * Set up default state + */ + data = -1; + type = TYPE_A; + form = FORM_N; + stru = STRU_F; + mode = MODE_S; + tmpline[0] = '\0'; + + /* If logins are disabled, print out the message. */ + if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { + while (fgets(line, sizeof(line), fd) != NULL) { + if ((cp = strchr(line, '\n')) != NULL) + *cp = '\0'; + lreply(530, "%s", line); + } + (void) fflush(stdout); + (void) fclose(fd); + reply(530, "System not available."); + exit(0); + } + if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) { + while (fgets(line, sizeof(line), fd) != NULL) { + if ((cp = strchr(line, '\n')) != NULL) + *cp = '\0'; + lreply(220, "%s", line); + } + (void) fflush(stdout); + (void) fclose(fd); + /* reply(220,) must follow */ + } + + reply(220, "%s FTP server (%s) ready.", + hostname, version); + (void) setjmp(errcatch); + for (;;) + (void) yyparse(); + /* NOTREACHED */ +} + +static int login_attempts; /* number of failed login attempts */ +static int askpasswd; /* had user command, ask for passwd */ +static char curname[16]; /* current USER name */ + +/* + * USER command. + * Sets global passwd pointer pw if named account exists and is acceptable; + * sets askpasswd if a PASS command is expected. If logged in previously, + * need to reset state. If name is "ftp" or "anonymous", the name is not in + * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. + * If account doesn't exist, ask for passwd anyway. Otherwise, check user + * requesting login privileges. Disallow anyone who does not have a standard + * shell as returned by getusershell(). Disallow anyone mentioned in the file + * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. + */ +void user(char *name) +{ + //const char *cp, *shell; + + if (logged_in) { + if (guest) { + reply(530, "Can't change user from guest login."); + return; + } else if (dochroot) { + reply(530, "Can't change user from chroot user."); + return; + } + end_login(); + } + + guest = 0; + + // check password + + pw = (passwd *)malloc(sizeof(pw)); + + strcpy((char*)username,name); + pw->pw_name = (char*)username; + + strcpy((char*)userhome,"/"); + pw->pw_dir = (char*)userhome; + + if (logging) { + strncpy(curname, name, sizeof(curname)-1); + curname[sizeof(curname)-1] = '\0'; + } + + if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) + reply(331,"Guest login ok, type your name as password."); + else + reply(331, "Password required for %s.", name); + + askpasswd = 1; + /* + * Delay before reading passwd after first failed + * attempt to slow down passwd-guessing programs. + */ + if (login_attempts) + sleep((unsigned) login_attempts); +} + + +/* + * Terminate login as previous user, if any, resetting state; + * used when USER command is given or login fails. + */ +static void end_login(void) +{ + (void) seteuid((uid_t)0); + if (logged_in) { + if (doutmp) + logout(utmp.ut_line); + } + pw = NULL; + logged_in = 0; + guest = 0; + dochroot = 0; +} + +void pass(char *passwd) +{ + int rval; + FILE *fd; + + if (logged_in || askpasswd == 0) { + reply(503, "Login with USER first."); + return; + } + askpasswd = 0; + + if (!guest) { /* "ftp" is only account allowed no password */ + + /* + * Authenticate password... + */ + + rval = 0; + + /* + * If rval == 1, the user failed the authentication check + * above. If rval == 0, either Kerberos or local authentication + * succeeded. + */ + if (rval) { + reply(530, "Login incorrect."); + pw = NULL; + return; + } + } else { + /* Save anonymous' password. */ + guestpw = strdup(passwd); + if (guestpw == (char *)NULL) + fatal("Out of memory"); + } + login_attempts = 0; /* this time successful */ + + if (setegid((gid_t)pw->pw_gid) < 0) { + reply(550, "Can't set gid."); + return; + } + (void) initgroups(pw->pw_name, pw->pw_gid); + + /* open utmp before chroot */ + if (doutmp) { + memset((void *)&utmp, 0, sizeof(utmp)); + (void)time(&utmp.ut_time); + (void)strncpy(utmp.ut_name, pw->pw_name, sizeof(utmp.ut_name)); + (void)strncpy(utmp.ut_host, remotehost, sizeof(utmp.ut_host)); + (void)strncpy(utmp.ut_line, ttyline, sizeof(utmp.ut_line)); + login(&utmp); + } + + /* open stats file before chroot */ + if (guest && (stats == 1) && (statfd < 0)) + if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0) + stats = 0; + + logged_in = 1; + dochroot = 0; + + /* + * Set home directory to root + */ + chdir("/"); //root + + /* + * Display a login message, if it exists. + * N.B. reply(230,) must follow the message. + */ + if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) { + char *cp, line[LINE_MAX]; + + while (fgets(line, sizeof(line), fd) != NULL) { + if ((cp = strchr(line, '\n')) != NULL) + *cp = '\0'; + lreply(230, "%s", line); + } + (void) fflush(stdout); + (void) fclose(fd); + } + if (guest) { + if (ident != NULL) + free(ident); + ident = strdup(passwd); + if (ident == (char *)NULL) + fatal("Ran out of memory."); + reply(230, "Guest login ok, access restrictions apply."); + } else { + reply(230, "User %s logged in.", pw->pw_name); + } + (void) umask(defumask); + return; +} + +void internalName(char* output, const char *input) + { + //from /c/aaa.txt to c:\aaa.txt + + strcpy(output,input); + + //root is a special case + if(strcmp(output,"/")==0) + { + return; + } + + for(unsigned int i=0 ; i1) || strcmp(output,".")==0) + { + return; + } + + if(strlen(output)==1 && root) + { + //it's a drive: C\0 -> C:\0 + output[2]=output[1]; + output[1]=':'; + + } + else + { + + // /C/system\n -> C:/system\n + output[0]=output[1]; + output[1]=':'; + } + } + +void externalName(char* output, const char *input) + { + //from c:/aaa.txt to /c/aaa.txt + + strcpy(output,input); + + for(unsigned int i=0 ; i= 0) + return (fdopen(data, mode)); + (void) seteuid((uid_t)0); + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) + goto bad; + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, + (char *) &on, sizeof(on)) < 0) + goto bad; + /* anchor socket to avoid multi-homing problems */ + + data_source.sin_family = AF_INET; + data_source.sin_addr = ctrl_addr.sin_addr; + for (tries = 1; ; tries++) { + if (bind(s, (struct sockaddr *)&data_source, + sizeof(data_source)) >= 0) + break; + if (errno != EADDRINUSE || tries > 10) + goto bad; + sleep(tries); + } + (void) seteuid((uid_t)pw->pw_uid); + + return (fdopen(s, mode)); +bad: + /* Return the real value of errno (close may change it) */ + t = errno; + (void) seteuid((uid_t)pw->pw_uid); + (void) close(s); + errno = t; + return (NULL); +} + +static FILE * dataconn(const char *name, off_t size, const char *mode) +{ + char sizebuf[32]; + FILE *file; + int retry = 0; + + file_size = size; + byte_count = 0; + if (size != (off_t) -1) { + (void) snprintf(sizebuf, sizeof(sizebuf), " (%lld bytes)", + (quad_t) size); + } else + sizebuf[0] = '\0'; + + if (pdata >= 0) { + struct sockaddr_in from; + int s; + socklen_t fromlen = sizeof(from); + + s = accept(pdata, (struct sockaddr *)&from, &fromlen); + + //TODO: OE bug ? sizebuf seems to be changed to 0x18 after accept + //reassigning sizebuf + if (size != (off_t) -1) { + (void) snprintf(sizebuf, sizeof(sizebuf), " (%lld bytes)", + (quad_t) size); + } else + sizebuf[0] = '\0'; + + + if (s < 0) { + reply(425, "Can't open data connection."); + (void) close(pdata); + pdata = -1; + return (NULL); + } + if (ntohs(from.sin_port) < IPPORT_RESERVED) { + perror_reply(425, "Can't build data connection"); + (void) close(pdata); + (void) close(s); + pdata = -1; + return (NULL); + } + if (from.sin_addr.s_addr != his_addr.sin_addr.s_addr) { + perror_reply(435, "Can't build data connection"); + (void) close(pdata); + (void) close(s); + pdata = -1; + return (NULL); + } + (void) close(pdata); + pdata = s; + + if (size != (off_t) -1) { + (void) snprintf(sizebuf, sizeof(sizebuf), " (%lld bytes)", + (quad_t) size); + } else + sizebuf[0] = '\0'; + + reply(150, "Opening %s mode data connection for '%s'%s.", + type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); + return (fdopen(pdata, mode)); + } + if (data >= 0) { + reply(125, "Using existing data connection for '%s'%s.", + name, sizebuf); + usedefault = 1; + return (fdopen(data, mode)); + } + if (usedefault) + data_dest = his_addr; + usedefault = 1; + file = getdatasock(mode); + if (file == NULL) { + reply(425, "Can't create data socket (%s,%d): %s.", + inet_ntoa(data_source.sin_addr), + ntohs(data_source.sin_port), strerror(errno)); + return (NULL); + } + data = fileno(file); + + /* + * attempt to connect to reserved port on client machine; + * this looks like an attack + */ + if (ntohs(data_dest.sin_port) < IPPORT_RESERVED || + ntohs(data_dest.sin_port) == 2049) { /* XXX */ + perror_reply(425, "Can't build data connection"); + (void) fclose(file); + data = -1; + return NULL; + } + if (data_dest.sin_addr.s_addr != his_addr.sin_addr.s_addr) { + perror_reply(435, "Can't build data connection"); + (void) fclose(file); + data = -1; + return NULL; + } + while (connect(data, (struct sockaddr *)&data_dest, + sizeof(data_dest)) < 0) { + if (errno == EADDRINUSE && retry < swaitmax) { + sleep((unsigned) swaitint); + retry += swaitint; + continue; + } + perror_reply(425, "Can't build data connection"); + (void) fclose(file); + data = -1; + return (NULL); + } + reply(150, "Opening %s mode data connection for '%s'%s.", + type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); + return (file); +} + +/* + * Tranfer the contents of "instr" to "outstr" peer using the appropriate + * encapsulation of the data subject to Mode, Structure, and Type. + * + * NB: Form isn't handled. + */ +static void send_data(FILE *instr, FILE *outstr, off_t blksize, off_t filesize, int isreg) +{ + int c, cnt, filefd, netfd; + char *buf, *bp; + size_t len,size; + + transflag++; + if (setjmp(urgcatch)) { + transflag = 0; + return; + } + switch (type) { + + case TYPE_A: + while ((c = getc(instr)) != EOF) { + byte_count++; + (void) putc(c, outstr); + } + fflush(outstr); + transflag = 0; + if (ferror(instr)) + goto file_err; + if (ferror(outstr)) + goto data_err; + reply(226, "Transfer complete."); + return; + + case TYPE_I: + case TYPE_L: + /* + * isreg is only set if we are not doing restart and we + * are sending a regular file + */ + netfd = fileno(outstr); + filefd = fileno(instr); + + if (isreg && filesize < (off_t)16 * 1024 * 1024) { + buf = (char *)mmap(0, filesize, PROT_READ, MAP_SHARED, filefd, (off_t)0); + if (buf==MAP_FAILED || buf==NULL) { + goto oldway; + } + bp = buf; + len = filesize; + do { + cnt = write(netfd, bp, len); + len -= cnt; + bp += cnt; + if (cnt > 0) byte_count += cnt; + } while(cnt > 0 && len > 0); + + transflag = 0; + munmap(buf, (size_t)filesize); + if (cnt < 0) + goto data_err; + reply(226, "Transfer complete."); + return; + } + +oldway: + size = blksize * 16; + + if ((buf = (char *)malloc(size)) == NULL) { + transflag = 0; + perror_reply(451, "Local resource failure: malloc"); + return; + } + + while ((cnt = read(filefd, buf, size)) > 0 && + write(netfd, buf, cnt) == cnt) + byte_count += cnt; + + transflag = 0; + (void)free(buf); + if (cnt != 0) { + if (cnt < 0) + goto file_err; + goto data_err; + } + reply(226, "Transfer complete."); + return; + default: + transflag = 0; + reply(550, "Unimplemented TYPE %d in send_data", type); + return; + } + +data_err: + transflag = 0; + perror_reply(426, "Data connection"); + return; + +file_err: + transflag = 0; + perror_reply(551, "Error on input file"); +} + +/* + * Transfer data from peer to "outstr" using the appropriate encapulation of + * the data subject to Mode, Structure, and Type. + * + * N.B.: Form isn't handled. + */ +static int receive_data(FILE *instr, FILE *outstr) +{ + int c; + int cnt; + volatile int bare_lfs = 0; + char buf[BUFSIZ]; + + transflag++; + if (setjmp(urgcatch)) { + transflag = 0; + return (-1); + } + switch (type) { + + case TYPE_I: + case TYPE_L: + + do { + cnt = read(fileno(instr), buf, sizeof(buf)); + + if (cnt > 0) { + if (write(fileno(outstr), buf, cnt) != cnt) + goto file_err; + byte_count += cnt; + } + } while (cnt > 0); + transflag = 0; + return (0); + + case TYPE_E: + reply(553, "TYPE E not implemented."); + transflag = 0; + return (-1); + + case TYPE_A: + while ((c = getc(instr)) != EOF) { + byte_count++; + (void) putc(c, outstr); + } + fflush(outstr); + if (ferror(outstr)) + goto file_err; + transflag = 0; + if (bare_lfs) { + lreply(226, + "WARNING! %d bare linefeeds received in ASCII mode", + bare_lfs); + (void)printf(" File may not have transferred correctly.\r\n"); + } + return (0); + default: + reply(550, "Unimplemented TYPE %d in receive_data", type); + transflag = 0; + return (-1); + } + +file_err: + transflag = 0; + perror_reply(452, "Error writing file"); + return (-1); +} + +void statfilecmd(char *filename) +{ + FILE *fin; + int c; + char line[LINE_MAX]; + + char adaptedName[MAXPATHLEN]; + internalName(adaptedName,filename); + + (void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename); + fin = ftpd_popen(line, "r"); + + lreply(211, "status of %s:", filename); + while ((c = getc(fin)) != EOF) { + if (c == '\n') { + if (ferror(stdout)){ + perror_reply(421, "control connection"); + (void) ftpd_pclose(fin); + dologout(1); + /* NOTREACHED */ + } + if (ferror(fin)) { + perror_reply(551, filename); + (void) ftpd_pclose(fin); + return; + } + (void) putc('\r', stdout); + } + (void) putc(c, stdout); + } + (void) ftpd_pclose(fin); + + + reply(211, "End of Status"); +} + +void statcmd(void) +{ + struct sockaddr_in *sn; + u_char *a, *p; + + lreply(211, "%s FTP server status:", hostname, version); + printf(" %s\r\n", version); + printf(" Connected to %s", remotehost); + if (!isdigit(remotehost[0])) + printf(" (%s)", inet_ntoa(server_in_addr)); + printf("\r\n"); + if (logged_in) { + if (guest) + printf(" Logged in anonymously\r\n"); + else + printf(" Logged in as %s\r\n", pw->pw_name); + } else if (askpasswd) + printf(" Waiting for password\r\n"); + else + printf(" Waiting for user name\r\n"); + printf(" TYPE: %s", typenames[type]); + if (type == TYPE_A || type == TYPE_E) + printf(", FORM: %s", formnames[form]); + if (type == TYPE_L) +#if CHAR_BIT == 8 + printf(" %d", CHAR_BIT); +#else + printf(" %d", bytesize); /* need definition! */ +#endif + printf("; STRUcture: %s; transfer MODE: %s\r\n", + strunames[stru], modenames[mode]); + if (data != -1) + printf(" Data connection open\r\n"); + else if (pdata != -1) { + printf(" in Passive mode"); + sn = &pasv_addr; + goto printaddr; + } else if (usedefault == 0) { + printf(" PORT"); + sn = &data_dest; +printaddr: + a = (u_char *) &sn->sin_addr; + p = (u_char *) &sn->sin_port; +#define UC(b) (((int) b) & 0xff) + printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), + UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); +#undef UC + } else + printf(" No data connection\r\n"); + + + printf(" System Drive: %c\r\n",'A'+drive); + + reply(211, "End of status"); +} + +void fatal(const char *s) +{ + + reply(451, "Error in server: %s\n", s); + reply(221, "Closing connection due to server error."); + dologout(0); + /* NOTREACHED */ +} + +void +#ifdef __STDC__ +reply(int n, const char *fmt, ...) +#else +reply(int n, char *fmt, va_dcl va_alist) +#endif +{ + va_list ap; +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)printf("%d ", n); + (void)vprintf(fmt, ap); + (void)printf("\r\n"); + (void)fflush(stdout); +} + +void +#ifdef __STDC__ +lreply(int n, const char *fmt, ...) +#else +lreply(n, fmt, va_alist) + int n; + char *fmt; + va_dcl +#endif +{ + va_list ap; +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)printf("%d- ", n); + (void)vprintf(fmt, ap); + (void)printf("\r\n"); + (void)fflush(stdout); +} + +static void ack(const char *s) +{ + + reply(250, "%s command successful.", s); +} + +void nack(const char *s) +{ + + reply(502, "%s command not implemented.", s); +} + +/* ARGSUSED */ + +void yyerror(char *s) +{ + char *cp; + + (void)s; /* ignore argument */ + + if ((cp = strchr(cbuf,'\n'))!=NULL) + *cp = '\0'; + reply(500, "'%s': command not understood.", cbuf); +} + +void dele(char *name) +{ + struct stat st; + + char internalPath[MAXPATHLEN]; + internalName(internalPath,name); + + LOGCMD("delete", name); + if (stat(internalPath, &st) < 0) { + perror_reply(550, name); + return; + } + if ((st.st_mode&S_IFMT) == S_IFDIR) { + if (rmdir(internalPath) < 0) { + perror_reply(550, name); + return; + } + goto done; + } + if (unlink(internalPath) < 0) { + perror_reply(550, name); + return; + } +done: + ack("DELE"); +} + +void cwd(const char *path) +{ + FILE *message; + + LOGCMD("cwd", path); + + char internalPath[MAXPATHLEN]; + + char cwd_path[MAXPATHLEN]; + getcwd(cwd_path, sizeof cwd_path); + + + if( (strlen(path)==1 && path[0]=='/') || (strlen(cwd_path)==2 && strcmp(path,"..")==0)) + { + root=1; + ack("CWD"); + } + else + { + + internalName(internalPath, path); + + if (chdir(internalPath) < 0) + perror_reply(550, path); + else { + + if ((message = fopen(_PATH_CWDMESG, "r")) != NULL) { + char *cp, line[LINE_MAX]; + + while (fgets(line, sizeof(line), message) != NULL) { + if ((cp = strchr(line, '\n')) != NULL) + *cp = '\0'; + lreply(250, "%s", line); + } + (void) fflush(stdout); + (void) fclose(message); + } + root = 0; + ack("CWD"); + } + } +} + +void replydirname(const char *name, const char *message) +{ + char npath[MAXPATHLEN]; + int i; + + for (i = 0; *name != '\0' && i < (int)sizeof(npath) - 1; i++, name++) { + npath[i] = *name; + if (*name == '"') + npath[++i] = '"'; + } + npath[i] = '\0'; + reply(257, "\"%s\" %s", npath, message); +} + +void makedir(char *name) +{ + + LOGCMD("mkdir", name); + + char path[MAXPATHLEN]; + + internalName(path, name); + + if (mkdir(path, 0777) < 0) + perror_reply(550, name); + else + replydirname(name, "directory created."); +} + +void removedir(char *name) +{ + + char path[MAXPATHLEN]; + + internalName(path, name); + + LOGCMD("rmdir", name); + if (rmdir(path) < 0) + perror_reply(550, name); + else + ack("RMD"); +} +void pwd(void) +{ + char path[MAXPATHLEN]; + + if(root) + { + replydirname("/", "is current directory."); + } + else + if (getcwd(path, sizeof path) == (char *)NULL) + { + externalName(path, path); + reply(550, "%s.", path); + } + else + { + externalName(path, path); + replydirname(path, "is current directory."); + } +} + +char * renamefrom(char *name) +{ + struct stat st; + + char internal[MAXPATHLEN]; + + internalName(internal,name); + + if (stat(internal, &st) < 0) { + perror_reply(550, name); + return ((char *)0); + } + reply(350, "File exists, ready for destination name"); + return (name); +} + +void renamecmd(char *from, char *to) +{ + + char internalFrom[MAXPATHLEN]; + char internalTo[MAXPATHLEN]; + + internalName(internalFrom,from); + internalName(internalTo,to); + + LOGCMD2("rename", from, to); + if (rename(internalFrom, internalTo) < 0) + perror_reply(550, "rename"); + else + ack("RNTO"); +} + +static void dolog(struct sockaddr_in *sn) +{ + struct hostent *hp = gethostbyaddr((char *)&sn->sin_addr, + sizeof(struct in_addr), AF_INET); + + if (hp) + (void) strncpy(remotehost, hp->h_name, sizeof(remotehost)-1); + else + (void) strncpy(remotehost, inet_ntoa(sn->sin_addr), + sizeof(remotehost)-1); + remotehost[sizeof(remotehost)-1] = '\0'; +} + +/* + * Record logout in wtmp file + * and exit with supplied status. + */ +void dologout(int status) +{ + transflag = 0; + + if(logging) + { + fclose(logFile); + } + + if (logged_in) { + (void) seteuid((uid_t)0); + if (doutmp) + logout(utmp.ut_line); + } + /* beware of flushing buffers after a SIGPIPE */ + _exit(status); +} + +/* + * Note: a response of 425 is not mentioned as a possible response to + * the PASV command in RFC959. However, it has been blessed as + * a legitimate response by Jon Postel in a telephone conversation + * with Rick Adams on 25 Jan 89. + */ +void passive(void) +{ + socklen_t len; +#ifdef IP_PORTRANGE + int on; +#else + u_short port; +#endif + char *p, *a; + + if (pw == NULL) { + reply(530, "Please login with USER and PASS"); + return; + } + if (pdata >= 0) + close(pdata); + pdata = socket(AF_INET, SOCK_STREAM, 0); + if (pdata < 0) { + perror_reply(425, "Can't open passive connection"); + return; + } + +#ifdef IP_PORTRANGE + on = high_data_ports ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT; + if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, + (char *)&on, sizeof(on)) < 0) + goto pasv_error; +#else +#define FTP_DATA_BOTTOM 40000 +#define FTP_DATA_TOP 44999 + if (high_data_ports) { + for (port = FTP_DATA_BOTTOM; port <= FTP_DATA_TOP; port++) { + pasv_addr = ctrl_addr; + pasv_addr.sin_port = htons(port); + if (bind(pdata, (struct sockaddr *) &pasv_addr, + sizeof(pasv_addr)) == 0) + break; + if (errno != EADDRINUSE) + goto pasv_error; + } + if (port > FTP_DATA_TOP) + goto pasv_error; + } + else +#endif + { + pasv_addr = ctrl_addr; + pasv_addr.sin_port = 0; + if (bind(pdata, (struct sockaddr *)&pasv_addr, + sizeof(pasv_addr)) < 0) + goto pasv_error; + } + + len = sizeof(pasv_addr); + if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) + goto pasv_error; + if (listen(pdata, 1) < 0) + goto pasv_error; + + in_addr addr = getServerAddress(); + + a = (char *) &addr; + p = (char *) &pasv_addr.sin_port; + +#define UC(b) (((int) b) & 0xff) + + reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), + UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); + return; + +pasv_error: + (void) close(pdata); + pdata = -1; + perror_reply(425, "Can't open passive connection"); + return; +} + +/* + * Generate unique name for file with basename "local". + * The file named "local" is already known to exist. + * Generates failure reply on error. + */ +static int guniquefd(const char *local, char **nam) +{ + static char newch[MAXPATHLEN]; + struct stat st; + int count, len, fd; + char *cp; + + cp = strrchr(local, '/'); + if (cp) + *cp = '\0'; + if (stat(cp ? local : ".", &st) < 0) { + perror_reply(553, cp ? local : "."); + return (-1); + } + if (cp) + *cp = '/'; + (void) strncpy(newch, local, sizeof(newch)-1); + newch[sizeof(newch)-1] = '\0'; + len = strlen(newch); + if (len+2+1 >= (int)sizeof(newch)-1) + return (-1); + cp = newch + len; + *cp++ = '.'; + for (count = 1; count < 100; count++) { + (void)snprintf(cp, sizeof(newch) - (cp - newch), "%d", count); + fd = open(newch, O_RDWR|O_CREAT|O_EXCL, 0666); + if (fd == -1) + continue; + if (nam) + *nam = newch; + return (fd); + } + reply(452, "Unique file name cannot be created."); + return (-1); +} + +/* + * Format and send reply containing system error number. + */ +void perror_reply(int code, const char *string) +{ + + reply(code, "%s: %s.", string, strerror(errno)); +} + +static const char *onefile[] = { + "", + 0 +}; + + +void send_file_list(const char *whichf, int simple) +{ + struct stat st; + DIR *dirp = NULL; + struct dirent *dir; + FILE *volatile dout = NULL; + char const *const *volatile dirlist; + const char *dirname; + //volatile int simple = 0; + volatile int freeglob = 0; + glob_t gl; + char time[26]; + + char internal_whichf[MAXPATHLEN]; + + //remove modifier for hidden files "-a" + if(whichf[0]=='-' && whichf[1]=='a') + { + strcpy(internal_whichf,whichf+2); + + //if target location is empty, set "." + if(internal_whichf[0]==0) + strcpy(internal_whichf,"."); + + internalName(internal_whichf,internal_whichf); + } + else + { + internalName(internal_whichf,whichf); + } + + + LOGCMD("send_file_list",internal_whichf); + + /* XXX: should the { go away if __linux__? */ + + if((root && strcmp(internal_whichf, ".")==0) || strcmp(internal_whichf, "/")==0 ) + { + if (dout == NULL) { + dout = dataconn("file list", (off_t)-1, + "w"); + if (dout == NULL) + goto out; + transflag++; + } + + //get the drives list + RFs fileSystem; + fileSystem.Connect(); + + TDriveList driveList; + fileSystem.DriveList(driveList); + + + if (simple) + { + for(int i=0; id_name[0] == '.' && dir->d_namlen == 1) + continue; + if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && + dir->d_namlen == 2) + continue; + + snprintf(nbuf, sizeof(nbuf), "%s/%s", dirname, + dir->d_name); + + /* + * We have to do a stat to insure it's + * not a directory or special file. + */ + stat(nbuf, &st); + + if (simple){ + if (dout == NULL) { + dout = dataconn("file list", (off_t)-1, + "w"); + if (dout == NULL) + goto out; + transflag++; + } + + fprintf(dout, "%s%s\n", dir->d_name, type == TYPE_A ? "\r" : ""); + + byte_count += strlen(nbuf) + 1; + } + else //!simple + { + + if (dout == NULL) { + dout = dataconn("file list", (off_t)-1, + "w"); + if (dout == NULL) + goto out; + transflag++; + } + + ctime_r(&st.st_atime,time); + //"Mon Dec 10 17:35:44 2007" + + char month[4]; + month[0] = time[4]; //'D' + month[1] = time[5]; //'e' + month[2] = time[6]; //'c' + month[3] = '\0'; + + char day[3]; + day[0] = time[8]; //'1' + day[1] = time[9]; //'0' + day[2] = '\0'; + + char year[5]; + year[0] = time[20]; //'2' + year[1] = time[21]; //'0' + year[2] = time[22]; //'0' + year[3] = time[23]; //'7' + year[4] = '\0'; + + + fprintf(dout, "%c%c%c%c%c%c%c%c%c%c 1 ftp ftp %9jd %s %s %s %s%s\n", + S_ISDIR(st.st_mode) ? 'd' : '-', + st.st_mode & S_IRUSR ? 'r' : '-', + st.st_mode & S_IWUSR ? 'w' : '-', + st.st_mode & S_IXUSR ? 'x' : '-', + st.st_mode & S_IRGRP ? 'r' : '-', + st.st_mode & S_IWGRP ? 'w' : '-', + st.st_mode & S_IXGRP ? 'x' : '-', + st.st_mode & S_IROTH ? 'r' : '-', + st.st_mode & S_IWOTH ? 'w' : '-', + st.st_mode & S_IXOTH ? 'x' : '-', + st.st_size, + month, + day, + year, + dir->d_name, + type == TYPE_A ? "\r" : "" + ); + + } + } + (void) closedir(dirp); + } + } + if (dout == NULL) + reply(550, "No files found."); + else if (ferror(dout) != 0) + perror_reply(550, "Data connection"); + else + reply(226, "Transfer complete."); + + transflag = 0; + if (dout != NULL) + (void) fclose(dout); + data = -1; + pdata = -1; +out: + if (freeglob) { + freeglob = 0; + globfree(&gl); + } +} + +void logxfer(const char *name, off_t size, time_t start) +{ + char buf[400 + MAXHOSTNAMELEN*4 + MAXPATHLEN*4]; + char dir[MAXPATHLEN], path[MAXPATHLEN], rpath[MAXPATHLEN]; + char vremotehost[MAXHOSTNAMELEN*4], vpath[MAXPATHLEN*4]; + char *vpw; + time_t now; + + if ((statfd >= 0) && (getcwd(dir, sizeof(dir)) != NULL)) { + time(&now); + + vpw = (char *)malloc(strlen((guest) ? guestpw : pw->pw_name)*4+1); + if (vpw == NULL) + return; + + snprintf(path, sizeof path, "%s/%s", dir, name); + if (realpath(path, rpath) == NULL) { + strncpy(rpath, path, sizeof rpath-1); + rpath[sizeof rpath-1] = '\0'; + } + + snprintf(buf, sizeof(buf), + "%.24s %ld %s %qd %s %c %s %c %c %s ftp %d %s %s\n", + ctime(&now), (long)(now - start + (now == start)), + vremotehost, (long long) size, vpath, + ((type == TYPE_A) ? 'a' : 'b'), "*" /* none yet */, + 'o', ((guest) ? 'a' : 'r'), + vpw, 0 /* none yet */, + ((guest) ? "*" : pw->pw_name), + dhostname); + write(statfd, buf, strlen(buf)); + free(vpw); + } +} + +TDriveNumber getSystemDrive() + { + _LIT(KFileSrvDll, "efsrv.dll"); + + TDriveNumber defaultSysDrive(EDriveC); + RLibrary pluginLibrary; + TInt pluginErr = pluginLibrary.Load(KFileSrvDll); + + if (pluginErr == KErrNone) + { + typedef TDriveNumber(*fun1)(); + fun1 sysdrive; + + #ifdef __EABI__ + sysdrive = (fun1)pluginLibrary.Lookup(336); + #else + sysdrive = (fun1)pluginLibrary.Lookup(304); + #endif + + if(sysdrive!=NULL) + { + defaultSysDrive = sysdrive(); + } + } + pluginLibrary.Close(); + return defaultSysDrive; + } + +in_addr getServerAddress() + { + + in_addr address; + + //default adddress to be returned if failure + inet_aton("0.0.0.0", &address); + + RSocketServ rSockServer; + if(KErrNone==rSockServer.Connect()) + { + RConnection rConnect; + if(KErrNone==rConnect.Open(rSockServer)) + { + TRequestStatus status; + rConnect.Start(status); + + User::WaitForRequest(status); + } + } + + TAutoClose ss; + User::LeaveIfError(ss.iObj.Connect()); + ss.PushL(); + + TAutoClose sock; + User::LeaveIfError(sock.iObj.Open(ss.iObj, _L("udp"))); + sock.PushL(); + + User::LeaveIfError(sock.iObj.SetOpt(KSoInetEnumInterfaces, KSolInetIfCtrl)); + + TProtocolDesc in; + User::LeaveIfError(sock.iObj.Info(in)); + TPckgBuf info, next; + + TInt res=sock.iObj.GetOpt(KSoInetNextInterface, KSolInetIfCtrl, info); + + while(res==KErrNone) + { + + res=sock.iObj.GetOpt(KSoInetNextInterface, KSolInetIfCtrl, next); + if(info().iState != EIfUp || info().iFeatures&KIfIsLoopback || info().iName.Left(4) == _L("eth6")) + { + info = next; continue; + } + + TName address_descriptor; + char address_string[16]; //xxx.xxx.xxx.xxx\0 + + info().iAddress.Output(address_descriptor); + + int i=0; + + for(i=0; i