ftpd.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2005 by egnite Software GmbH. All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00021  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  *
00032  */
00033 
00074 #include <sys/version.h>
00075 
00076 #include <sys/socket.h>
00077 #include <netinet/tcp.h>
00078 
00079 #include <string.h>
00080 #include <stdio.h>
00081 #include <stdlib.h>
00082 #include <io.h>
00083 #include <fcntl.h>
00084 #include <sys/stat.h>
00085 #include <dirent.h>
00086 #include <unistd.h>
00087 
00088 #include <pro/ftpd.h>
00089 
00090 #ifdef FTPD_DEBUG
00091 #include <sys/heap.h>
00092 #endif
00093 
00100 
00107 
00113 #ifndef FTP_ROOTPATH
00114 #define FTP_ROOTPATH "PNUT:"
00115 #endif
00116 
00122 #ifndef FTP_DATA_PORT
00123 #define FTP_DATA_PORT   20
00124 #endif
00125 
00128 static char *ftp_root;
00129 static char *ftp_user;
00130 static char *ftp_pass;
00131 
00132 /*
00133  * On Harvard architectures constant strings are stored in ROM, 
00134  * because RAM is usually a scarce resource on these platforms.
00135  */
00136 static prog_char cmd_cwd_P[] = "CWD";
00137 static prog_char cmd_dele_P[] = "DELE";
00138 static prog_char cmd_list_P[] = "LIST";
00139 static prog_char cmd_mkd_P[] = "MKD";
00140 static prog_char cmd_xmkd_P[] = "XMKD";
00141 static prog_char cmd_nlst_P[] = "NLST";
00142 static prog_char cmd_noop_P[] = "NOOP";
00143 static prog_char cmd_pass_P[] = "PASS";
00144 static prog_char cmd_pasv_P[] = "PASV";
00145 static prog_char cmd_port_P[] = "PORT";
00146 static prog_char cmd_pwd_P[] = "PWD";
00147 static prog_char cmd_xpwd_P[] = "XPWD";
00148 static prog_char cmd_quit_P[] = "QUIT";
00149 static prog_char cmd_retr_P[] = "RETR";
00150 static prog_char cmd_rmd_P[] = "RMD";
00151 static prog_char cmd_xrmd_P[] = "XRMD";
00152 static prog_char cmd_stor_P[] = "STOR";
00153 static prog_char cmd_syst_P[] = "SYST";
00154 static prog_char cmd_type_P[] = "TYPE";
00155 static prog_char cmd_user_P[] = "USER";
00156 
00157 static char *mon_name = "JanFebMarAprMayJunJulAugSepOctNovDec";
00158 
00159 static prog_char rep_banner[] = "220 Nut/OS FTP %s ready at %.3s%3d %02d:%02d:%02d\r\n";
00160 
00173 static void SplitCmdArg(char * line, char ** cmd, char ** args)
00174 {
00175     /* Skip leading spaces. */
00176     while (*line && *line <= ' ') {
00177         line++;
00178     }
00179 
00180     /* The first word is the command. Convert it to upper case. */
00181     *cmd = line;
00182     while (*line > ' ') {
00183         if (*line >= (u_char) 'a' && *line <= (u_char) 'z') {
00184             *line -= (u_char) 'a' - 'A';
00185         }
00186         line++;
00187     }
00188 
00189     /* Mark end of the command word. */
00190     if (*line) {
00191         *line++ = '\0';
00192     }
00193 
00194     /* Skip spaces. */
00195     while (*line && *line <= ' ') {
00196         ++line;
00197     }
00198 
00199     /* Arguments start here. */
00200     *args = line;
00201     while (*line && *line != '\r' && *line != '\n') {
00202         line++;
00203     }
00204 
00205     /* Mark end of arguments. */
00206     *line = 0;
00207 }
00208 
00224 static int ParseIpPort(CONST char * arg, u_long * ip, u_short * port)
00225 {
00226     int rc;
00227 
00228     *ip = 0;
00229     *port = 0;
00230     for (rc = 0; rc < 6; rc++) {
00231         if (*arg < '0' || *arg > '9') {
00232             break;
00233         }
00234         if (rc < 4) {
00235             *ip >>= 8;
00236             *ip += atol(arg) << 24;
00237         } else {
00238             *port <<= 8;
00239             *port += atoi(arg);
00240         }
00241         while (*arg && *arg != ',') {
00242             arg++;
00243         }
00244         if (*arg == ',') {
00245             arg++;
00246         }
00247     }
00248     return rc;
00249 }
00250 
00260 int NutFtpRespondOk(FTPSESSION * session, int code)
00261 {
00262     static prog_char fmt_P[] = "%d OK\r\n";
00263 
00264 #ifdef FTPD_DEBUG
00265     printf("\n<'%d OK' ", code);
00266 #endif
00267     fprintf_P(session->ftp_stream, fmt_P, code);
00268     fflush(session->ftp_stream);
00269 
00270     return 0;
00271 }
00272 
00282 int NutFtpRespondBad(FTPSESSION * session, int code)
00283 {
00284     static prog_char fmt_P[] = "%d Failed\r\n";
00285 
00286 #ifdef FTPD_DEBUG
00287     printf("\n<'%d Failed' ", code);
00288 #endif
00289     fprintf_P(session->ftp_stream, fmt_P, code);
00290     fflush(session->ftp_stream);
00291 
00292     return 0;
00293 }
00294 
00304 int NutFtpSendMode(FTPSESSION * session, int binary)
00305 {
00306     static prog_char intro_P[] = "150 Opening ";
00307     static prog_char amode_P[] = "ASCII.\r\n";
00308     static prog_char bmode_P[] = "BINARY.\r\n";
00309 
00310 #ifdef FTPD_DEBUG
00311     printf("\n<'150 Opening %s' ", binary ? "BINARY" : "ASCII");
00312 #endif
00313     fputs_P(intro_P, session->ftp_stream);
00314     fputs_P(binary ? bmode_P : amode_P, session->ftp_stream);
00315     fflush(session->ftp_stream);
00316 
00317     return 0;
00318 }
00319 
00340 char *CreateFullPathName(char *root, char *work, char *path)
00341 {
00342     char *full;
00343     char *cp;
00344     size_t rl = root ? strlen(root) : 0;
00345     size_t wl = work ? strlen(work) : 0;
00346     size_t pl = path ? strlen(path) : 0;
00347 
00348     /* Ignore trailing slashes in root and work. */
00349     if (rl && *(root + rl - 1) == '/') {
00350         rl--;
00351     }
00352     if (wl && *(work + wl - 1) == '/') {
00353         wl--;
00354     }
00355 
00356     if ((full = malloc(rl + wl + pl + 3)) != NULL) {
00357         /* Put the root in front. */
00358         cp = full;
00359         if (rl) {
00360             cp = strcpy(full, root) + rl;
00361         }
00362 
00363         /* If path is relative, prepend the working directory. */
00364         if(pl == 0 || *path != '/') {
00365             if (wl) {
00366                 if (*work != '/') {
00367                     *cp++ = '/';
00368                 }
00369                 cp = strcpy(cp, work) + wl;
00370             }
00371             *cp++ = '/';
00372             rl++;
00373         }
00374 
00375         if (pl) {
00376             *cp = 0;
00377             work = full + rl;
00378 
00379             while (*path) {
00380                 /* Ingore duplicate slashes. */
00381                 if (*path == '/') {
00382                     *cp++ = *path++;
00383                     while (*path == '/') {
00384                         path++;
00385                     }
00386                 }
00387                 /* Ignore single dots. */
00388                 if (*path == '.') {
00389                     path++;
00390                     if (*path == '/') {
00391                         path++;
00392                         continue;
00393                     }
00394                     if (*path == 0) {
00395                         break;
00396                     }
00397                     if (*path == '.') {
00398                         path++;
00399                         if (*path == '/' || *path == 0) {
00400                             if (cp != work) {
00401                                 cp--;
00402                                 while (cp != work) {
00403                                     cp--;
00404                                     if (*cp == '/') {
00405                                         break;
00406                                     }
00407                                 }
00408                             }
00409                             continue;
00410                         }
00411                         path--;
00412                     }
00413                     path--;
00414                 }
00415                 /* Copy the current path component. */
00416                 while (*path && *path != '/') {
00417                     *cp++ = *path++;
00418                 }
00419             }
00420         }
00421         *cp = 0;
00422     }
00423     return full;
00424 }
00425 
00435 TCPSOCKET *NutFtpDataConnect(FTPSESSION * session)
00436 {
00437     TCPSOCKET *sock;
00438     int rc;
00439 
00440     if ((sock = NutTcpCreateSocket()) != 0) {
00441 
00442         if (session->ftp_maxseg) {
00443             NutTcpSetSockOpt(sock, TCP_MAXSEG, &session->ftp_maxseg, sizeof(session->ftp_maxseg));
00444         }
00445         if (session->ftp_passive) {
00446             rc = NutTcpAccept(sock, session->ftp_data_port);
00447         } else {
00448             rc = NutTcpConnect(sock, session->ftp_data_ip, session->ftp_data_port);
00449         }
00450         if (rc) {
00451             NutTcpCloseSocket(sock);
00452             sock = 0;
00453         }
00454     }
00455     return sock;
00456 }
00457 
00471 int NutRegisterFtpRoot(CONST char *path)
00472 {
00473     /* Reset path to default. */
00474     if (path == NULL || *path == 0) {
00475         /* Release previously allocate space. */
00476         if (ftp_root) {
00477             free(ftp_root);
00478         }
00479         if ((ftp_root = malloc(sizeof(FTP_ROOTPATH))) == 0) {
00480             return -1;
00481         }
00482         strcpy(ftp_root, FTP_ROOTPATH);
00483     }
00484 
00485     /* Set a specified path. */
00486     else {
00487         char *cp = strchr(path, ':');
00488         int len = strlen(path);
00489 
00490         /* Make sure that the path fulfills all requirements. */
00491         if (len < 2 || cp == 0 || (*++cp && *cp != '/')) {
00492             return -1;
00493         }
00494 
00495         /* Allocate space for new path, but preserve the current one. */
00496         if ((cp = malloc(len + 1)) == 0) {
00497             return -1;
00498         }
00499 
00500         /* Take over, releasing previously allocate space. */
00501         strcpy(cp, path);
00502         if (ftp_root) {
00503             free(ftp_root);
00504         }
00505         ftp_root = cp;
00506 
00507         /* Chop off an optional trailing slash. */
00508         cp = cp + strlen(cp) - 1;
00509         if (*cp == '/') {
00510             *cp = 0;
00511         }
00512     }
00513     return 0;
00514 }
00515 
00531 int NutRegisterFtpUser(CONST char *user, CONST char *pass)
00532 {
00533     if (ftp_user) {
00534         free(ftp_user);
00535         ftp_user = 0;
00536     }
00537     if (user && *user) {
00538         if ((ftp_user = malloc(strlen(user) + 1)) == 0) {
00539             return -1;
00540         }
00541         strcpy(ftp_user, user);
00542     }
00543     if (ftp_pass) {
00544         free(ftp_pass);
00545         ftp_pass = 0;
00546     }
00547     if (pass && *pass) {
00548         if ((ftp_pass = malloc(strlen(pass) + 1)) == 0) {
00549             return -1;
00550         }
00551         strcpy(ftp_pass, pass);
00552     }
00553     return 0;
00554 }
00555 
00556 
00566 FTPSESSION *NutFtpOpenSession(TCPSOCKET * sock)
00567 {
00568     FTPSESSION *session;
00569 
00570     session = malloc(sizeof(FTPSESSION));
00571 
00572     if (session) {
00573         memset(session, 0, sizeof(FTPSESSION));
00574         session->ftp_data_port = FTP_DATA_PORT;
00575         session->ftp_maxseg = sock->so_mss;
00576         session->ftp_sock = sock;
00577 
00578         /* Set initial working directory. */
00579         if ((session->ftp_cwd = malloc(2)) == 0) {
00580             free(session);
00581             session = 0;
00582         } else {
00583             session->ftp_cwd[0] = '/';
00584             session->ftp_cwd[1] = 0;
00585 
00586             /*
00587              * Open a stream and associate it with the socket, so 
00588              * we can use standard I/O. Note, that socket streams
00589              * currently do support text mode.
00590              */
00591             if ((session->ftp_stream = _fdopen((int) sock, "r+b")) == 0) {
00592                 free(session->ftp_cwd);
00593                 free(session);
00594                 session = 0;
00595             }
00596         }
00597     }
00598     return session;
00599 }
00600 
00607 void NutFtpCloseSession(FTPSESSION * session)
00608 {
00609     if (session) {
00610         /* Close the stream. */
00611         fclose(session->ftp_stream);
00612         if (session->ftp_cwd) {
00613             free(session->ftp_cwd);
00614         }
00615         free(session);
00616     }
00617 }
00618 
00629 int NutFtpProcessCwd(FTPSESSION * session, char *path)
00630 {
00631     struct stat st;
00632     char *cp = path + strlen(ftp_root);
00633 
00634     if (*cp && strcmp(cp, "/")) {
00635         /*
00636          * Check, if the path exists and if this is a directory. 
00637          */
00638         if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
00639             return NutFtpRespondBad(session, 550);
00640         }
00641     }
00642 
00643     /*
00644      * Store the new working directory excluding our root part.
00645      */
00646     if (*cp == 0) {
00647         cp = "/";
00648     }
00649     if (session->ftp_cwd) {
00650         free(session->ftp_cwd);
00651     }
00652     if ((session->ftp_cwd = malloc(strlen(cp) + 1)) == 0) {
00653         return NutFtpRespondBad(session, 550);
00654     }
00655     strcpy(session->ftp_cwd, cp);
00656 
00657     return NutFtpRespondOk(session, 250);
00658 }
00659 
00672 int NutFtpProcessDelete(FTPSESSION * session, char *path)
00673 {
00674     if (unlink(path)) {
00675         return NutFtpRespondBad(session, 550);
00676     }
00677     return NutFtpRespondOk(session, 250);
00678 }
00679 
00697 int NutFtpTransferFile(FTPSESSION * session, char *path, int mode)
00698 {
00699     TCPSOCKET *sock;
00700     int ec = 550;
00701     int fh;
00702 
00703     /* Open the file to send. */
00704     if (mode) {
00705         fh = _open(path, _O_BINARY | _O_RDONLY);
00706     }
00707     /* Open the file to receive. */
00708     else {
00709         fh = _open(path, _O_CREAT | _O_TRUNC);
00710     }
00711 
00712     if (fh != -1) {
00713         /* File status OK, opening data connection */
00714         NutFtpSendMode(session, session->ftp_tran_mode);
00715         if ((sock = NutFtpDataConnect(session)) != 0) {
00716             u_short mss = sock->so_mss;
00717             u_char *buf;
00718 
00719             if (mss < 256) {
00720                 mss = 256;
00721             }
00722             if ((buf = malloc(mss)) != 0) {
00723                 int got;
00724 
00725                 ec = 0;
00726 
00727                 /* Send a file. */
00728                 if (mode) {
00729                     while ((got = _read(fh, buf, mss)) > 0) {
00730                         if (NutTcpSend(sock, buf, got) != got) {
00731                             ec = 551;
00732                             break;
00733                         }
00734                     }
00735                 }
00736 
00737                 /* Receive a file. */
00738                 else {
00739                     while ((got = NutTcpReceive(sock, buf, mss)) > 0) {
00740                         int x;
00741                         if ((x = _write(fh, buf, got)) != got) {
00742                             ec = 552;
00743                             break;
00744                         }
00745                     }
00746                 }
00747                 free(buf);
00748             }
00749             NutTcpCloseSocket(sock);
00750         }
00751         _close(fh);
00752 
00753         /* Remove files received with an error. */
00754         if (mode == 0 && ec) {
00755             unlink(path);
00756         }
00757     }
00758     if (ec) {
00759         return NutFtpRespondBad(session, ec);
00760     }
00761     return NutFtpRespondOk(session, 226);
00762 }
00763 
00776 int NutFtpTransferDirectory(FTPSESSION * session, char *path)
00777 {
00778     TCPSOCKET *sock;
00779     FILE *fp;
00780 
00781     struct stat st;
00782     DIR *dir;
00783     struct dirent *d_ent;
00784     tm *gmt;
00785     u_long size;
00786     int ec = 550;
00787     char *name;
00788 
00789     dir = opendir(path);
00790     if (dir) {
00791         NutFtpSendMode(session, 0);
00792         if ((sock = NutFtpDataConnect(session)) != 0) {
00793             if ((fp = _fdopen((int) sock, "r+b")) != 0) {
00794                 ec = 0;
00795                 while ((d_ent = readdir(dir)) != 0) {
00796                     if (d_ent->d_name[0] == '.') {
00797                         continue;
00798                     }
00799 
00800                     if ((name = malloc(strlen(path) + strlen(d_ent->d_name) + 2)) != 0) {
00801                         sprintf(name, "%s/%s", path, d_ent->d_name);
00802                         if (stat(name, &st) == 0) {
00803                             if (S_ISDIR(st.st_mode)) {
00804                                 fputc('d', fp);
00805                                 size = 0;
00806                             } else {
00807                                 fputc('-', fp);
00808                                 size = st.st_size;
00809                             }
00810                             fprintf(fp, "rw-rw-rw-  1 0 0 %6lu ", size);
00811                             gmt = gmtime(&st.st_mtime);
00812                             //fprintf(fp, "%s %u %u ", mon_name[gmt->tm_mon], gmt->tm_mday, 1900 + gmt->tm_year);
00813                             fprintf(fp, "%.3s %u ", mon_name + gmt->tm_mon * 3, gmt->tm_mday);
00814                             //fprintf(fp, "%02u:%02u:%02u ", gmt->tm_hour, gmt->tm_min, gmt->tm_sec);
00815                             fprintf(fp, "%02u:%02u ", gmt->tm_hour, gmt->tm_min);
00816                             fputs(d_ent->d_name, fp);
00817                             fputs("\r\n", fp);
00818                         }
00819                         free(name);
00820                     }
00821                 }
00822                 fclose(fp);
00823             }
00824             NutTcpCloseSocket(sock);
00825         }
00826         closedir(dir);
00827     }
00828     if (ec) {
00829         return NutFtpRespondBad(session, ec);
00830     }
00831     return NutFtpRespondOk(session, 226);
00832 }
00833 
00846 int NutFtpProcessMkd(FTPSESSION * session, char *path)
00847 {
00848     if (mkdir(path, 0777)) {
00849         return NutFtpRespondBad(session, 550);
00850     }
00851     return NutFtpRespondOk(session, 257);
00852 }
00853 
00866 int NutFtpProcessPass(FTPSESSION * session, char *pass)
00867 {
00868     if (ftp_pass && *ftp_pass) {
00869         if (session->ftp_login != 1 || strcmp(ftp_pass, pass)) {
00870             session->ftp_login = 0;
00871             return NutFtpRespondBad(session, 550);
00872         }
00873     }
00874     session->ftp_login = 2;
00875     return NutFtpRespondOk(session, 230);
00876 }
00877 
00891 int NutFtpProcessPassiv(FTPSESSION * session)
00892 {
00893     u_long ip = session->ftp_sock->so_local_addr;
00894     u_short port = 20;
00895 
00896     fprintf(session->ftp_stream, "227 Passive (%u,%u,%u,%u,%u,%u).\r\n",        /* */
00897             (u_char) ip, (u_char) (ip >> 8), (u_char) (ip >> 16), (u_char) (ip >> 24),  /* */
00898             (u_char) (port >> 8), (u_char) port);
00899     fflush(session->ftp_stream);
00900     session->ftp_passive = 1;
00901 
00902     return 0;
00903 }
00904 
00919 int NutFtpProcessPort(FTPSESSION * session, char *args)
00920 {
00921     if (ParseIpPort(args, &session->ftp_data_ip, &session->ftp_data_port) == 6) {
00922         if (session->ftp_sock->so_remote_addr == session->ftp_data_ip) {
00923             return NutFtpRespondOk(session, 200);
00924         }
00925         return NutFtpRespondBad(session, 425);
00926     }
00927     return NutFtpRespondBad(session, 501);;
00928 }
00929 
00942 int NutFtpProcessPwd(FTPSESSION * session)
00943 {
00944 #ifdef FTPD_DEBUG
00945     printf("\n<'257 \"%s\"' ", session->ftp_cwd);
00946 #endif
00947     fprintf(session->ftp_stream, "257 \"%s\"\r\n", session->ftp_cwd);
00948     return 0;
00949 }
00950 
00963 int NutFtpProcessRmd(FTPSESSION * session, char *path)
00964 {
00965     if (rmdir(path)) {
00966         return NutFtpRespondBad(session, 451);
00967     }
00968     return NutFtpRespondOk(session, 257);
00969 }
00970 
00980 int NutFtpProcessSystem(FTPSESSION * session)
00981 {
00982 #ifdef FTPD_DEBUG
00983     printf("\n<'215 UNIX Type: L8' ");
00984 #endif
00985     fputs("215 UNIX Type: L8\r\n", session->ftp_stream);
00986     return 0;
00987 }
00988 
01003 int NutFtpProcessType(FTPSESSION * session, char *typecode)
01004 {
01005     session->ftp_tran_mode = (*typecode != 'A') && (*typecode != 'a');
01006     return NutFtpRespondOk(session, 200);
01007 }
01008 
01021 int NutFtpProcessUser(FTPSESSION * session, char *user)
01022 {
01023     if (ftp_user && *ftp_user) {
01024         if (session->ftp_login && strcmp(ftp_user, user)) {
01025             session->ftp_login = 0;
01026             return NutFtpRespondBad(session, 550);
01027         }
01028     }
01029 
01030     /* Need a password too. */
01031     if (ftp_pass && *ftp_pass) {
01032         session->ftp_login = 1;
01033         return NutFtpRespondOk(session, 331);
01034     }
01035 
01036     /* No password required. */
01037     session->ftp_login = 2;
01038     return NutFtpRespondOk(session, 230);
01039 }
01040 
01052 int NutFtpProcessRequest(FTPSESSION * session, char *request)
01053 {
01054     int rc = 0;
01055     char *cmd;
01056     char *args;
01057 
01058     /* Split the line into command and argument part. */
01059     SplitCmdArg(request, &cmd, &args);
01060 #ifdef FTPD_DEBUG
01061     printf("\n>'%s %s' ", cmd, args);
01062 #endif
01063 
01064     /* QUIT - Terminate session. */
01065     if (strcmp_P(cmd, cmd_quit_P) == 0) {
01066         NutFtpRespondOk(session, 221);
01067         rc = -1;
01068     }
01069 
01070     /* USER <username> - Check user name. */
01071     else if (strcmp_P(cmd, cmd_user_P) == 0) {
01072         rc = NutFtpProcessUser(session, args);
01073     }
01074 
01075     /* PASS <password> - Check user password. */
01076     else if (strcmp_P(cmd, cmd_pass_P) == 0) {
01077         rc = NutFtpProcessPass(session, args);
01078     } else if (strcmp_P(cmd, cmd_noop_P) == 0) {
01079         NutFtpRespondOk(session, 200);
01080     }
01081     /* Anything else requires a successful login. */
01082     else if (session->ftp_login < 2) {
01083         rc = NutFtpRespondBad(session, 530);
01084     }
01085 
01086     /* Valid login. */
01087     else {
01088 
01089         /* PASV - Prepare passive transfer. */
01090         if (strcmp_P(cmd, cmd_pasv_P) == 0) {
01091             rc = NutFtpProcessPassiv(session);
01092         }
01093 
01094         /* PORT <host-port> - Set data connection. */
01095         else if (strcmp_P(cmd, cmd_port_P) == 0) {
01096             rc = NutFtpProcessPort(session, args);
01097         }
01098 
01099         /* [X]PWD - Send name of current working directory. */
01100         else if (strcmp_P(cmd, cmd_pwd_P) == 0 || strcmp_P(cmd, cmd_xpwd_P) == 0) {
01101             rc = NutFtpProcessPwd(session);
01102         }
01103 
01104         /* SYST - Send system identifier. */
01105         else if (strcmp_P(cmd, cmd_syst_P) == 0) {
01106             rc = NutFtpProcessSystem(session);
01107         }
01108 
01109         /* TYPE <type-code> - Receive transfer mode. */
01110         else if (strcmp_P(cmd, cmd_type_P) == 0) {
01111             rc = NutFtpProcessType(session, args);
01112         }
01113 
01114         /* Commands with path name argument. */
01115         else {
01116             char *path;
01117 
01118             if ((path = CreateFullPathName(ftp_root, session->ftp_cwd, args)) == 0) {
01119                 rc = NutFtpRespondBad(session, 451);
01120             }
01121 
01122             /* CWD <pathname> - Change working directory. */
01123             else if (strcmp_P(cmd, cmd_cwd_P) == 0) {
01124                 rc = NutFtpProcessCwd(session, path);
01125             }
01126 
01127             /* DELE <pathname> - Delete a file. */
01128             else if (strcmp_P(cmd, cmd_dele_P) == 0) {
01129                 rc = NutFtpProcessDelete(session, path);
01130             }
01131 
01132             /* LIST | NLST [<pathname>] - Send list of files in a directory. */
01133             else if (strcmp_P(cmd, cmd_list_P) == 0 || strcmp_P(cmd, cmd_nlst_P) == 0) {
01134                 rc = NutFtpTransferDirectory(session, path);
01135             }
01136 
01137             /* MKD <pathname> - Make a directory. */
01138             else if (strcmp_P(cmd, cmd_mkd_P) == 0 || strcmp_P(cmd, cmd_xmkd_P) == 0) {
01139                 rc = NutFtpProcessMkd(session, path);
01140             }
01141 
01142             /* RETR <pathname> - Send a file to the client. */
01143             else if (strcmp_P(cmd, cmd_retr_P) == 0) {
01144                 rc = NutFtpTransferFile(session, path, 1);
01145             }
01146 
01147             /* RMD <pathname> - Remove a directory. */
01148             else if (strcmp_P(cmd, cmd_rmd_P) == 0 || strcmp_P(cmd, cmd_xrmd_P) == 0) {
01149                 rc = NutFtpProcessRmd(session, path);
01150             }
01151 
01152             /* STOR <pathname> - Receive a file from the client. */
01153             else if (strcmp_P(cmd, cmd_stor_P) == 0) {
01154                 rc = NutFtpTransferFile(session, path, 0);
01155             }
01156 
01157             /* Anything else is an unknown command. */
01158             else {
01159                 rc = NutFtpRespondBad(session, 502);
01160             }
01161 
01162             if (path) {
01163                 free(path);
01164             }
01165         }
01166     }
01167     return rc;
01168 }
01169 
01185 int NutFtpServerSession(TCPSOCKET * sock)
01186 {
01187     int rc = 0;
01188     FTPSESSION *session;
01189     char *buff;
01190     time_t now;
01191     struct _tm *tip;
01192     char ch;
01193 
01194     /* Set the root path if not yet done. */
01195     if (NutRegisterFtpRoot(ftp_root)) {
01196         return -1;
01197     }
01198 
01199     /* Allocate the command line buffer. */
01200     if ((buff = malloc(FTP_MAX_CMDBUF)) == 0) {
01201         return -1;
01202     }
01203 
01204     /* Create a session structure and open a stream. */
01205     if ((session = NutFtpOpenSession(sock)) == 0) {
01206         free(buff);
01207         return -1;
01208     }
01209 
01210     /* Send a welcome banner including system time. */
01211     time(&now);
01212     tip = localtime(&now);
01213 #ifdef FTPD_DEBUG
01214     printf("\n<'");
01215     printf_P(rep_banner, NutVersionString(), &mon_name[tip->tm_mon * 3],        /* */
01216              tip->tm_mday, tip->tm_hour, tip->tm_min, tip->tm_sec);
01217 #endif
01218     fprintf_P(session->ftp_stream, rep_banner, NutVersionString(),      /* */
01219               &mon_name[tip->tm_mon * 3],       /* */
01220               tip->tm_mday, tip->tm_hour, tip->tm_min, tip->tm_sec);
01221 
01222     /* 
01223      * Loop for requests. 
01224      */
01225     while (rc == 0) {
01226 
01227         /* Flush any previous output and read a new command line. */
01228         fflush(session->ftp_stream);
01229         if (fgets(buff, FTP_MAX_CMDBUF, session->ftp_stream) == 0) {
01230             rc = -1;
01231             break;
01232         }
01233 
01234         /* Skip command lines, which are too long. */
01235         if ((ch = *(buff + strlen(buff) - 1)) != '\n' && ch != '\r') {
01236             do {
01237                 if (fgets(buff, FTP_MAX_CMDBUF, session->ftp_stream) == 0) {
01238                     rc = -1;
01239                     break;
01240                 }
01241             } while ((ch = *(buff + strlen(buff) - 1)) != '\n' && ch != '\r');
01242             if (rc == 0) {
01243                 rc = NutFtpRespondBad(session, 500);
01244             }
01245         }
01246 
01247         /* Process the request. */
01248         else {
01249             rc = NutFtpProcessRequest(session, buff);
01250         }
01251     }
01252 
01253     /* Cleanup and return. */
01254     NutFtpCloseSession(session);
01255     free(buff);
01256     return rc;
01257 }
01258 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/