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 
00077 #include <sys/version.h>
00078 
00079 #include <sys/socket.h>
00080 #include <netinet/tcp.h>
00081 
00082 #include <string.h>
00083 #include <stdio.h>
00084 #include <stdlib.h>
00085 #include <io.h>
00086 #include <fcntl.h>
00087 #include <sys/stat.h>
00088 #include <dirent.h>
00089 #include <unistd.h>
00090 
00091 #include <pro/ftpd.h>
00092 
00093 #ifdef FTPD_DEBUG
00094 #include <sys/heap.h>
00095 #endif
00096 
00103 
00110 
00116 #ifndef FTP_ROOTPATH
00117 #define FTP_ROOTPATH "PNUT:"
00118 #endif
00119 
00125 #ifndef FTP_DATA_PORT
00126 #define FTP_DATA_PORT   20
00127 #endif
00128 
00131 static char *ftp_root;
00132 static char *ftp_user;
00133 static char *ftp_pass;
00134 
00135 /*
00136  * On Harvard architectures constant strings are stored in ROM, 
00137  * because RAM is usually a scarce resource on these platforms.
00138  */
00139 static prog_char cmd_cwd_P[] = "CWD";
00140 static prog_char cmd_dele_P[] = "DELE";
00141 static prog_char cmd_list_P[] = "LIST";
00142 static prog_char cmd_mkd_P[] = "MKD";
00143 static prog_char cmd_xmkd_P[] = "XMKD";
00144 static prog_char cmd_nlst_P[] = "NLST";
00145 static prog_char cmd_noop_P[] = "NOOP";
00146 static prog_char cmd_pass_P[] = "PASS";
00147 static prog_char cmd_pasv_P[] = "PASV";
00148 static prog_char cmd_port_P[] = "PORT";
00149 static prog_char cmd_pwd_P[] = "PWD";
00150 static prog_char cmd_xpwd_P[] = "XPWD";
00151 static prog_char cmd_quit_P[] = "QUIT";
00152 static prog_char cmd_retr_P[] = "RETR";
00153 static prog_char cmd_rmd_P[] = "RMD";
00154 static prog_char cmd_xrmd_P[] = "XRMD";
00155 static prog_char cmd_stor_P[] = "STOR";
00156 static prog_char cmd_syst_P[] = "SYST";
00157 static prog_char cmd_type_P[] = "TYPE";
00158 static prog_char cmd_user_P[] = "USER";
00159 
00160 static char *mon_name = "JanFebMarAprMayJunJulAugSepOctNovDec";
00161 
00162 static prog_char rep_banner[] = "220 Nut/OS FTP %s ready at %.3s%3d %02d:%02d:%02d\r\n";
00163 
00176 static void SplitCmdArg(char * line, char ** cmd, char ** args)
00177 {
00178     /* Skip leading spaces. */
00179     while (*line && *line <= ' ') {
00180         line++;
00181     }
00182 
00183     /* The first word is the command. Convert it to upper case. */
00184     *cmd = line;
00185     while (*line > ' ') {
00186         if (*line >= (uint8_t) 'a' && *line <= (uint8_t) 'z') {
00187             *line -= (uint8_t) 'a' - 'A';
00188         }
00189         line++;
00190     }
00191 
00192     /* Mark end of the command word. */
00193     if (*line) {
00194         *line++ = '\0';
00195     }
00196 
00197     /* Skip spaces. */
00198     while (*line && *line <= ' ') {
00199         ++line;
00200     }
00201 
00202     /* Arguments start here. */
00203     *args = line;
00204     while (*line && *line != '\r' && *line != '\n') {
00205         line++;
00206     }
00207 
00208     /* Mark end of arguments. */
00209     *line = 0;
00210 }
00211 
00227 static int ParseIpPort(CONST char * arg, uint32_t * ip, uint16_t * port)
00228 {
00229     int rc;
00230 
00231     *ip = 0;
00232     *port = 0;
00233     for (rc = 0; rc < 6; rc++) {
00234         if (*arg < '0' || *arg > '9') {
00235             break;
00236         }
00237         if (rc < 4) {
00238             *ip >>= 8;
00239             *ip += atol(arg) << 24;
00240         } else {
00241             *port <<= 8;
00242             *port += atoi(arg);
00243         }
00244         while (*arg && *arg != ',') {
00245             arg++;
00246         }
00247         if (*arg == ',') {
00248             arg++;
00249         }
00250     }
00251     return rc;
00252 }
00253 
00263 int NutFtpRespondOk(FTPSESSION * session, int code)
00264 {
00265     static prog_char fmt_P[] = "%d OK\r\n";
00266 
00267 #ifdef FTPD_DEBUG
00268     printf("\n<'%d OK' ", code);
00269 #endif
00270     fprintf_P(session->ftp_stream, fmt_P, code);
00271     fflush(session->ftp_stream);
00272 
00273     return 0;
00274 }
00275 
00285 int NutFtpRespondBad(FTPSESSION * session, int code)
00286 {
00287     static prog_char fmt_P[] = "%d Failed\r\n";
00288 
00289 #ifdef FTPD_DEBUG
00290     printf("\n<'%d Failed' ", code);
00291 #endif
00292     fprintf_P(session->ftp_stream, fmt_P, code);
00293     fflush(session->ftp_stream);
00294 
00295     return 0;
00296 }
00297 
00307 int NutFtpSendMode(FTPSESSION * session, int binary)
00308 {
00309     static prog_char intro_P[] = "150 Opening ";
00310     static prog_char amode_P[] = "ASCII.\r\n";
00311     static prog_char bmode_P[] = "BINARY.\r\n";
00312 
00313 #ifdef FTPD_DEBUG
00314     printf("\n<'150 Opening %s' ", binary ? "BINARY" : "ASCII");
00315 #endif
00316     fputs_P(intro_P, session->ftp_stream);
00317     fputs_P(binary ? bmode_P : amode_P, session->ftp_stream);
00318     fflush(session->ftp_stream);
00319 
00320     return 0;
00321 }
00322 
00343 char *CreateFullPathName(char *root, char *work, char *path)
00344 {
00345     char *full;
00346     char *cp;
00347     size_t rl = root ? strlen(root) : 0;
00348     size_t wl = work ? strlen(work) : 0;
00349     size_t pl = path ? strlen(path) : 0;
00350 
00351     /* Ignore trailing slashes in root and work. */
00352     if (rl && *(root + rl - 1) == '/') {
00353         rl--;
00354     }
00355     if (wl && *(work + wl - 1) == '/') {
00356         wl--;
00357     }
00358 
00359     if ((full = malloc(rl + wl + pl + 3)) != NULL) {
00360         /* Put the root in front. */
00361         cp = full;
00362         if (rl) {
00363             cp = strcpy(full, root) + rl;
00364         }
00365 
00366         /* If path is relative, prepend the working directory. */
00367         if(pl == 0 || *path != '/') {
00368             if (wl) {
00369                 if (*work != '/') {
00370                     *cp++ = '/';
00371                 }
00372                 cp = strcpy(cp, work) + wl;
00373             }
00374             *cp++ = '/';
00375             rl++;
00376         }
00377 
00378         if (pl) {
00379             *cp = 0;
00380             work = full + rl;
00381 
00382             while (*path) {
00383                 /* Ingore duplicate slashes. */
00384                 if (*path == '/') {
00385                     *cp++ = *path++;
00386                     while (*path == '/') {
00387                         path++;
00388                     }
00389                 }
00390                 /* Ignore single dots. */
00391                 if (*path == '.') {
00392                     path++;
00393                     if (*path == '/') {
00394                         path++;
00395                         continue;
00396                     }
00397                     if (*path == 0) {
00398                         break;
00399                     }
00400                     if (*path == '.') {
00401                         path++;
00402                         if (*path == '/' || *path == 0) {
00403                             if (cp != work) {
00404                                 cp--;
00405                                 while (cp != work) {
00406                                     cp--;
00407                                     if (*cp == '/') {
00408                                         break;
00409                                     }
00410                                 }
00411                             }
00412                             continue;
00413                         }
00414                         path--;
00415                     }
00416                     path--;
00417                 }
00418                 /* Copy the current path component. */
00419                 while (*path && *path != '/') {
00420                     *cp++ = *path++;
00421                 }
00422             }
00423         }
00424         *cp = 0;
00425     }
00426     return full;
00427 }
00428 
00438 TCPSOCKET *NutFtpDataConnect(FTPSESSION * session)
00439 {
00440     TCPSOCKET *sock;
00441     int rc;
00442 
00443     if ((sock = NutTcpCreateSocket()) != 0) {
00444 
00445         if (session->ftp_maxseg) {
00446             NutTcpSetSockOpt(sock, TCP_MAXSEG, &session->ftp_maxseg, sizeof(session->ftp_maxseg));
00447         }
00448         if (session->ftp_passive) {
00449             rc = NutTcpAccept(sock, session->ftp_data_port);
00450         } else {
00451             rc = NutTcpConnect(sock, session->ftp_data_ip, session->ftp_data_port);
00452         }
00453         if (rc) {
00454             NutTcpCloseSocket(sock);
00455             sock = 0;
00456         }
00457     }
00458     return sock;
00459 }
00460 
00474 int NutRegisterFtpRoot(CONST char *path)
00475 {
00476     /* Reset path to default. */
00477     if (path == NULL || *path == 0) {
00478         /* Release previously allocate space. */
00479         if (ftp_root) {
00480             free(ftp_root);
00481         }
00482         if ((ftp_root = malloc(sizeof(FTP_ROOTPATH))) == 0) {
00483             return -1;
00484         }
00485         strcpy(ftp_root, FTP_ROOTPATH);
00486     }
00487 
00488     /* Set a specified path. */
00489     else {
00490         char *cp = strchr(path, ':');
00491         int len = strlen(path);
00492 
00493         /* Make sure that the path fulfills all requirements. */
00494         if (len < 2 || cp == 0 || (*++cp && *cp != '/')) {
00495             return -1;
00496         }
00497 
00498         /* Allocate space for new path, but preserve the current one. */
00499         if ((cp = malloc(len + 1)) == 0) {
00500             return -1;
00501         }
00502 
00503         /* Take over, releasing previously allocate space. */
00504         strcpy(cp, path);
00505         if (ftp_root) {
00506             free(ftp_root);
00507         }
00508         ftp_root = cp;
00509 
00510         /* Chop off an optional trailing slash. */
00511         cp = cp + strlen(cp) - 1;
00512         if (*cp == '/') {
00513             *cp = 0;
00514         }
00515     }
00516     return 0;
00517 }
00518 
00534 int NutRegisterFtpUser(CONST char *user, CONST char *pass)
00535 {
00536     if (ftp_user) {
00537         free(ftp_user);
00538         ftp_user = 0;
00539     }
00540     if (user && *user) {
00541         if ((ftp_user = malloc(strlen(user) + 1)) == 0) {
00542             return -1;
00543         }
00544         strcpy(ftp_user, user);
00545     }
00546     if (ftp_pass) {
00547         free(ftp_pass);
00548         ftp_pass = 0;
00549     }
00550     if (pass && *pass) {
00551         if ((ftp_pass = malloc(strlen(pass) + 1)) == 0) {
00552             return -1;
00553         }
00554         strcpy(ftp_pass, pass);
00555     }
00556     return 0;
00557 }
00558 
00559 
00569 FTPSESSION *NutFtpOpenSession(TCPSOCKET * sock)
00570 {
00571     FTPSESSION *session;
00572 
00573     session = malloc(sizeof(FTPSESSION));
00574 
00575     if (session) {
00576         memset(session, 0, sizeof(FTPSESSION));
00577         session->ftp_data_port = FTP_DATA_PORT;
00578         session->ftp_maxseg = sock->so_mss;
00579         session->ftp_sock = sock;
00580 
00581         /* Set initial working directory. */
00582         if ((session->ftp_cwd = malloc(2)) == 0) {
00583             free(session);
00584             session = 0;
00585         } else {
00586             session->ftp_cwd[0] = '/';
00587             session->ftp_cwd[1] = 0;
00588 
00589             /*
00590              * Open a stream and associate it with the socket, so 
00591              * we can use standard I/O. Note, that socket streams
00592              * currently do support text mode.
00593              */
00594             if ((session->ftp_stream = _fdopen((int) sock, "r+b")) == 0) {
00595                 free(session->ftp_cwd);
00596                 free(session);
00597                 session = 0;
00598             }
00599         }
00600     }
00601     return session;
00602 }
00603 
00610 void NutFtpCloseSession(FTPSESSION * session)
00611 {
00612     if (session) {
00613         /* Close the stream. */
00614         fclose(session->ftp_stream);
00615         if (session->ftp_cwd) {
00616             free(session->ftp_cwd);
00617         }
00618         free(session);
00619     }
00620 }
00621 
00632 int NutFtpProcessCwd(FTPSESSION * session, char *path)
00633 {
00634     struct stat st;
00635     char *cp = path + strlen(ftp_root);
00636 
00637     if (*cp && strcmp(cp, "/")) {
00638         /*
00639          * Check, if the path exists and if this is a directory. 
00640          */
00641         if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
00642             return NutFtpRespondBad(session, 550);
00643         }
00644     }
00645 
00646     /*
00647      * Store the new working directory excluding our root part.
00648      */
00649     if (*cp == 0) {
00650         cp = "/";
00651     }
00652     if (session->ftp_cwd) {
00653         free(session->ftp_cwd);
00654     }
00655     if ((session->ftp_cwd = malloc(strlen(cp) + 1)) == 0) {
00656         return NutFtpRespondBad(session, 550);
00657     }
00658     strcpy(session->ftp_cwd, cp);
00659 
00660     return NutFtpRespondOk(session, 250);
00661 }
00662 
00675 int NutFtpProcessDelete(FTPSESSION * session, char *path)
00676 {
00677     if (unlink(path)) {
00678         return NutFtpRespondBad(session, 550);
00679     }
00680     return NutFtpRespondOk(session, 250);
00681 }
00682 
00700 int NutFtpTransferFile(FTPSESSION * session, char *path, int mode)
00701 {
00702     TCPSOCKET *sock;
00703     int ec = 550;
00704     int fh;
00705 
00706     /* Open the file to send. */
00707     if (mode) {
00708         fh = _open(path, _O_BINARY | _O_RDONLY);
00709     }
00710     /* Open the file to receive. */
00711     else {
00712         fh = _open(path, _O_CREAT | _O_TRUNC);
00713     }
00714 
00715     if (fh != -1) {
00716         /* File status OK, opening data connection */
00717         NutFtpSendMode(session, session->ftp_tran_mode);
00718         if ((sock = NutFtpDataConnect(session)) != 0) {
00719             uint16_t mss = sock->so_mss;
00720             uint8_t *buf;
00721 
00722             if (mss < 256) {
00723                 mss = 256;
00724             }
00725             if ((buf = malloc(mss)) != 0) {
00726                 int got;
00727 
00728                 ec = 0;
00729 
00730                 /* Send a file. */
00731                 if (mode) {
00732                     while ((got = _read(fh, buf, mss)) > 0) {
00733                         if (NutTcpSend(sock, buf, got) != got) {
00734                             ec = 551;
00735                             break;
00736                         }
00737                     }
00738                 }
00739 
00740                 /* Receive a file. */
00741                 else {
00742                     while ((got = NutTcpReceive(sock, buf, mss)) > 0) {
00743                         int x;
00744                         if ((x = _write(fh, buf, got)) != got) {
00745                             ec = 552;
00746                             break;
00747                         }
00748                     }
00749                 }
00750                 free(buf);
00751             }
00752             NutTcpCloseSocket(sock);
00753         }
00754         _close(fh);
00755 
00756         /* Remove files received with an error. */
00757         if (mode == 0 && ec) {
00758             unlink(path);
00759         }
00760     }
00761     if (ec) {
00762         return NutFtpRespondBad(session, ec);
00763     }
00764     return NutFtpRespondOk(session, 226);
00765 }
00766 
00779 int NutFtpTransferDirectory(FTPSESSION * session, char *path)
00780 {
00781     TCPSOCKET *sock;
00782     FILE *fp;
00783 
00784     struct stat st;
00785     DIR *dir;
00786     struct dirent *d_ent;
00787     tm *gmt;
00788     uint32_t size;
00789     int ec = 550;
00790     char *name;
00791 
00792     dir = opendir(path);
00793     if (dir) {
00794         NutFtpSendMode(session, 0);
00795         if ((sock = NutFtpDataConnect(session)) != 0) {
00796             if ((fp = _fdopen((int) sock, "r+b")) != 0) {
00797                 ec = 0;
00798                 while ((d_ent = readdir(dir)) != 0) {
00799                     if (d_ent->d_name[0] == '.') {
00800                         continue;
00801                     }
00802 
00803                     if ((name = malloc(strlen(path) + strlen(d_ent->d_name) + 2)) != 0) {
00804                         sprintf(name, "%s/%s", path, d_ent->d_name);
00805                         if (stat(name, &st) == 0) {
00806                             if (S_ISDIR(st.st_mode)) {
00807                                 fputc('d', fp);
00808                                 size = 0;
00809                             } else {
00810                                 fputc('-', fp);
00811                                 size = st.st_size;
00812                             }
00813                             fprintf(fp, "rw-rw-rw-  1 0 0 %6lu ", size);
00814                             gmt = gmtime(&st.st_mtime);
00815                             //fprintf(fp, "%s %u %u ", mon_name[gmt->tm_mon], gmt->tm_mday, 1900 + gmt->tm_year);
00816                             fprintf(fp, "%.3s %u ", mon_name + gmt->tm_mon * 3, gmt->tm_mday);
00817                             //fprintf(fp, "%02u:%02u:%02u ", gmt->tm_hour, gmt->tm_min, gmt->tm_sec);
00818                             fprintf(fp, "%02u:%02u ", gmt->tm_hour, gmt->tm_min);
00819                             fputs(d_ent->d_name, fp);
00820                             fputs("\r\n", fp);
00821                         }
00822                         free(name);
00823                     }
00824                 }
00825                 fclose(fp);
00826             }
00827             NutTcpCloseSocket(sock);
00828         }
00829         closedir(dir);
00830     }
00831     if (ec) {
00832         return NutFtpRespondBad(session, ec);
00833     }
00834     return NutFtpRespondOk(session, 226);
00835 }
00836 
00849 int NutFtpProcessMkd(FTPSESSION * session, char *path)
00850 {
00851     if (mkdir(path, 0777)) {
00852         return NutFtpRespondBad(session, 550);
00853     }
00854     return NutFtpRespondOk(session, 257);
00855 }
00856 
00869 int NutFtpProcessPass(FTPSESSION * session, char *pass)
00870 {
00871     if (ftp_pass && *ftp_pass) {
00872         if (session->ftp_login != 1 || strcmp(ftp_pass, pass)) {
00873             session->ftp_login = 0;
00874             return NutFtpRespondBad(session, 550);
00875         }
00876     }
00877     session->ftp_login = 2;
00878     return NutFtpRespondOk(session, 230);
00879 }
00880 
00894 int NutFtpProcessPassiv(FTPSESSION * session)
00895 {
00896     uint32_t ip = session->ftp_sock->so_local_addr;
00897     uint16_t port = 20;
00898 
00899     fprintf(session->ftp_stream, "227 Passive (%u,%u,%u,%u,%u,%u).\r\n",        /* */
00900             (uint8_t) ip, (uint8_t) (ip >> 8), (uint8_t) (ip >> 16), (uint8_t) (ip >> 24),  /* */
00901             (uint8_t) (port >> 8), (uint8_t) port);
00902     fflush(session->ftp_stream);
00903     session->ftp_passive = 1;
00904 
00905     return 0;
00906 }
00907 
00922 int NutFtpProcessPort(FTPSESSION * session, char *args)
00923 {
00924     if (ParseIpPort(args, &session->ftp_data_ip, &session->ftp_data_port) == 6) {
00925         if (session->ftp_sock->so_remote_addr == session->ftp_data_ip) {
00926             return NutFtpRespondOk(session, 200);
00927         }
00928         return NutFtpRespondBad(session, 425);
00929     }
00930     return NutFtpRespondBad(session, 501);;
00931 }
00932 
00945 int NutFtpProcessPwd(FTPSESSION * session)
00946 {
00947 #ifdef FTPD_DEBUG
00948     printf("\n<'257 \"%s\"' ", session->ftp_cwd);
00949 #endif
00950     fprintf(session->ftp_stream, "257 \"%s\"\r\n", session->ftp_cwd);
00951     return 0;
00952 }
00953 
00966 int NutFtpProcessRmd(FTPSESSION * session, char *path)
00967 {
00968     if (rmdir(path)) {
00969         return NutFtpRespondBad(session, 451);
00970     }
00971     return NutFtpRespondOk(session, 257);
00972 }
00973 
00983 int NutFtpProcessSystem(FTPSESSION * session)
00984 {
00985 #ifdef FTPD_DEBUG
00986     printf("\n<'215 UNIX Type: L8' ");
00987 #endif
00988     fputs("215 UNIX Type: L8\r\n", session->ftp_stream);
00989     return 0;
00990 }
00991 
01006 int NutFtpProcessType(FTPSESSION * session, char *typecode)
01007 {
01008     session->ftp_tran_mode = (*typecode != 'A') && (*typecode != 'a');
01009     return NutFtpRespondOk(session, 200);
01010 }
01011 
01024 int NutFtpProcessUser(FTPSESSION * session, char *user)
01025 {
01026     if (ftp_user && *ftp_user) {
01027         if (session->ftp_login && strcmp(ftp_user, user)) {
01028             session->ftp_login = 0;
01029             return NutFtpRespondBad(session, 550);
01030         }
01031     }
01032 
01033     /* Need a password too. */
01034     if (ftp_pass && *ftp_pass) {
01035         session->ftp_login = 1;
01036         return NutFtpRespondOk(session, 331);
01037     }
01038 
01039     /* No password required. */
01040     session->ftp_login = 2;
01041     return NutFtpRespondOk(session, 230);
01042 }
01043 
01055 int NutFtpProcessRequest(FTPSESSION * session, char *request)
01056 {
01057     int rc = 0;
01058     char *cmd;
01059     char *args;
01060 
01061     /* Split the line into command and argument part. */
01062     SplitCmdArg(request, &cmd, &args);
01063 #ifdef FTPD_DEBUG
01064     printf("\n>'%s %s' ", cmd, args);
01065 #endif
01066 
01067     /* QUIT - Terminate session. */
01068     if (strcmp_P(cmd, cmd_quit_P) == 0) {
01069         NutFtpRespondOk(session, 221);
01070         rc = -1;
01071     }
01072 
01073     /* USER <username> - Check user name. */
01074     else if (strcmp_P(cmd, cmd_user_P) == 0) {
01075         rc = NutFtpProcessUser(session, args);
01076     }
01077 
01078     /* PASS <password> - Check user password. */
01079     else if (strcmp_P(cmd, cmd_pass_P) == 0) {
01080         rc = NutFtpProcessPass(session, args);
01081     } else if (strcmp_P(cmd, cmd_noop_P) == 0) {
01082         NutFtpRespondOk(session, 200);
01083     }
01084     /* Anything else requires a successful login. */
01085     else if (session->ftp_login < 2) {
01086         rc = NutFtpRespondBad(session, 530);
01087     }
01088 
01089     /* Valid login. */
01090     else {
01091 
01092         /* PASV - Prepare passive transfer. */
01093         if (strcmp_P(cmd, cmd_pasv_P) == 0) {
01094             rc = NutFtpProcessPassiv(session);
01095         }
01096 
01097         /* PORT <host-port> - Set data connection. */
01098         else if (strcmp_P(cmd, cmd_port_P) == 0) {
01099             rc = NutFtpProcessPort(session, args);
01100         }
01101 
01102         /* [X]PWD - Send name of current working directory. */
01103         else if (strcmp_P(cmd, cmd_pwd_P) == 0 || strcmp_P(cmd, cmd_xpwd_P) == 0) {
01104             rc = NutFtpProcessPwd(session);
01105         }
01106 
01107         /* SYST - Send system identifier. */
01108         else if (strcmp_P(cmd, cmd_syst_P) == 0) {
01109             rc = NutFtpProcessSystem(session);
01110         }
01111 
01112         /* TYPE <type-code> - Receive transfer mode. */
01113         else if (strcmp_P(cmd, cmd_type_P) == 0) {
01114             rc = NutFtpProcessType(session, args);
01115         }
01116 
01117         /* Commands with path name argument. */
01118         else {
01119             char *path;
01120 
01121             if ((path = CreateFullPathName(ftp_root, session->ftp_cwd, args)) == 0) {
01122                 rc = NutFtpRespondBad(session, 451);
01123             }
01124 
01125             /* CWD <pathname> - Change working directory. */
01126             else if (strcmp_P(cmd, cmd_cwd_P) == 0) {
01127                 rc = NutFtpProcessCwd(session, path);
01128             }
01129 
01130             /* DELE <pathname> - Delete a file. */
01131             else if (strcmp_P(cmd, cmd_dele_P) == 0) {
01132                 rc = NutFtpProcessDelete(session, path);
01133             }
01134 
01135             /* LIST | NLST [<pathname>] - Send list of files in a directory. */
01136             else if (strcmp_P(cmd, cmd_list_P) == 0 || strcmp_P(cmd, cmd_nlst_P) == 0) {
01137                 rc = NutFtpTransferDirectory(session, path);
01138             }
01139 
01140             /* MKD <pathname> - Make a directory. */
01141             else if (strcmp_P(cmd, cmd_mkd_P) == 0 || strcmp_P(cmd, cmd_xmkd_P) == 0) {
01142                 rc = NutFtpProcessMkd(session, path);
01143             }
01144 
01145             /* RETR <pathname> - Send a file to the client. */
01146             else if (strcmp_P(cmd, cmd_retr_P) == 0) {
01147                 rc = NutFtpTransferFile(session, path, 1);
01148             }
01149 
01150             /* RMD <pathname> - Remove a directory. */
01151             else if (strcmp_P(cmd, cmd_rmd_P) == 0 || strcmp_P(cmd, cmd_xrmd_P) == 0) {
01152                 rc = NutFtpProcessRmd(session, path);
01153             }
01154 
01155             /* STOR <pathname> - Receive a file from the client. */
01156             else if (strcmp_P(cmd, cmd_stor_P) == 0) {
01157                 rc = NutFtpTransferFile(session, path, 0);
01158             }
01159 
01160             /* Anything else is an unknown command. */
01161             else {
01162                 rc = NutFtpRespondBad(session, 502);
01163             }
01164 
01165             if (path) {
01166                 free(path);
01167             }
01168         }
01169     }
01170     return rc;
01171 }
01172 
01188 int NutFtpServerSession(TCPSOCKET * sock)
01189 {
01190     int rc = 0;
01191     FTPSESSION *session;
01192     char *buff;
01193     time_t now;
01194     struct _tm *tip;
01195     char ch;
01196 
01197     /* Set the root path if not yet done. */
01198     if (NutRegisterFtpRoot(ftp_root)) {
01199         return -1;
01200     }
01201 
01202     /* Allocate the command line buffer. */
01203     if ((buff = malloc(FTP_MAX_CMDBUF)) == 0) {
01204         return -1;
01205     }
01206 
01207     /* Create a session structure and open a stream. */
01208     if ((session = NutFtpOpenSession(sock)) == 0) {
01209         free(buff);
01210         return -1;
01211     }
01212 
01213     /* Send a welcome banner including system time. */
01214     time(&now);
01215     tip = localtime(&now);
01216 #ifdef FTPD_DEBUG
01217     printf("\n<'");
01218     printf_P(rep_banner, NutVersionString(), &mon_name[tip->tm_mon * 3],        /* */
01219              tip->tm_mday, tip->tm_hour, tip->tm_min, tip->tm_sec);
01220 #endif
01221     fprintf_P(session->ftp_stream, rep_banner, NutVersionString(),      /* */
01222               &mon_name[tip->tm_mon * 3],       /* */
01223               tip->tm_mday, tip->tm_hour, tip->tm_min, tip->tm_sec);
01224 
01225     /* 
01226      * Loop for requests. 
01227      */
01228     while (rc == 0) {
01229 
01230         /* Flush any previous output and read a new command line. */
01231         fflush(session->ftp_stream);
01232         if (fgets(buff, FTP_MAX_CMDBUF, session->ftp_stream) == 0) {
01233             rc = -1;
01234             break;
01235         }
01236 
01237         /* Skip command lines, which are too long. */
01238         if ((ch = *(buff + strlen(buff) - 1)) != '\n' && ch != '\r') {
01239             do {
01240                 if (fgets(buff, FTP_MAX_CMDBUF, session->ftp_stream) == 0) {
01241                     rc = -1;
01242                     break;
01243                 }
01244             } while ((ch = *(buff + strlen(buff) - 1)) != '\n' && ch != '\r');
01245             if (rc == 0) {
01246                 rc = NutFtpRespondBad(session, 500);
01247             }
01248         }
01249 
01250         /* Process the request. */
01251         else {
01252             rc = NutFtpProcessRequest(session, buff);
01253         }
01254     }
01255 
01256     /* Cleanup and return. */
01257     NutFtpCloseSession(session);
01258     free(buff);
01259     return rc;
01260 }
01261 

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