Nut/OS  4.10.3
API Reference
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 
00094 //#define FTPD_DEBUG
00095 
00096 #include <sys/version.h>
00097 
00098 #include <sys/socket.h>
00099 #include <netinet/tcp.h>
00100 #include <sys/timer.h>
00101 
00102 #include <string.h>
00103 #include <stdio.h>
00104 #include <stdlib.h>
00105 #include <io.h>
00106 #include <fcntl.h>
00107 #include <sys/stat.h>
00108 #include <dirent.h>
00109 #include <unistd.h>
00110 #include <memdebug.h>
00111 
00112 #include <pro/ftpd.h>
00113 
00114 #ifdef FTPD_DEBUG
00115 #include <sys/heap.h>
00116 #endif
00117 
00124 
00131 
00137 #ifndef FTP_ROOTPATH
00138 #define FTP_ROOTPATH "PNUT:"
00139 #endif
00140 
00146 #ifndef FTP_DATA_PORT
00147 #define FTP_DATA_PORT   20
00148 #endif
00149 
00152 static char *ftp_root;
00153 static char *ftp_user;
00154 static char *ftp_pass;
00155 
00156 /*
00157  * On Harvard architectures constant strings are stored in ROM,
00158  * because RAM is usually a scarce resource on these platforms.
00159  */
00160 static prog_char cmd_cwd_P[] = "CWD";
00161 static prog_char cmd_dele_P[] = "DELE";
00162 static prog_char cmd_list_P[] = "LIST";
00163 static prog_char cmd_mkd_P[] = "MKD";
00164 static prog_char cmd_xmkd_P[] = "XMKD";
00165 static prog_char cmd_nlst_P[] = "NLST";
00166 static prog_char cmd_noop_P[] = "NOOP";
00167 static prog_char cmd_pass_P[] = "PASS";
00168 static prog_char cmd_pasv_P[] = "PASV";
00169 static prog_char cmd_port_P[] = "PORT";
00170 static prog_char cmd_pwd_P[] = "PWD";
00171 static prog_char cmd_xpwd_P[] = "XPWD";
00172 static prog_char cmd_quit_P[] = "QUIT";
00173 static prog_char cmd_retr_P[] = "RETR";
00174 static prog_char cmd_rmd_P[] = "RMD";
00175 static prog_char cmd_xrmd_P[] = "XRMD";
00176 static prog_char cmd_stor_P[] = "STOR";
00177 static prog_char cmd_syst_P[] = "SYST";
00178 static prog_char cmd_type_P[] = "TYPE";
00179 static prog_char cmd_user_P[] = "USER";
00180 static prog_char cmd_rename1_P[] = "RNFR";
00181 static prog_char cmd_rename2_P[] = "RNTO";
00182 
00183 
00184 static char *mon_name = "JanFebMarAprMayJunJulAugSepOctNovDec";
00185 
00186 static prog_char rep_banner[] = "220 Nut/OS FTP %s ready at %.3s%3d %02d:%02d:%02d\r\n";
00187 
00200 static void SplitCmdArg(char * line, char ** cmd, char ** args)
00201 {
00202     /* Skip leading spaces. */
00203     while (*line && *line <= ' ') {
00204         line++;
00205     }
00206 
00207     /* The first word is the command. Convert it to upper case. */
00208     *cmd = line;
00209     while (*line > ' ') {
00210         if (*line >= (uint8_t) 'a' && *line <= (uint8_t) 'z') {
00211             *line -= (uint8_t) 'a' - 'A';
00212         }
00213         line++;
00214     }
00215 
00216     /* Mark end of the command word. */
00217     if (*line) {
00218         *line++ = '\0';
00219     }
00220 
00221     /* Skip spaces. */
00222     while (*line && *line <= ' ') {
00223         ++line;
00224     }
00225 
00226     /* Arguments start here. */
00227     *args = line;
00228     while (*line && *line != '\r' && *line != '\n') {
00229         line++;
00230     }
00231 
00232     /* Mark end of arguments. */
00233     *line = 0;
00234 }
00235 
00251 static int ParseIpPort(CONST char * arg, uint32_t * ip, uint16_t * port)
00252 {
00253     int rc;
00254 
00255     *ip = 0;
00256     *port = 0;
00257     for (rc = 0; rc < 6; rc++) {
00258         if (*arg < '0' || *arg > '9') {
00259             break;
00260         }
00261         if (rc < 4) {
00262             *ip >>= 8;
00263             *ip += atol(arg) << 24;
00264         } else {
00265             *port <<= 8;
00266             *port += atoi(arg);
00267         }
00268         while (*arg && *arg != ',') {
00269             arg++;
00270         }
00271         if (*arg == ',') {
00272             arg++;
00273         }
00274     }
00275     return rc;
00276 }
00277 
00287 int NutFtpRespondOk(FTPSESSION * session, int code)
00288 {
00289     static prog_char fmt_P[] = "%d OK\r\n";
00290 
00291 #ifdef FTPD_DEBUG
00292     printf("\n<'%d OK' ", code);
00293 #endif
00294     fprintf_P(session->ftp_stream, fmt_P, code);
00295     fflush(session->ftp_stream);
00296 
00297     return 0;
00298 }
00299 
00309 int NutFtpRespondBad(FTPSESSION * session, int code)
00310 {
00311     static prog_char fmt_P[] = "%d Failed\r\n";
00312 
00313 #ifdef FTPD_DEBUG
00314     printf("\n<'%d Failed' ", code);
00315 #endif
00316     fprintf_P(session->ftp_stream, fmt_P, code);
00317     fflush(session->ftp_stream);
00318 
00319     return 0;
00320 }
00321 
00331 int NutFtpSendMode(FTPSESSION * session, int binary)
00332 {
00333     static prog_char intro_P[] = "150 Opening ";
00334     static prog_char amode_P[] = "ASCII.\r\n";
00335     static prog_char bmode_P[] = "BINARY.\r\n";
00336 
00337 #ifdef FTPD_DEBUG
00338     printf("\n<'150 Opening %s' ", binary ? "BINARY" : "ASCII");
00339 #endif
00340     fputs_P(intro_P, session->ftp_stream);
00341     fputs_P(binary ? bmode_P : amode_P, session->ftp_stream);
00342     fflush(session->ftp_stream);
00343 
00344     return 0;
00345 }
00346 
00367 char *CreateFullPathName(char *root, char *work, char *path)
00368 {
00369     char *full;
00370     char *cp;
00371     size_t rl = root ? strlen(root) : 0;
00372     size_t wl = work ? strlen(work) : 0;
00373     size_t pl = path ? strlen(path) : 0;
00374 
00375     /* Ignore trailing slashes in root and work. */
00376     if (rl && *(root + rl - 1) == '/') {
00377         rl--;
00378     }
00379     if (wl && *(work + wl - 1) == '/') {
00380         wl--;
00381     }
00382 
00383     if ((full = malloc(rl + wl + pl + 3)) != NULL) {
00384         /* Put the root in front. */
00385         cp = full;
00386         if (rl) {
00387             cp = strcpy(full, root) + rl;
00388         }
00389 
00390         /* If path is relative, prepend the working directory. */
00391         if(pl == 0 || *path != '/') {
00392             if (wl) {
00393                 if (*work != '/') {
00394                     *cp++ = '/';
00395                 }
00396                 cp = strcpy(cp, work) + wl;
00397             }
00398             *cp++ = '/';
00399             rl++;
00400         }
00401 
00402         if (pl) {
00403             *cp = 0;
00404             work = full + rl;
00405 
00406             while (*path) {
00407                 /* Ingore duplicate slashes. */
00408                 if (*path == '/') {
00409                     /* No double slash in target if we are at the root */
00410                     if ((cp == full) || (*(cp-1) != '/')) {
00411                         *cp++ = *path++;
00412                     }
00413                     while (*path == '/') {
00414                         path++;
00415                     }
00416                 }
00417                 /* Ignore single dots. */
00418                 if (*path == '.') {
00419                     path++;
00420                     if (*path == '/') {
00421                         path++;
00422                         continue;
00423                     }
00424                     if (*path == 0) {
00425                         break;
00426                     }
00427                     if (*path == '.') {
00428                         path++;
00429                         if (*path == '/' || *path == 0) {
00430                             if (cp != work) {
00431                                 cp--;
00432                                 while (cp != work) {
00433                                     cp--;
00434                                     if (*cp == '/') {
00435                                         break;
00436                                     }
00437                                 }
00438                             }
00439                             continue;
00440                         }
00441                         path--;
00442                     }
00443                     path--;
00444                 }
00445                 /* Copy the current path component. */
00446                 while (*path && *path != '/') {
00447                     *cp++ = *path++;
00448                 }
00449             }
00450         }
00451         *cp = 0;
00452     }
00453     return full;
00454 }
00455 
00465 TCPSOCKET *NutFtpDataConnect(FTPSESSION * session)
00466 {
00467 #ifdef FTPD_DEBUG
00468     static prog_char errorcode_P[] = "errorcode of active socket: %i\n";
00469     static prog_char socfailed_P[] = "Create socket failed";
00470 #endif
00471     TCPSOCKET *sock;
00472     int rc;
00473 
00474     if ((sock = NutTcpCreateSocket()) != 0) {
00475 
00476         if (session->ftp_maxseg) {
00477             NutTcpSetSockOpt(sock, TCP_MAXSEG, &session->ftp_maxseg, sizeof(session->ftp_maxseg));
00478         }
00479         if (session->ftp_passive) {
00480             rc = NutTcpAccept(sock, session->ftp_data_port);
00481         } else {
00482             rc = NutTcpConnect(sock, session->ftp_data_ip, session->ftp_data_port);
00483 #ifdef FTPD_DEBUG
00484             if (rc) {
00485               int errorcode = NutTcpError(sock);
00486               printf_P(errorcode_P, errorcode);
00487             }
00488 #endif
00489         }
00490         if (rc) {
00491             NutTcpCloseSocket(sock);
00492             sock = 0;
00493         }
00494     }
00495 #ifdef FTPD_DEBUG
00496     else {
00497         puts_P(socfailed_P);
00498     }
00499 #endif
00500     return sock;
00501 }
00502 
00516 int NutRegisterFtpRoot(CONST char *path)
00517 {
00518     /* Reset path to default. */
00519     if (path == NULL || *path == 0) {
00520         /* Release previously allocate space. */
00521         if (ftp_root) {
00522             free(ftp_root);
00523         }
00524         if ((ftp_root = malloc(sizeof(FTP_ROOTPATH))) == 0) {
00525             return -1;
00526         }
00527         strcpy(ftp_root, FTP_ROOTPATH);
00528     }
00529 
00530     /* Set a specified path. */
00531     else {
00532         char *cp = strchr(path, ':');
00533         int len = strlen(path);
00534 
00535         /* Make sure that the path fulfills all requirements. */
00536         if (len < 2 || cp == 0 || (*++cp && *cp != '/')) {
00537             return -1;
00538         }
00539 
00540         /* Allocate space for new path, but preserve the current one. */
00541         if ((cp = malloc(len + 1)) == 0) {
00542             return -1;
00543         }
00544 
00545         /* Take over, releasing previously allocate space. */
00546         strcpy(cp, path);
00547         if (ftp_root) {
00548             free(ftp_root);
00549         }
00550         ftp_root = cp;
00551 
00552         /* Chop off an optional trailing slash. */
00553         cp = cp + strlen(cp) - 1;
00554         if (*cp == '/') {
00555             *cp = 0;
00556         }
00557     }
00558     return 0;
00559 }
00560 
00576 int NutRegisterFtpUser(CONST char *user, CONST char *pass)
00577 {
00578     if (ftp_user) {
00579         free(ftp_user);
00580         ftp_user = NULL;
00581     }
00582     if (user && *user) {
00583         if ((ftp_user = strdup(user)) == NULL) {
00584             return -1;
00585         }
00586     }
00587     if (ftp_pass) {
00588         free(ftp_pass);
00589         ftp_pass = NULL;
00590     }
00591     if (pass && *pass) {
00592         if ((ftp_pass = strdup(pass)) == NULL) {
00593             return -1;
00594         }
00595     }
00596     return 0;
00597 }
00598 
00599 
00609 FTPSESSION *NutFtpOpenSession(TCPSOCKET * sock)
00610 {
00611     FTPSESSION *session;
00612 
00613     session = malloc(sizeof(FTPSESSION));
00614 
00615     if (session) {
00616         memset(session, 0, sizeof(FTPSESSION));
00617         session->ftp_data_port = FTP_DATA_PORT;
00618         session->ftp_maxseg = sock->so_mss;
00619         session->ftp_sock = sock;
00620 
00621         /* Set initial working directory. */
00622         if ((session->ftp_cwd = malloc(2)) == 0) {
00623             free(session);
00624             session = 0;
00625         } else {
00626             session->ftp_cwd[0] = '/';
00627             session->ftp_cwd[1] = 0;
00628 
00629             /*
00630              * Open a stream and associate it with the socket, so
00631              * we can use standard I/O. Note, that socket streams
00632              * currently do support text mode.
00633              */
00634             if ((session->ftp_stream = _fdopen((int) sock, "r+b")) == 0) {
00635                 free(session->ftp_cwd);
00636                 free(session);
00637                 session = 0;
00638             }
00639         }
00640     }
00641     return session;
00642 }
00643 
00650 void NutFtpCloseSession(FTPSESSION * session)
00651 {
00652     if (session) {
00653         /* Close the stream. */
00654         fclose(session->ftp_stream);
00655         if (session->ftp_cwd) {
00656             free(session->ftp_cwd);
00657         }
00658         if (session->ftp_renamesource) {
00659             free(session->ftp_renamesource);
00660         }
00661         free(session);
00662     }
00663 }
00664 
00675 int NutFtpProcessCwd(FTPSESSION * session, char *path)
00676 {
00677     struct stat st;
00678     char *cp = path + strlen(ftp_root);
00679 
00680     if (*cp && strcmp(cp, "/")) {
00681         /*
00682          * Check, if the path exists and if this is a directory.
00683          */
00684         if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
00685             return NutFtpRespondBad(session, 550);
00686         }
00687     }
00688 
00689     /*
00690      * Store the new working directory excluding our root part.
00691      */
00692     if (*cp == 0) {
00693         cp = "/";
00694     }
00695     if (session->ftp_cwd) {
00696         free(session->ftp_cwd);
00697     }
00698     if ((session->ftp_cwd = strdup(cp)) == NULL) {
00699         return NutFtpRespondBad(session, 550);
00700     }
00701     return NutFtpRespondOk(session, 250);
00702 }
00703 
00716 int NutFtpProcessDelete(FTPSESSION * session, char *path)
00717 {
00718     if (unlink(path)) {
00719         return NutFtpRespondBad(session, 550);
00720     }
00721     return NutFtpRespondOk(session, 250);
00722 }
00723 
00741 int NutFtpTransferFile(FTPSESSION * session, char *path, int mode)
00742 {
00743 #ifdef FTPD_DEBUG
00744     static prog_char dataconnectfailed_P[] = "NutFtpDataConnect failed";
00745 #endif
00746     TCPSOCKET *sock;
00747     int ec = 550;
00748     int fh = -1;
00749     /* Open the file to send. */
00750     if (mode) {
00751         /* Some clients open file as directorys. Giving them a direct failure */
00752         struct stat st;
00753         if (stat(path, &st) == 0) {
00754             if (S_ISREG(st.st_mode)) {
00755                 fh = _open(path, _O_BINARY | _O_RDONLY);
00756             } else {
00757 #ifdef FTPD_DEBUG
00758                printf("Not a regular file\n");
00759 #endif
00760             }
00761         } else {
00762 #ifdef FTPD_DEBUG
00763             printf("'%s' stat() failed\n", path);
00764 #endif
00765         }
00766     }
00767     /* Open the file to receive. */
00768     else {
00769         fh = _open(path, _O_CREAT | _O_TRUNC);
00770     }
00771     if (fh != -1) {
00772         /* File status OK, opening data connection */
00773         NutFtpSendMode(session, session->ftp_tran_mode);
00774         if ((sock = NutFtpDataConnect(session)) != 0) {
00775             uint16_t mss = sock->so_mss;
00776             uint8_t *buf;
00777 
00778             if (mss < 256) {
00779                 mss = 256;
00780             }
00781             if ((buf = malloc(mss)) != 0) {
00782                 int got;
00783 
00784                 ec = 0;
00785 
00786                 /* Send a file. */
00787                 if (mode) {
00788                     while ((got = _read(fh, buf, mss)) > 0) {
00789                         int send = 0;
00790                         int retcode;
00791                         uint8_t timeout = 0;
00792                         /* Mentioned in inetq.c, NutTcpSend does not gurantee
00793                            to send all bytes, so we use a loop and do a timeout if
00794                            no byte has been send within two seconds. */
00795                         do {
00796                             retcode = NutTcpSend(sock, buf+send, got-send);
00797                             send += retcode; //if < 0, we will leave the loop anyway
00798                             if (send == 0) {
00799                                 timeout++;
00800                                 NutSleep(10);
00801                             } else {
00802                                 timeout = 0;
00803                             }
00804                         } while ((send < got ) && (retcode >= 0) && (timeout < 200));
00805                         if (retcode <= 0) {
00806 #ifdef FTPD_DEBUG
00807                             printf("Error in NutTcpSend\n");
00808 #endif
00809                             ec = 551;
00810                             break;
00811                         }
00812                     }
00813                 }
00814 
00815                 /* Receive a file. */
00816                 else {
00817                     while ((got = NutTcpReceive(sock, buf, mss)) > 0) {
00818                         int x;
00819                         if ((x = _write(fh, buf, got)) != got) {
00820                             ec = 552;
00821                             break;
00822                         }
00823                     }
00824                 }
00825                 free(buf);
00826             }
00827             NutTcpCloseSocket(sock);
00828         }
00829 #ifdef FTPD_DEBUG
00830           else {
00831           puts_P(dataconnectfailed_P);
00832         }
00833 #endif
00834         _close(fh);
00835 
00836         /* Remove files received with an error. */
00837         if (mode == 0 && ec) {
00838             unlink(path);
00839         }
00840     }
00841     if (ec) {
00842         return NutFtpRespondBad(session, ec);
00843     }
00844     return NutFtpRespondOk(session, 226);
00845 }
00846 
00860 int NutFtpTransferDirectoryOptions(FTPSESSION * session, char *path, int options)
00861 {
00862     static prog_char fileattributes_P[] = "rw-rw-rw-  1 0 0 %6lu ";
00863     static prog_char dateattribute_P[] = "%.3s %u ";
00864     static prog_char timeattribute_P[] = "%02u:%02u ";
00865     TCPSOCKET *sock;
00866     FILE *fp;
00867 
00868     struct stat st;
00869     DIR *dir;
00870     struct dirent *d_ent;
00871     tm *gmt;
00872     uint32_t size;
00873     int ec = 550;
00874     char *name;
00875     size_t pathlen;
00876 
00877     dir = opendir(path);
00878     if (dir) {
00879         NutFtpSendMode(session, 0);
00880         if ((sock = NutFtpDataConnect(session)) != 0) {
00881             if ((fp = _fdopen((int) sock, "r+b")) != 0) {
00882                 ec = 0;
00883                 pathlen = strlen(path);
00884                 while ((d_ent = readdir(dir)) != 0) {
00885                     if ((d_ent->d_name[0] == '.')  && ((options & 1) == 0)) {
00886                         continue;
00887                     }
00888                     if ((name = malloc(pathlen + strlen(d_ent->d_name) + 2)) != 0) {
00889                         strcpy(name, path);
00890                         if (pathlen == 0 || name[pathlen - 1] != '/') {
00891                             strcat(name, "/");
00892                         }
00893                         strcat(name, d_ent->d_name);
00894                         if (stat(name, &st) == 0) {
00895                             if (S_ISDIR(st.st_mode)) {
00896                                 fputc('d', fp);
00897                                 size = 0;
00898                             } else {
00899                                 fputc('-', fp);
00900                                 size = st.st_size;
00901                             }
00902                             fprintf_P(fp, fileattributes_P, size);
00903                             gmt = gmtime(&st.st_mtime);
00904                             //fprintf(fp, "%s %u %u ", mon_name[gmt->tm_mon], gmt->tm_mday, 1900 + gmt->tm_year);
00905                             fprintf_P(fp, dateattribute_P, mon_name + gmt->tm_mon * 3, gmt->tm_mday);
00906                             //fprintf(fp, "%02u:%02u:%02u ", gmt->tm_hour, gmt->tm_min, gmt->tm_sec);
00907                             fprintf_P(fp, timeattribute_P, gmt->tm_hour, gmt->tm_min);
00908                             fputs(d_ent->d_name, fp);
00909                             fputs("\r\n", fp);
00910                         }
00911                         free(name);
00912                     }
00913                 }
00914                 fclose(fp);
00915             }
00916             NutTcpCloseSocket(sock);
00917         }
00918         closedir(dir);
00919     }
00920     if (ec) {
00921         return NutFtpRespondBad(session, ec);
00922     }
00923     return NutFtpRespondOk(session, 226);
00924 }
00925 
00938 int NutFtpTransferDirectory(FTPSESSION * session, char *path)
00939 {
00940     return NutFtpTransferDirectoryOptions(session, path, 0);
00941 }
00942 
00955 int NutFtpProcessMkd(FTPSESSION * session, char *path)
00956 {
00957     if (mkdir(path, 0777)) {
00958         return NutFtpRespondBad(session, 550);
00959     }
00960     return NutFtpRespondOk(session, 257);
00961 }
00962 
00963 
00964 int NutFtpRenamePrepare(FTPSESSION * session, char *path)
00965 {
00966     if (session->ftp_renamesource) {
00967         free(session->ftp_renamesource);
00968         session->ftp_renamesource = NULL;
00969     }
00970     if (path) {
00971         struct stat st;
00972         if (stat(path, &st) == 0) {
00973             session->ftp_renamesource = strdup(path);
00974             if (session->ftp_renamesource) {
00975                 return NutFtpRespondOk(session, 350);
00976             }
00977         } else {
00978           return NutFtpRespondBad(session, 450);
00979         }
00980     }
00981     return NutFtpRespondBad(session, 501);
00982 }
00983 
00984 int NutFtpRenameAction(FTPSESSION * session, char *path)
00985 {
00986     uint16_t responsebad;
00987     if (session->ftp_renamesource) {
00988          if (path) {
00989              int res = rename(session->ftp_renamesource, path);
00990              free(session->ftp_renamesource);
00991              session->ftp_renamesource = NULL;
00992              if (res == 0) {
00993                  return NutFtpRespondOk(session, 250);
00994              } else {
00995                  responsebad = 550;
00996              }
00997          } else {
00998              responsebad = 501;
00999          }
01000     } else {
01001          responsebad = 503;
01002     }
01003     return NutFtpRespondBad(session, responsebad);
01004 }
01005 
01006 
01019 int NutFtpProcessPass(FTPSESSION * session, char *pass)
01020 {
01021     if (ftp_pass && *ftp_pass) {
01022         if (session->ftp_login != 1 || strcmp(ftp_pass, pass)) {
01023             session->ftp_login = 0;
01024             return NutFtpRespondBad(session, 550);
01025         }
01026     }
01027     session->ftp_login = 2;
01028     return NutFtpRespondOk(session, 230);
01029 }
01030 
01044 int NutFtpProcessPassiv(FTPSESSION * session)
01045 {
01046     static prog_char passiveprint_P[] = "227 Passive (%u,%u,%u,%u,%u,%u).\r\n";
01047     uint32_t ip = session->ftp_sock->so_local_addr;
01048     uint16_t port = 20;
01049 
01050     fprintf_P(session->ftp_stream, passiveprint_P,        /* */
01051             (uint8_t) ip, (uint8_t) (ip >> 8), (uint8_t) (ip >> 16), (uint8_t) (ip >> 24),  /* */
01052             (uint8_t) (port >> 8), (uint8_t) port);
01053     fflush(session->ftp_stream);
01054     session->ftp_passive = 1;
01055 
01056     return 0;
01057 }
01058 
01073 int NutFtpProcessPort(FTPSESSION * session, char *args)
01074 {
01075     if (ParseIpPort(args, &session->ftp_data_ip, &session->ftp_data_port) == 6) {
01076         if (session->ftp_sock->so_remote_addr == session->ftp_data_ip) {
01077             return NutFtpRespondOk(session, 200);
01078         }
01079         return NutFtpRespondBad(session, 425);
01080     }
01081     return NutFtpRespondBad(session, 501);;
01082 }
01083 
01096 int NutFtpProcessPwd(FTPSESSION * session)
01097 {
01098     static prog_char pwdanswer_P[] = "257 \"%s\"\r\n";
01099 #ifdef FTPD_DEBUG
01100     printf("\n<'257 \"%s\"' ", session->ftp_cwd);
01101 #endif
01102     fprintf_P(session->ftp_stream, pwdanswer_P, session->ftp_cwd);
01103     return 0;
01104 }
01105 
01118 int NutFtpProcessRmd(FTPSESSION * session, char *path)
01119 {
01120     if (rmdir(path)) {
01121         return NutFtpRespondBad(session, 451);
01122     }
01123     return NutFtpRespondOk(session, 257);
01124 }
01125 
01135 int NutFtpProcessSystem(FTPSESSION * session)
01136 {
01137     static prog_char unixtype_P[] = "215 UNIX Type: L8\r\n";
01138 #ifdef FTPD_DEBUG
01139     printf("\n<'215 UNIX Type: L8' ");
01140 #endif
01141     fputs_P(unixtype_P, session->ftp_stream);
01142     return 0;
01143 }
01144 
01159 int NutFtpProcessType(FTPSESSION * session, char *typecode)
01160 {
01161     session->ftp_tran_mode = (*typecode != 'A') && (*typecode != 'a');
01162     return NutFtpRespondOk(session, 200);
01163 }
01164 
01177 int NutFtpProcessUser(FTPSESSION * session, char *user)
01178 {
01179     if (ftp_user && *ftp_user) {
01180         if (session->ftp_login && strcmp(ftp_user, user)) {
01181             session->ftp_login = 0;
01182             return NutFtpRespondBad(session, 550);
01183         }
01184     }
01185 
01186     /* Need a password too. */
01187     if (ftp_pass && *ftp_pass) {
01188         session->ftp_login = 1;
01189         return NutFtpRespondOk(session, 331);
01190     }
01191 
01192     /* No password required. */
01193     session->ftp_login = 2;
01194     return NutFtpRespondOk(session, 230);
01195 }
01196 
01208 int NutFtpProcessRequest(FTPSESSION * session, char *request)
01209 {
01210     int rc = 0;
01211     char *cmd;
01212     char *args;
01213 
01214     /* Split the line into command and argument part. */
01215     SplitCmdArg(request, &cmd, &args);
01216 #ifdef FTPD_DEBUG
01217     printf("\n>'%s %s' ", cmd, args);
01218 #endif
01219 
01220     /* Clean-up if rename request is incomplete */
01221     if ((session->ftp_renamesource) && (strcmp_P(cmd, cmd_rename2_P))) {
01222           free(session->ftp_renamesource);
01223           session->ftp_renamesource = NULL;
01224     }
01225     /* QUIT - Terminate session. */
01226     if (strcmp_P(cmd, cmd_quit_P) == 0) {
01227         NutFtpRespondOk(session, 221);
01228         rc = -1;
01229     }
01230     /* USER <username> - Check user name. */
01231     else if (strcmp_P(cmd, cmd_user_P) == 0) {
01232         rc = NutFtpProcessUser(session, args);
01233     }
01234 
01235     /* PASS <password> - Check user password. */
01236     else if (strcmp_P(cmd, cmd_pass_P) == 0) {
01237         rc = NutFtpProcessPass(session, args);
01238     } else if (strcmp_P(cmd, cmd_noop_P) == 0) {
01239         NutFtpRespondOk(session, 200);
01240     }
01241     /* Anything else requires a successful login. */
01242     else if (session->ftp_login < 2) {
01243         rc = NutFtpRespondBad(session, 530);
01244     }
01245 
01246     /* Valid login. */
01247     else {
01248 
01249         /* PASV - Prepare passive transfer. */
01250         if (strcmp_P(cmd, cmd_pasv_P) == 0) {
01251             rc = NutFtpProcessPassiv(session);
01252         }
01253 
01254         /* PORT <host-port> - Set data connection. */
01255         else if (strcmp_P(cmd, cmd_port_P) == 0) {
01256             rc = NutFtpProcessPort(session, args);
01257         }
01258 
01259         /* [X]PWD - Send name of current working directory. */
01260         else if (strcmp_P(cmd, cmd_pwd_P) == 0 || strcmp_P(cmd, cmd_xpwd_P) == 0) {
01261             rc = NutFtpProcessPwd(session);
01262         }
01263 
01264         /* SYST - Send system identifier. */
01265         else if (strcmp_P(cmd, cmd_syst_P) == 0) {
01266             rc = NutFtpProcessSystem(session);
01267         }
01268 
01269         /* TYPE <type-code> - Receive transfer mode. */
01270         else if (strcmp_P(cmd, cmd_type_P) == 0) {
01271             rc = NutFtpProcessType(session, args);
01272         }
01273         /* Commands with path name argument. */
01274         else {
01275             char *path;
01276             char *argsredux = args;
01277             int options = 0;
01278             if (args[0] == '-') {
01279                  if (strchr(args, 'a')) {
01280                    options = 1;
01281                  }
01282                  argsredux = strchr(args, ' ');
01283                  if ((argsredux) && (strlen(argsredux) > 0)) {
01284                      argsredux++; //remove space
01285                  } else {
01286                      argsredux = "";
01287                  }
01288             }
01289             if ((path = CreateFullPathName(ftp_root, session->ftp_cwd, argsredux)) == 0) {
01290                 rc = NutFtpRespondBad(session, 451);
01291             }
01292 
01293             /* CWD <pathname> - Change working directory. */
01294             else if (strcmp_P(cmd, cmd_cwd_P) == 0) {
01295                 rc = NutFtpProcessCwd(session, path);
01296             }
01297 
01298             /* DELE <pathname> - Delete a file. */
01299             else if (strcmp_P(cmd, cmd_dele_P) == 0) {
01300                 rc = NutFtpProcessDelete(session, path);
01301             }
01302 
01303             /* LIST | NLST [<pathname>] - Send list of files in a directory. */
01304             else if (strcmp_P(cmd, cmd_list_P) == 0 || strcmp_P(cmd, cmd_nlst_P) == 0) {
01305                 rc = NutFtpTransferDirectoryOptions(session, path, options);
01306             }
01307 
01308             /* MKD <pathname> - Make a directory. */
01309             else if (strcmp_P(cmd, cmd_mkd_P) == 0 || strcmp_P(cmd, cmd_xmkd_P) == 0) {
01310                 rc = NutFtpProcessMkd(session, path);
01311             }
01312 
01313             /* RETR <pathname> - Send a file to the client. */
01314             else if (strcmp_P(cmd, cmd_retr_P) == 0) {
01315                 rc = NutFtpTransferFile(session, path, 1);
01316             }
01317 
01318             /* RMD <pathname> - Remove a directory. */
01319             else if (strcmp_P(cmd, cmd_rmd_P) == 0 || strcmp_P(cmd, cmd_xrmd_P) == 0) {
01320                 rc = NutFtpProcessRmd(session, path);
01321             }
01322 
01323             /* STOR <pathname> - Receive a file from the client. */
01324             else if (strcmp_P(cmd, cmd_stor_P) == 0) {
01325                 rc = NutFtpTransferFile(session, path, 0);
01326             }
01327             /* RNFR <pathname> from - Rename a file on the client. */
01328             else if (strcmp_P(cmd, cmd_rename1_P) == 0) {
01329                 rc = NutFtpRenamePrepare(session, path);
01330             }
01331             /* RNTO <pathname> to - Rename a file on the client. */
01332             else if (strcmp_P(cmd, cmd_rename2_P) == 0) {
01333                 rc = NutFtpRenameAction(session, path);
01334             }
01335             /* Anything else is an unknown command. */
01336             else {
01337                 rc = NutFtpRespondBad(session, 502);
01338             }
01339 
01340             if (path) {
01341                 free(path);
01342             }
01343         }
01344     }
01345     return rc;
01346 }
01347 
01363 int NutFtpServerSession(TCPSOCKET * sock)
01364 {
01365     int rc = 0;
01366     FTPSESSION *session;
01367     char *buff;
01368     time_t now;
01369     struct _tm *tip;
01370     char ch;
01371 
01372     /* Set the root path if not yet done. */
01373     if (NutRegisterFtpRoot(ftp_root)) {
01374         return -1;
01375     }
01376 
01377     /* Allocate the command line buffer. */
01378     if ((buff = malloc(FTP_MAX_CMDBUF)) == 0) {
01379         return -1;
01380     }
01381 
01382     /* Create a session structure and open a stream. */
01383     if ((session = NutFtpOpenSession(sock)) == 0) {
01384         free(buff);
01385         return -1;
01386     }
01387 
01388     /* Send a welcome banner including system time. */
01389     time(&now);
01390     tip = localtime(&now);
01391 #ifdef FTPD_DEBUG
01392     printf("\n<'");
01393     printf_P(rep_banner, NutVersionString(), &mon_name[tip->tm_mon * 3],        /* */
01394              tip->tm_mday, tip->tm_hour, tip->tm_min, tip->tm_sec);
01395 #endif
01396     fprintf_P(session->ftp_stream, rep_banner, NutVersionString(),      /* */
01397               &mon_name[tip->tm_mon * 3],       /* */
01398               tip->tm_mday, tip->tm_hour, tip->tm_min, tip->tm_sec);
01399 
01400     /*
01401      * Loop for requests.
01402      */
01403     while (rc == 0) {
01404 
01405         /* Flush any previous output and read a new command line. */
01406         fflush(session->ftp_stream);
01407         if (fgets(buff, FTP_MAX_CMDBUF, session->ftp_stream) == 0) {
01408             rc = -1;
01409             break;
01410         }
01411 
01412         /* Skip command lines, which are too long. */
01413         if ((ch = *(buff + strlen(buff) - 1)) != '\n' && ch != '\r') {
01414             do {
01415                 if (fgets(buff, FTP_MAX_CMDBUF, session->ftp_stream) == 0) {
01416                     rc = -1;
01417                     break;
01418                 }
01419             } while ((ch = *(buff + strlen(buff) - 1)) != '\n' && ch != '\r');
01420             if (rc == 0) {
01421                 rc = NutFtpRespondBad(session, 500);
01422             }
01423         }
01424 
01425         /* Process the request. */
01426         else {
01427             rc = NutFtpProcessRequest(session, buff);
01428         }
01429     }
01430 
01431     /* Cleanup and return. */
01432     NutFtpCloseSession(session);
01433     free(buff);
01434     return rc;
01435 }
01436