A quick and dirty FTP server port to mbed, for file i/o ops via LwIP stack.

Dependencies:   mbed

Files at this revision

API Documentation at this revision

Comitter:
Airman50
Date:
Tue Nov 30 00:45:55 2010 +0000
Commit message:
Pre Alpha release.

Changed in this revision

NetFtp/ftpd.c Show annotated file Show diff for this revision Revisions of this file
NetFtp/ftpd.h Show annotated file Show diff for this revision Revisions of this file
NetFtp/vfs.c Show annotated file Show diff for this revision Revisions of this file
NetFtp/vfs.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NetFtp/ftpd.c	Tue Nov 30 00:45:55 2010 +0000
@@ -0,0 +1,1478 @@
+/*
+ * Copyright (c) 2002 Florian Schulze.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the authors nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * ftpd.c - This file is part of the FTP daemon for lwIP
+ *
+ */
+
+#include "mbed.h"                  // DRB
+#include "SDFileSystem.h"          // DRB
+#include "EthernetNetIf.h"
+#include "HTTPServer.h"
+
+#include "lwip/debug.h"
+
+#include "lwip/stats.h"
+
+#include "ftpd.h"
+
+#include "lwip/tcp.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+//..#include <malloc.h>
+#ifdef _WIN32
+#include <string.h>
+#endif
+#include <ctype.h>
+#include <errno.h>
+#include <time.h>
+
+#include "vfs.h"
+}
+
+
+extern void printit( char *p_buf );
+
+
+#ifdef FTPD_DEBUG
+int dbg_printf(const char *fmt, ...);
+#else
+#ifdef _MSC_VER
+#define dbg_printf(x) /* x */
+#else
+#define dbg_printf(f, ...) /* */
+#endif
+#endif
+
+#define msg110 "110 MARK %s = %s."
+/*
+         110 Restart marker reply.
+             In this case, the text is exact and not left to the
+             particular implementation; it must read:
+                  MARK yyyy = mmmm
+             Where yyyy is User-process data stream marker, and mmmm
+             server's equivalent marker (note the spaces between markers
+             and "=").
+*/
+#define msg120 "120 Service ready in nnn minutes."
+#define msg125 "125 Data connection already open; transfer starting."
+#define msg150 "150 File status okay; about to open data connection."
+#define msg150recv "150 Opening BINARY mode data connection for %s (%i bytes)."
+#define msg150stor "150 Opening BINARY mode data connection for %s."
+#define msg200 "200 Command okay."
+#define msg202 "202 Command not implemented, superfluous at this site."
+#define msg211 "211 System status, or system help reply."
+#define msg212 "212 Directory status."
+#define msg213 "213 File status."
+#define msg214 "214 %s."
+/*
+             214 Help message.
+             On how to use the server or the meaning of a particular
+             non-standard command.  This reply is useful only to the
+             human user.
+*/
+#define msg214SYST "214 %s system type."
+/*
+         215 NAME system type.
+             Where NAME is an official system name from the list in the
+             Assigned Numbers document.
+*/
+#define msg220 "220 lwIP FTP Server ready."
+/*
+         220 Service ready for new user.
+*/
+#define msg221 "221 Goodbye."
+/*
+         221 Service closing control connection.
+             Logged out if appropriate.
+*/
+#define msg225 "225 Data connection open; no transfer in progress."
+#define msg226 "226 Closing data connection."
+/*
+             Requested file action successful (for example, file
+             transfer or file abort).
+*/
+#define msg227 "227 Entering Passive Mode (%i,%i,%i,%i,%i,%i)."
+/*
+         227 Entering Passive Mode (h1,h2,h3,h4,p1,p2).
+*/
+#define msg230 "230 User logged in, proceed."
+#define msg250 "250 Requested file action okay, completed."
+#define msg257PWD "257 \"%s\" is current directory."
+#define msg257 "257 \"%s\" created."
+/*
+         257 "PATHNAME" created.
+*/
+#define msg331 "331 User name okay, need password."
+#define msg332 "332 Need account for login."
+#define msg350 "350 Requested file action pending further information."
+#define msg421 "421 Service not available, closing control connection."
+/*
+             This may be a reply to any command if the service knows it
+             must shut down.
+*/
+#define msg425 "425 Can't open data connection."
+#define msg426 "426 Connection closed; transfer aborted."
+#define msg450 "450 Requested file action not taken."
+/*
+             File unavailable (e.g., file busy).
+*/
+#define msg451 "451 Requested action aborted: local error in processing."
+#define msg452 "452 Requested action not taken."
+/*
+             Insufficient storage space in system.
+*/
+#define msg500 "500 Syntax error, command unrecognized."
+/*
+             This may include errors such as command line too long.
+*/
+#define msg501 "501 Syntax error in parameters or arguments."
+#define msg502 "502 Command not implemented."
+#define msg503 "503 Bad sequence of commands."
+#define msg504 "504 Command not implemented for that parameter."
+#define msg530 "530 Not logged in."
+#define msg532 "532 Need account for storing files."
+#define msg550 "550 Requested action not taken."
+/*
+             File unavailable (e.g., file not found, no access).
+*/
+#define msg551 "551 Requested action aborted: page type unknown."
+#define msg552 "552 Requested file action aborted."
+/*
+             Exceeded storage allocation (for current directory or
+             dataset).
+*/
+#define msg553 "553 Requested action not taken."
+/*
+             File name not allowed.
+*/
+
+enum ftpd_state_e {
+    FTPD_USER,
+    FTPD_PASS,
+    FTPD_IDLE,
+    FTPD_NLST,
+    FTPD_LIST,
+    FTPD_RETR,
+    FTPD_RNFR,
+    FTPD_STOR,
+    FTPD_QUIT
+};
+
+static const char *month_table[12] = {
+    "Jan",
+    "Feb",
+    "Mar",
+    "Apr",
+    "May",
+    "Jun",
+    "Jul",
+    "Aug",
+    "Sep",
+    "Oct",
+    "Nov",
+    "Dez"
+};
+
+/*
+------------------------------------------------------------
+    SFIFO 1.3
+------------------------------------------------------------
+ * Simple portable lock-free FIFO
+ * (c) 2000-2002, David Olofson
+ *
+ * Platform support:
+ *    gcc / Linux / x86:        Works
+ *    gcc / Linux / x86 kernel:    Works
+ *    gcc / FreeBSD / x86:        Works
+ *    gcc / NetBSD / x86:        Works
+ *    gcc / Mac OS X / PPC:        Works
+ *    gcc / Win32 / x86:        Works
+ *    Borland C++ / DOS / x86RM:    Works
+ *    Borland C++ / Win32 / x86PM16:    Untested
+ *    ? / Various Un*ces / ?:        Untested
+ *    ? / Mac OS / PPC:        Untested
+ *    gcc / BeOS / x86:        Untested
+ *    gcc / BeOS / PPC:        Untested
+ *    ? / ? / Alpha:            Untested
+ *
+ * 1.2: Max buffer size halved, to avoid problems with
+ *    the sign bit...
+ *
+ * 1.3:    Critical buffer allocation bug fixed! For certain
+ *    requested buffer sizes, older version would
+ *    allocate a buffer of insufficient size, which
+ *    would result in memory thrashing. (Amazing that
+ *    I've manage to use this to the extent I have
+ *    without running into this... *heh*)
+ */
+
+/*
+ * Porting note:
+ *    Reads and writes of a variable of this type in memory
+ *    must be *atomic*! 'int' is *not* atomic on all platforms.
+ *    A safe type should be used, and  sfifo should limit the
+ *    maximum buffer size accordingly.
+ */
+typedef int sfifo_atomic_t;
+#ifdef __TURBOC__
+#    define    SFIFO_MAX_BUFFER_SIZE    0x7fff
+#else /* Kludge: Assume 32 bit platform */
+#    define    SFIFO_MAX_BUFFER_SIZE    0x7fffffff
+#endif
+
+typedef struct sfifo_t
+{
+    char *buffer;
+    int size;            /* Number of bytes */
+    sfifo_atomic_t readpos;        /* Read position */
+    sfifo_atomic_t writepos;    /* Write position */
+} sfifo_t;
+
+#define SFIFO_SIZEMASK(x)    ((x)->size - 1)
+
+#define sfifo_used(x)    (((x)->writepos - (x)->readpos) & SFIFO_SIZEMASK(x))
+#define sfifo_space(x)    ((x)->size - 1 - sfifo_used(x))
+
+//..DRB #define DBG(x)
+
+/*
+ * Alloc buffer, init FIFO etc...
+ */
+static int sfifo_init(sfifo_t *f, int size)
+{
+    memset(f, 0, sizeof(sfifo_t));
+
+    if(size > SFIFO_MAX_BUFFER_SIZE)
+        return -EINVAL;
+
+    /*
+     * Set sufficient power-of-2 size.
+     *
+     * No, there's no bug. If you need
+     * room for N bytes, the buffer must
+     * be at least N+1 bytes. (The fifo
+     * can't tell 'empty' from 'full'
+     * without unsafe index manipulations
+     * otherwise.)
+     */
+    f->size = 1;
+    for(; f->size <= size; f->size <<= 1)
+        ;
+
+    /* Get buffer */
+    if( 0 == (f->buffer = (char *)malloc(f->size)) )
+        return -ENOMEM;
+
+    return 0;
+}
+
+/*
+ * Dealloc buffer etc...
+ */
+static void sfifo_close(sfifo_t *f)
+{
+    if(f->buffer)
+        free(f->buffer);
+}
+
+/*
+ * Empty FIFO buffer
+ */
+static void sfifo_flush(sfifo_t *f)
+{
+    /* Reset positions */
+    f->readpos = 0;
+    f->writepos = 0;
+}
+
+/*
+ * Write bytes to a FIFO
+ * Return number of bytes written, or an error code
+ */
+static int sfifo_write(sfifo_t *f, const void *_buf, int len)
+{
+    int total;
+    int i;
+    const char *buf = (const char *)_buf;
+
+    if(!f->buffer)
+        return -ENODEV;    /* No buffer! */
+
+    /* total = len = min(space, len) */
+    total = sfifo_space(f);
+    DBG(dbg_printf("sfifo_space() = %d\n",total));
+    if(len > total)
+        len = total;
+    else
+        total = len;
+
+    i = f->writepos;
+    if(i + len > f->size)
+    {
+        memcpy(f->buffer + i, buf, f->size - i);
+        buf += f->size - i;
+        len -= f->size - i;
+        i = 0;
+    }
+    memcpy(f->buffer + i, buf, len);
+    f->writepos = i + len;
+
+    return total;
+}
+
+/*
+ * Read bytes from a FIFO
+ * Return number of bytes read, or an error code
+ */
+static int sfifo_read(sfifo_t *f, void *_buf, int len)
+{
+    int total;
+    int i;
+    char *buf = (char *)_buf;
+
+    if(!f->buffer)
+        return -ENODEV;    /* No buffer! */
+
+    /* total = len = min(used, len) */
+    total = sfifo_used(f);
+    DBG(dbg_printf("sfifo_used() = %d\n",total));
+    if(len > total)
+        len = total;
+    else
+        total = len;
+
+    i = f->readpos;
+    if(i + len > f->size)
+    {
+        memcpy(buf, f->buffer + i, f->size - i);
+        buf += f->size - i;
+        len -= f->size - i;
+        i = 0;
+    }
+    memcpy(buf, f->buffer + i, len);
+    f->readpos = i + len;
+
+    return total;
+}
+
+struct ftpd_datastate {
+    int connected;
+    vfs_dir_t *vfs_dir;
+    vfs_dirent_t *vfs_dirent;
+    vfs_file_t *vfs_file;
+    sfifo_t fifo;
+    struct tcp_pcb *msgpcb;
+    struct ftpd_msgstate *msgfs;
+};
+
+struct ftpd_msgstate {
+    enum ftpd_state_e state;
+    sfifo_t fifo;
+    vfs_t *vfs;
+    struct ip_addr dataip;
+    u16_t dataport;
+    struct tcp_pcb *datapcb;
+    struct ftpd_datastate *datafs;
+    int passive;
+    char *renamefrom;
+};
+
+static void send_msg(struct tcp_pcb *pcb, struct ftpd_msgstate *fsm, char *msg, ...);
+
+static void ftpd_dataerr(void *arg, err_t err)
+{
+    struct ftpd_datastate *fsd = (struct ftpd_datastate*)arg;
+
+    dbg_printf("ftpd_dataerr: %s (%i)\n", lwip_strerr(err), err);
+    if (fsd == NULL)
+        return;
+    fsd->msgfs->datafs = NULL;
+    fsd->msgfs->state = FTPD_IDLE;
+    free(fsd);
+}
+
+static void ftpd_dataclose(struct tcp_pcb *pcb, struct ftpd_datastate *fsd)
+{
+    tcp_arg(pcb, NULL);
+    tcp_sent(pcb, NULL);
+    tcp_recv(pcb, NULL);
+    fsd->msgfs->datafs = NULL;
+    sfifo_close(&fsd->fifo);
+    free(fsd);
+    tcp_arg(pcb, NULL);
+    tcp_close(pcb);
+}
+
+static void send_data(struct tcp_pcb *pcb, struct ftpd_datastate *fsd)
+{
+    err_t err;
+    u16_t len;
+
+    if (sfifo_used(&fsd->fifo) > 0) {
+        int i;
+
+        /* We cannot send more data than space available in the send
+           buffer. */
+        if (tcp_sndbuf(pcb) < sfifo_used(&fsd->fifo)) {
+            len = tcp_sndbuf(pcb);
+        } else {
+            len = (u16_t) sfifo_used(&fsd->fifo);
+        }
+
+        i = fsd->fifo.readpos;
+        if ((i + len) > fsd->fifo.size) {
+            err = tcp_write(pcb, fsd->fifo.buffer + i, (u16_t)(fsd->fifo.size - i), 1);
+            if (err != ERR_OK) {
+                dbg_printf("send_data: error writing!\n");
+                return;
+            }
+            len -= fsd->fifo.size - i;
+            fsd->fifo.readpos = 0;
+            i = 0;
+        }
+
+        err = tcp_write(pcb, fsd->fifo.buffer + i, len, 1);
+        if (err != ERR_OK) {
+            dbg_printf("send_data: error writing!\n");
+            return;
+        }
+        fsd->fifo.readpos += len;
+    }
+}
+
+static void send_file( struct ftpd_datastate *fsd, struct tcp_pcb *pcb )
+{
+    if (!fsd->connected)
+        return;
+
+    if (fsd->vfs_file)
+    {
+        char buffer[ 2048 ];
+        int len;
+
+        len = sfifo_space( &fsd->fifo );
+        if (len == 0)
+        {
+            send_data(pcb, fsd);
+            return;
+        }
+        if (len > 2048)
+            len = 2048;
+        len = vfs_read( buffer, 1, len, fsd->vfs_file );
+        if( len == 0 )
+        {
+            if( vfs_eof( fsd->vfs_file ) == 0)
+                return;
+            vfs_close( fsd->vfs_file );
+            fsd->vfs_file = NULL;
+            return;
+        }
+        sfifo_write( &fsd->fifo, buffer, len );
+        send_data( pcb, fsd );
+    } else {
+        struct ftpd_msgstate *fsm;
+        struct tcp_pcb *msgpcb;
+
+        if (sfifo_used(&fsd->fifo) > 0) {
+            send_data(pcb, fsd);
+            return;
+        }
+        fsm = fsd->msgfs;
+        msgpcb = fsd->msgpcb;
+
+        vfs_close( fsd->vfs_file );
+        fsd->vfs_file = NULL;
+        ftpd_dataclose( pcb, fsd );
+        fsm->datapcb = NULL;
+        fsm->datafs = NULL;
+        fsm->state = FTPD_IDLE;
+        send_msg( msgpcb, fsm, msg226 );
+//        printit( msg226 );
+        return;
+    }
+}
+
+static void send_next_directory(struct ftpd_datastate *fsd, struct tcp_pcb *pcb, int shortlist)
+{
+    char buffer[1024];
+    int len;
+
+    while (1) {
+    if (fsd->vfs_dirent == NULL)
+        fsd->vfs_dirent = vfs_readdir(fsd->vfs_dir);
+
+    if (fsd->vfs_dirent) {
+        if (shortlist) {
+            len = sprintf(buffer, "%s\r\n", fsd->vfs_dirent->name);
+            if (sfifo_space(&fsd->fifo) < len) {
+                send_data(pcb, fsd);
+                return;
+            }
+            sfifo_write(&fsd->fifo, buffer, len);
+            fsd->vfs_dirent = NULL;
+        } else {
+            vfs_stat_t st;
+            time_t current_time;
+            int current_year;
+            struct tm *s_time;
+
+            time(&current_time);
+            s_time = gmtime(&current_time);
+            current_year = s_time->tm_year;
+
+//..DRB            vfs_stat(fsd->msgfs->vfs, fsd->vfs_dirent->name, &st);
+            s_time = gmtime(&st.st_mtime);
+            if (s_time->tm_year == current_year)
+                len = sprintf(buffer, "-rw-rw-rw-   1 user     ftp  %11ld %s %02i %02i:%02i %s\r\n", st.st_size, month_table[s_time->tm_mon], s_time->tm_mday, s_time->tm_hour, s_time->tm_min, fsd->vfs_dirent->name);
+            else
+                len = sprintf(buffer, "-rw-rw-rw-   1 user     ftp  %11ld %s %02i %5i %s\r\n", st.st_size, month_table[s_time->tm_mon], s_time->tm_mday, s_time->tm_year + 1900, fsd->vfs_dirent->name);
+            if (VFS_ISDIR(st.st_mode))
+                buffer[0] = 'd';
+            if (sfifo_space(&fsd->fifo) < len) {
+                send_data(pcb, fsd);
+                return;
+            }
+            sfifo_write(&fsd->fifo, buffer, len);
+            fsd->vfs_dirent = NULL;
+        }
+    } else {
+        struct ftpd_msgstate *fsm;
+        struct tcp_pcb *msgpcb;
+
+        if (sfifo_used(&fsd->fifo) > 0) {
+            send_data(pcb, fsd);
+            return;
+        }
+        fsm = fsd->msgfs;
+        msgpcb = fsd->msgpcb;
+
+        vfs_closedir(fsd->vfs_dir);
+        fsd->vfs_dir = NULL;
+        ftpd_dataclose(pcb, fsd);
+        fsm->datapcb = NULL;
+        fsm->datafs = NULL;
+        fsm->state = FTPD_IDLE;
+        send_msg(msgpcb, fsm, msg226);
+        return;
+    }
+    }
+}
+
+static err_t ftpd_datasent(void *arg, struct tcp_pcb *pcb, u16_t len)
+{
+    struct ftpd_datastate *fsd = (struct ftpd_datastate*)arg;
+
+    switch (fsd->msgfs->state) {
+    case FTPD_LIST:
+        send_next_directory(fsd, pcb, 0);
+        break;
+    case FTPD_NLST:
+        send_next_directory(fsd, pcb, 1);
+        break;
+    case FTPD_RETR:
+        send_file(fsd, pcb);
+        break;
+    default:
+        break;
+    }
+
+    return ERR_OK;
+}
+
+static err_t ftpd_datarecv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+    struct ftpd_datastate *fsd = (struct ftpd_datastate*)arg;
+
+    if (err == ERR_OK && p != NULL) {
+        struct pbuf *q;
+        u16_t tot_len = 0;
+
+        for (q = p; q != NULL; q = q->next) {
+            int len;
+
+            len = vfs_write(q->payload, 1, q->len, fsd->vfs_file);
+            tot_len += len;
+            if (len != q->len)
+                break;
+        }
+
+        /* Inform TCP that we have taken the data. */
+        tcp_recved(pcb, tot_len);
+
+        pbuf_free(p);
+    }
+    if (err == ERR_OK && p == NULL) {
+        struct ftpd_msgstate *fsm;
+        struct tcp_pcb *msgpcb;
+
+        fsm = fsd->msgfs;
+        msgpcb = fsd->msgpcb;
+
+        vfs_close(fsd->vfs_file);
+        fsd->vfs_file = NULL;
+        ftpd_dataclose(pcb, fsd);
+        fsm->datapcb = NULL;
+        fsm->datafs = NULL;
+        fsm->state = FTPD_IDLE;
+        send_msg(msgpcb, fsm, msg226);
+    }
+
+    return ERR_OK;
+}
+
+static err_t ftpd_dataconnected(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+    struct ftpd_datastate *fsd = (struct ftpd_datastate*)arg;
+
+    fsd->msgfs->datapcb = pcb;
+    fsd->connected = 1;
+
+    /* Tell TCP that we wish to be informed of incoming data by a call
+       to the http_recv() function. */
+    tcp_recv(pcb, ftpd_datarecv);
+
+    /* Tell TCP that we wish be to informed of data that has been
+       successfully sent by a call to the ftpd_sent() function. */
+    tcp_sent(pcb, ftpd_datasent);
+
+    tcp_err(pcb, ftpd_dataerr);
+
+    switch (fsd->msgfs->state) {
+    case FTPD_LIST:
+        send_next_directory(fsd, pcb, 0);
+        break;
+    case FTPD_NLST:
+        send_next_directory(fsd, pcb, 1);
+        break;
+    case FTPD_RETR:
+        send_file(fsd, pcb);
+        break;
+    default:
+        break;
+    }
+
+    return ERR_OK;
+}
+
+static err_t ftpd_dataaccept(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+    struct ftpd_datastate *fsd = (struct ftpd_datastate*)arg;
+
+    fsd->msgfs->datapcb = pcb;
+    fsd->connected = 1;
+
+    /* Tell TCP that we wish to be informed of incoming data by a call
+       to the http_recv() function. */
+    tcp_recv(pcb, ftpd_datarecv);
+
+    /* Tell TCP that we wish be to informed of data that has been
+       successfully sent by a call to the ftpd_sent() function. */
+    tcp_sent(pcb, ftpd_datasent);
+
+    tcp_err(pcb, ftpd_dataerr);
+
+    switch (fsd->msgfs->state) {
+    case FTPD_LIST:
+        send_next_directory(fsd, pcb, 0);
+        break;
+    case FTPD_NLST:
+        send_next_directory(fsd, pcb, 1);
+        break;
+    case FTPD_RETR:
+        send_file(fsd, pcb);
+        break;
+    default:
+        break;
+    }
+
+    return ERR_OK;
+}
+
+static int open_dataconnection(struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    if (fsm->passive)
+        return 0;
+
+    /* Allocate memory for the structure that holds the state of the connection. */
+    fsm->datafs = (struct ftpd_datastate*)malloc(sizeof(struct ftpd_datastate));
+
+    if (fsm->datafs == NULL) {
+        send_msg(pcb, fsm, msg451);
+        return 1;
+    }
+    memset(fsm->datafs, 0, sizeof(struct ftpd_datastate));
+    fsm->datafs->msgfs = fsm;
+    fsm->datafs->msgpcb = pcb;
+    sfifo_init(&fsm->datafs->fifo, 2000);
+
+    fsm->datapcb = tcp_new();
+    tcp_bind(fsm->datapcb, &pcb->local_ip, 20);
+    /* Tell TCP that this is the structure we wish to be passed for our
+       callbacks. */
+    tcp_arg(fsm->datapcb, fsm->datafs);
+    tcp_connect(fsm->datapcb, &fsm->dataip, fsm->dataport, ftpd_dataconnected);
+
+    return 0;
+}
+
+static void cmd_user(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    send_msg(pcb, fsm, msg331);
+    fsm->state = FTPD_PASS;
+    /*
+       send_msg(pcb, fs, msgLoginFailed);
+       fs->state = FTPD_QUIT;
+     */
+}
+
+static void cmd_pass(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    send_msg(pcb, fsm, msg230);
+    fsm->state = FTPD_IDLE;
+    /*
+       send_msg(pcb, fs, msgLoginFailed);
+       fs->state = FTPD_QUIT;
+     */
+}
+
+static void cmd_port(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    int nr;
+    unsigned pHi, pLo;
+    unsigned ip[4];
+
+    nr = sscanf(arg, "%u,%u,%u,%u,%u,%u", &(ip[0]), &(ip[1]), &(ip[2]), &(ip[3]), &pHi, &pLo);
+    if (nr != 6) {
+        send_msg(pcb, fsm, msg501);
+    } else {
+        IP4_ADDR(&fsm->dataip, (u8_t) ip[0], (u8_t) ip[1], (u8_t) ip[2], (u8_t) ip[3]);
+        fsm->dataport = ((u16_t) pHi << 8) | (u16_t) pLo;
+        send_msg(pcb, fsm, msg200);
+    }
+}
+
+static void cmd_quit(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    send_msg(pcb, fsm, msg221);
+    fsm->state = FTPD_QUIT;
+}
+
+static void cmd_cwd(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    if (!vfs_chdir(fsm->vfs, arg)) {
+        send_msg(pcb, fsm, msg250);
+    } else {
+        send_msg(pcb, fsm, msg550);
+    }
+}
+
+static void cmd_cdup(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    if (!vfs_chdir(fsm->vfs, "..")) {
+        send_msg(pcb, fsm, msg250);
+    } else {
+        send_msg(pcb, fsm, msg550);
+    }
+}
+
+static void cmd_pwd(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    char *path;
+
+    path = vfs_getcwd( fsm->vfs, NULL, 0 );
+    if( path )
+    {
+        send_msg(pcb, fsm, msg257PWD, path);
+        free(path);
+    }
+}
+
+static void cmd_list_common(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm, int shortlist)
+{
+    vfs_dir_t *vfs_dir;
+    char *cwd;
+
+#if 0
+    cwd = vfs_getcwd(fsm->vfs, NULL, 0);
+    if ((!cwd)) {
+        send_msg(pcb, fsm, msg451);
+        return;
+    }
+#endif
+    vfs_dir = vfs_opendir(fsm->vfs, cwd);
+    free(cwd);
+    if (!vfs_dir) {
+        send_msg(pcb, fsm, msg451);
+        return;
+    }
+
+    if( open_dataconnection(pcb, fsm) != 0)
+    {
+        vfs_closedir(vfs_dir);
+        return;
+    }
+
+    fsm->datafs->vfs_dir = vfs_dir;
+    fsm->datafs->vfs_dirent = NULL;
+    if (shortlist != 0)
+    {
+        fsm->state = FTPD_NLST;
+    }
+    else
+    {
+        fsm->state = FTPD_LIST;
+    }
+    send_msg(pcb, fsm, msg150);
+}
+
+
+static void cmd_nlst(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    cmd_list_common(arg, pcb, fsm, 1);
+}
+
+static void cmd_list(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    cmd_list_common(arg, pcb, fsm, 0);
+}
+
+static void cmd_retr(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    vfs_file_t *vfs_file;
+    vfs_stat_t st;
+
+#if 0
+    vfs_stat(fsm->vfs, arg, &st);
+    if (!VFS_ISREG(st.st_mode))
+    {
+        send_msg(pcb, fsm, msg550);
+        return;
+    }
+#endif
+    vfs_file = vfs_open(fsm->vfs, arg, "rb");
+    if (!vfs_file) {
+        send_msg(pcb, fsm, msg550);
+        return;
+    }
+
+    send_msg(pcb, fsm, msg150recv, arg, st.st_size);
+
+    if (open_dataconnection(pcb, fsm) != 0) {
+        vfs_close(vfs_file);
+        return;
+    }
+
+    fsm->datafs->vfs_file = vfs_file;
+    fsm->state = FTPD_RETR;
+}
+
+static void cmd_stor(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    vfs_file_t *vfs_file;
+
+    vfs_file = vfs_open(fsm->vfs, arg, "wb");
+    if (!vfs_file) {
+        send_msg(pcb, fsm, msg550);
+        return;
+    }
+
+    send_msg(pcb, fsm, msg150stor, arg);
+
+    if (open_dataconnection(pcb, fsm) != 0) {
+        vfs_close(vfs_file);
+        return;
+    }
+
+    fsm->datafs->vfs_file = vfs_file;
+    fsm->state = FTPD_STOR;
+}
+
+static void cmd_noop(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    send_msg(pcb, fsm, msg200);
+}
+
+static void cmd_syst(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    send_msg(pcb, fsm, msg214SYST, "UNIX");
+}
+
+static void cmd_pasv(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    static u16_t port = 4096;
+    static u16_t start_port = 4096;
+    struct tcp_pcb *temppcb;
+
+    /* Allocate memory for the structure that holds the state of the        connection. */
+    fsm->datafs = (struct ftpd_datastate*)malloc(sizeof(struct ftpd_datastate));
+
+    if (fsm->datafs == NULL) {
+        send_msg(pcb, fsm, msg451);
+        return;
+    }
+    memset(fsm->datafs, 0, sizeof(struct ftpd_datastate));
+
+    fsm->datapcb = tcp_new();
+    if (!fsm->datapcb) {
+        free(fsm->datafs);
+        send_msg(pcb, fsm, msg451);
+        return;
+    }
+
+    sfifo_init(&fsm->datafs->fifo, 2000);
+
+    start_port = port;
+
+    while (1) {
+        err_t err;
+
+        if(++port > 0x7fff)
+            port = 4096;
+    
+        fsm->dataport = port;
+        err = tcp_bind(fsm->datapcb, &pcb->local_ip, fsm->dataport);
+        if (err == ERR_OK)
+            break;
+        if (start_port == port)
+            err = ERR_CLSD;
+        if (err == ERR_USE)
+            continue;
+        if (err != ERR_OK) {
+            ftpd_dataclose(fsm->datapcb, fsm->datafs);
+            fsm->datapcb = NULL;
+            fsm->datafs = NULL;
+            return;
+        }
+    }
+
+    temppcb = tcp_listen(fsm->datapcb);
+    if (!temppcb) {
+        ftpd_dataclose(fsm->datapcb, fsm->datafs);
+        fsm->datapcb = NULL;
+        fsm->datafs = NULL;
+        return;
+    }
+    fsm->datapcb = temppcb;
+
+    fsm->passive = 1;
+    fsm->datafs->connected = 0;
+    fsm->datafs->msgfs = fsm;
+    fsm->datafs->msgpcb = pcb;
+
+    /* Tell TCP that this is the structure we wish to be passed for our
+       callbacks. */
+    tcp_arg(fsm->datapcb, fsm->datafs);
+
+    tcp_accept(fsm->datapcb, ftpd_dataaccept);
+    send_msg(pcb, fsm, msg227, ip4_addr1(&pcb->local_ip), ip4_addr2(&pcb->local_ip), ip4_addr3(&pcb->local_ip), ip4_addr4(&pcb->local_ip), (fsm->dataport >> 8) & 0xff, (fsm->dataport) & 0xff);
+}
+
+static void cmd_abrt(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    if (fsm->datafs != NULL) {
+        tcp_arg(fsm->datapcb, NULL);
+        tcp_sent(fsm->datapcb, NULL);
+        tcp_recv(fsm->datapcb, NULL);
+        tcp_arg(fsm->datapcb, NULL);
+        tcp_abort(pcb);
+        sfifo_close(&fsm->datafs->fifo);
+        free(fsm->datafs);
+        fsm->datafs = NULL;
+    }
+    fsm->state = FTPD_IDLE;
+}
+
+static void cmd_type(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    dbg_printf("Got TYPE -%s-\n", arg);
+    //printit( "Got TYPE -" );
+    //printit( (char*)arg ); // ASCII text 
+
+    if( *arg == 'A' )
+    {
+        send_msg(pcb, fsm, "200 Type set to A." );
+    }
+    else
+    {
+        send_msg(pcb, fsm, msg502);
+    }
+}
+
+static void cmd_mode(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    dbg_printf("Got MODE -%s-\n", arg);
+    //printit( "Got MODE -" );
+    //printit( (char*)arg );
+    send_msg(pcb, fsm, msg502);
+}
+
+static void cmd_rnfr(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    if (arg == NULL) {
+        send_msg(pcb, fsm, msg501);
+        return;
+    }
+    if (*arg == '\0') {
+        send_msg(pcb, fsm, msg501);
+        return;
+    }
+    if (fsm->renamefrom)
+        free(fsm->renamefrom);
+    fsm->renamefrom = (char*)malloc(strlen(arg) + 1);
+    if (fsm->renamefrom == NULL) {
+        send_msg(pcb, fsm, msg451);
+        return;
+    }
+    strcpy(fsm->renamefrom, arg);
+    fsm->state = FTPD_RNFR;
+    send_msg(pcb, fsm, msg350);
+}
+
+static void cmd_rnto(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    if (fsm->state != FTPD_RNFR) {
+        send_msg(pcb, fsm, msg503);
+        return;
+    }
+    fsm->state = FTPD_IDLE;
+    if (arg == NULL) {
+        send_msg(pcb, fsm, msg501);
+        return;
+    }
+    if (*arg == '\0') {
+        send_msg(pcb, fsm, msg501);
+        return;
+    }
+    if (vfs_rename(fsm->vfs, fsm->renamefrom, arg)) {
+        send_msg(pcb, fsm, msg450);
+    } else {
+        send_msg(pcb, fsm, msg250);
+    }
+}
+
+static void cmd_mkd(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    if (arg == NULL) {
+        send_msg(pcb, fsm, msg501);
+        return;
+    }
+    if (*arg == '\0') {
+        send_msg(pcb, fsm, msg501);
+        return;
+    }
+    if (vfs_mkdir(fsm->vfs, arg, VFS_IRWXU | VFS_IRWXG | VFS_IRWXO) != 0) {
+        send_msg(pcb, fsm, msg550);
+    } else {
+        send_msg(pcb, fsm, msg257, arg);
+    }
+}
+
+static void cmd_nill(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    printit( "cmd_nill" );
+}
+
+
+static void cmd_rmd(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    vfs_stat_t st;
+
+    if (arg == NULL) {
+        send_msg(pcb, fsm, msg501);
+        return;
+    }
+    if (*arg == '\0') {
+        send_msg(pcb, fsm, msg501);
+        return;
+    }
+    if (vfs_stat(fsm->vfs, arg, &st) != 0) {
+        send_msg(pcb, fsm, msg550);
+        return;
+    }
+    if (!VFS_ISDIR(st.st_mode)) {
+        send_msg(pcb, fsm, msg550);
+        return;
+    }
+    if (vfs_rmdir(fsm->vfs, arg) != 0) {
+        send_msg(pcb, fsm, msg550);
+    } else {
+        send_msg(pcb, fsm, msg250);
+    }
+}
+
+static void cmd_dele(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    vfs_stat_t st;
+
+    if (arg == NULL) {
+        send_msg(pcb, fsm, msg501);
+        return;
+    }
+    if (*arg == '\0') {
+        send_msg(pcb, fsm, msg501);
+        return;
+    }
+#if 0
+    if (vfs_stat(fsm->vfs, arg, &st) != 0)
+    {
+        send_msg(pcb, fsm, msg550);
+        return;
+    }
+    if (!VFS_ISREG(st.st_mode))
+    {
+        send_msg(pcb, fsm, msg550);
+        return;
+    }
+#endif
+    if (vfs_remove(fsm->vfs, arg) != 0)
+    {
+        send_msg(pcb, fsm, msg550);
+    }
+    else
+    {
+        send_msg(pcb, fsm, msg250);
+    }
+}
+
+
+struct ftpd_command {
+    char *cmd;
+    void (*func) (const char *arg, struct tcp_pcb * pcb, struct ftpd_msgstate * fsm);
+};
+
+static struct ftpd_command ftpd_commands[] = {
+    "USER", cmd_user,
+    "PASS", cmd_pass,
+    "PORT", cmd_port,
+    "QUIT", cmd_quit,
+    "CWD",  cmd_cwd,
+    "CDUP", cmd_cdup,
+    "PWD",  cmd_pwd,
+    "XPWD", cmd_pwd,
+    "NLST", cmd_nlst,
+    "LIST", cmd_list,
+    "RETR", cmd_retr,
+    "STOR", cmd_stor,
+    "NOOP", cmd_noop,
+    "SYST", cmd_syst,
+    "ABOR", cmd_abrt,
+    "TYPE", cmd_type,
+    "MODE", cmd_mode,
+    "RNFR", cmd_rnfr,
+    "RNTO", cmd_rnto,
+    "MKD",  cmd_mkd,
+    "XMKD", cmd_mkd,
+    "RMD",  cmd_rmd,
+    "XRMD", cmd_rmd,
+    "DELE", cmd_dele,
+    //"PASV", cmd_pasv,
+    NULL
+};
+
+static void send_msgdata(struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    err_t err;
+    u16_t len;
+
+    if (sfifo_used(&fsm->fifo) > 0) {
+        int i;
+
+        /* We cannot send more data than space available in the send
+           buffer. */
+        if (tcp_sndbuf(pcb) < sfifo_used(&fsm->fifo)) {
+            len = tcp_sndbuf(pcb);
+        } else {
+            len = (u16_t) sfifo_used(&fsm->fifo);
+        }
+
+        i = fsm->fifo.readpos;
+        if ((i + len) > fsm->fifo.size) {
+            err = tcp_write(pcb, fsm->fifo.buffer + i, (u16_t)(fsm->fifo.size - i), 1);
+            if (err != ERR_OK) {
+                dbg_printf("send_msgdata: error writing!\n");
+                return;
+            }
+            len -= fsm->fifo.size - i;
+            fsm->fifo.readpos = 0;
+            i = 0;
+        }
+
+        err = tcp_write(pcb, fsm->fifo.buffer + i, len, 1);
+        if (err != ERR_OK) {
+            dbg_printf("send_msgdata: error writing!\n");
+            return;
+        }
+        fsm->fifo.readpos += len;
+    }
+}
+
+static void send_msg(struct tcp_pcb *pcb, struct ftpd_msgstate *fsm, char *msg, ...)
+{
+    va_list arg;
+    char buffer[1024];
+    int len;
+
+    va_start(arg, msg);
+    vsprintf(buffer, msg, arg);
+    va_end(arg);
+    strcat(buffer, "\r\n");
+    len = strlen(buffer);
+    if (sfifo_space(&fsm->fifo) < len)
+        return;
+    sfifo_write(&fsm->fifo, buffer, len);
+    dbg_printf("response: %s", buffer);
+    //printit( buffer );
+    send_msgdata(pcb, fsm);
+}
+
+static void ftpd_msgerr(void *arg, err_t err)
+{
+    struct ftpd_msgstate *fsm = (struct ftpd_msgstate*)arg;
+
+//    printit( "ftpd_msgerr" );
+    dbg_printf("ftpd_msgerr: %s (%i)\n", lwip_strerr(err), err);
+    if (fsm == NULL)
+        return;
+    if (fsm->datafs)
+        ftpd_dataclose(fsm->datapcb, fsm->datafs);
+    sfifo_close(&fsm->fifo);
+    vfs_close( (vfs_file_t*)fsm->vfs);
+    fsm->vfs = NULL;
+    if (fsm->renamefrom)
+        free(fsm->renamefrom);
+    fsm->renamefrom = NULL;
+    free(fsm);
+}
+
+static void ftpd_msgclose(struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
+{
+    //printit( "ftpd_msgclose" );
+    tcp_arg(pcb, NULL);
+    tcp_sent(pcb, NULL);
+    tcp_recv(pcb, NULL);
+    if (fsm->datafs)
+        ftpd_dataclose(fsm->datapcb, fsm->datafs);
+    sfifo_close(&fsm->fifo);
+    vfs_close(  (vfs_file_t*)fsm->vfs);
+    fsm->vfs = NULL;
+    if (fsm->renamefrom)
+        free(fsm->renamefrom);
+    fsm->renamefrom = NULL;
+    free(fsm);
+    tcp_arg(pcb, NULL);
+    tcp_close(pcb);
+}
+
+static err_t ftpd_msgsent(void *arg, struct tcp_pcb *pcb, u16_t len)
+{
+    struct ftpd_msgstate *fsm = (struct ftpd_msgstate*)arg;
+
+//    printit( "ftpd_msgsent" );
+    if (pcb->state > ESTABLISHED)
+        return ERR_OK;
+
+    if ((sfifo_used(&fsm->fifo) == 0) && (fsm->state == FTPD_QUIT))
+        ftpd_msgclose(pcb, fsm);
+
+    send_msgdata(pcb, fsm);
+
+    return ERR_OK;
+}
+
+
+void bcopy (char *src, char *dest, int len)
+{
+  if (dest < src)
+    while (len--)
+      *dest++ = *src++;
+  else
+    {
+      char *lasts = src + (len-1);
+      char *lastd = dest + (len-1);
+      while (len--)
+        *(char *)lastd-- = *(char *)lasts--;
+    }
+}
+
+static err_t ftpd_msgrecv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+    char *text;
+    struct ftpd_msgstate *fsm = (struct ftpd_msgstate*)arg;
+
+//    printit( "ftpd_msgrecv" );
+    //printit( arg );
+    if (err == ERR_OK && p != NULL) {
+
+        /* Inform TCP that we have taken the data. */
+        tcp_recved(pcb, p->tot_len);
+
+        text = (char*)malloc(p->tot_len + 1);
+        if (text) {
+            char cmd[5];
+            struct pbuf *q;
+            char *pt = text;
+            struct ftpd_command *ftpd_cmd;
+
+            for (q = p; q != NULL; q = q->next)
+            {
+                bcopy( (char*)q->payload, pt, q->len);
+                pt += q->len;
+            }
+            *pt = '\0';
+
+            pt = &text[strlen(text) - 1];
+            while (((*pt == '\r') || (*pt == '\n')) && pt >= text)
+                *pt-- = '\0';
+
+            dbg_printf("query: %s\n", text);
+            //printit( "query: " );
+            //printit( text );
+
+            strncpy(cmd, text, 4);
+            for (pt = cmd; isalpha(*pt) && pt < &cmd[4]; pt++)
+                *pt = toupper(*pt);
+            *pt = '\0';
+
+            for (ftpd_cmd = ftpd_commands; ftpd_cmd->cmd != NULL; ftpd_cmd++) {
+                if (!strcmp(ftpd_cmd->cmd, cmd))
+                    break;
+            }
+
+            if (strlen(text) < (strlen(cmd) + 1))
+                pt = "";
+            else
+                pt = &text[strlen(cmd) + 1];
+
+            if (ftpd_cmd->func)
+                ftpd_cmd->func(pt, pcb, fsm);
+            else
+                send_msg(pcb, fsm, msg502);
+
+            free(text);
+        }
+        pbuf_free(p);
+    }
+
+    return ERR_OK;
+}
+
+static err_t ftpd_msgpoll(void *arg, struct tcp_pcb *pcb)
+{
+    struct ftpd_msgstate *fsm = (struct ftpd_msgstate*)arg;
+
+    //printit( "ftpd_msgpoll" );
+    if (fsm == NULL)
+        return ERR_OK;
+
+    if (fsm->datafs) {
+        if (fsm->datafs->connected) {
+            switch (fsm->state) {
+            case FTPD_LIST:
+                send_next_directory(fsm->datafs, fsm->datapcb, 0);
+                break;
+            case FTPD_NLST:
+                send_next_directory(fsm->datafs, fsm->datapcb, 1);
+                break;
+            case FTPD_RETR:
+                send_file(fsm->datafs, fsm->datapcb);
+                break;
+            default:
+                break;
+            }
+        }
+    }
+
+    return ERR_OK;
+}
+
+static err_t ftpd_msgaccept(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+    struct ftpd_msgstate *fsm;
+
+
+//printit( "FTP call" );
+    /* Allocate memory for the structure that holds the state of the
+       connection. */
+    fsm = (struct ftpd_msgstate*)malloc(sizeof(struct ftpd_msgstate));
+
+    if (fsm == NULL) {
+        dbg_printf("ftpd_msgaccept: Out of memory\n");
+        return ERR_MEM;
+    }
+    memset(fsm, 0, sizeof(struct ftpd_msgstate));
+
+    /* Initialize the structure. */
+    sfifo_init(&fsm->fifo, 2000);
+    fsm->state = FTPD_IDLE;
+    fsm->vfs = vfs_openfs();
+#if 0
+    if (!fsm->vfs)
+    {
+        free(fsm);
+        return ERR_CLSD;
+    }
+#endif
+    /* Tell TCP that this is the structure we wish to be passed for our
+       callbacks. */
+    tcp_arg( pcb, fsm );
+
+    /* Tell TCP that we wish to be informed of incoming data by a call
+       to the http_recv() function. */
+    tcp_recv( pcb, ftpd_msgrecv );
+
+    /* Tell TCP that we wish be to informed of data that has been
+       successfully sent by a call to the ftpd_sent() function. */
+    tcp_sent( pcb, ftpd_msgsent );
+
+    tcp_err( pcb, ftpd_msgerr );
+
+    tcp_poll(pcb, ftpd_msgpoll, 1);
+
+    send_msg(pcb, fsm, msg220);
+
+    return ERR_OK;
+}
+
+
+void ftpd_init(void)
+{
+    struct tcp_pcb *pcb;
+
+//..    vfs_load_plugin(vfs_default_fs);
+
+    pcb = tcp_new();
+    tcp_bind( pcb, IP_ADDR_ANY, 21 );
+    pcb = tcp_listen( pcb );
+    tcp_accept( pcb, ftpd_msgaccept );
+}
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NetFtp/ftpd.h	Tue Nov 30 00:45:55 2010 +0000
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2002 Florian Schulze.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the authors nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * ftpd.h - This file is part of the FTP daemon for lwIP
+ *
+ */
+
+#ifndef __FTPD_H__
+#define __FTPD_H__
+
+//..void ftpd_init(void);
+
+#endif                /* __FTPD_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NetFtp/vfs.c	Tue Nov 30 00:45:55 2010 +0000
@@ -0,0 +1,146 @@
+
+#include "mbed.h"                  // DRB
+#include "SDFileSystem.h"          // DRB
+#include "vfs.h"
+}
+
+extern void printit( char *p_buf );
+
+
+int  *vfs_openfs( void )
+{
+//    printit( "FTP vfs_openfs" );
+    return NULL;
+}
+
+
+vfs_file_t *vfs_open( int *fd, const char *fname, const char *mode )
+{
+#if 0
+    char what[ 40 ];
+    
+//    printit( "FTP vfs_open" );
+    printit( (char*)fname );
+    sprintf( what, "/sd/%s", fname );
+//    printit( what );
+    return (vfs_file_t *)fopen( what, mode );
+#else
+    return (vfs_file_t *)fopen( fname, mode );
+#endif
+}
+
+
+ssize_t vfs_read( void *buf,int what, int BufferSize, vfs_file_t *fp )
+{
+#if 0
+    char txt[ 40 ];
+    
+    sprintf( txt, "FTP vfs_read,%u", BufferSize );
+    printit( txt );
+    memset( buf, 0xAA, BufferSize );
+#endif
+    return fread( buf, 1, BufferSize, (FILE*)fp);
+}
+
+
+int vfs_write(  void *buf,int what, int BufferSize, vfs_file_t *fp  )
+{
+    return fwrite( buf, 1, BufferSize, (FILE*)fp);
+}
+
+
+int vfs_eof( vfs_file_t *fp )
+{
+    //printit( "vfs_eof" );
+    return feof( (FILE*)fp );
+//    return 0; // not eof
+}
+
+
+int vfs_close( vfs_file_t *fp  )
+{
+    //printit( "vfs_close" );
+    if( NULL != fp )
+    {
+        fclose( (FILE*)fp );
+    }
+    else
+    {
+        //printit( "vfs_close--opps!!!" );
+    }
+    return 0;
+}
+
+
+int vfs_stat( int *fd, const char *fname, vfs_stat_t *st )
+{
+    printit( "" );
+    printit( (char*)fname );
+    st->st_mode = 1; // ok
+    st->st_size = 123; // file size
+    return 1;
+}
+
+
+/*
+ * Find pathname of process's current directory.
+ *
+ * Use vfs vnode-to-name reverse cache; if that fails, fall back
+ * to reading directory contents.
+ */
+char *vfs_getcwd( int *fd, void *x, int y )
+{
+    printit( "vfs_getcwd" );
+    return (char*)malloc( 123 );
+}
+
+
+vfs_dir_t    *vfs_opendir( int *fd, char *cwd )
+{
+    printit( "FTP vfs_opendir" );
+    return 0;
+}
+
+
+vfs_dirent_t *vfs_readdir( vfs_dir_t *fd )
+{
+    printit( "FTP vfs_readdir" );
+    return 0;
+}
+
+
+int vfs_closedir( vfs_dir_t *fd )
+{
+    return 0;
+}
+
+
+int vfs_mkdir( int *fd, const char *arg, int mode )
+{
+    return 0;
+}
+
+
+int vfs_rmdir( int *fd, const char *arg )
+{
+    return 0;
+}
+
+
+int vfs_rename( int *fd, char *oldname, const char *newname )
+{
+    return rename( oldname, newname );
+}
+
+
+int vfs_remove( int *fd, const char *arg )
+{
+    return remove( arg );
+}
+
+
+int vfs_chdir( int *fd, const char *arg )
+{
+    return 0;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NetFtp/vfs.h	Tue Nov 30 00:45:55 2010 +0000
@@ -0,0 +1,263 @@
+/**
+ * @file ap_vfs.h
+ * @brief Virtual File System interface
+ *
+ * @defgroup APACHE_CORE_VFS Virtual File System interface
+ * @ingroup  APACHE_CORE
+ * @{
+ */
+
+#ifndef APACHE_VFS_H
+#define APACHE_VFS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//..#include "apr.h"
+//..#include "apr_file_io.h"
+
+//..#include "httpd.h"
+
+#define AP_USE_VFS 1
+#if !AP_USE_VFS
+
+/* VFS Interface disabled */
+
+#define vfs_file_t apr_file_t
+#define vfs_dir_t apr_dir_t
+
+#define ap_vfs_stat(r, finfo, fname, wanted, pool) \
+    apr_stat(finfo, fname, wanted, pool)
+#define ap_vfs_file_open(r, new, fname, flag, perm, pool) \
+    apr_file_open(new, fname, flag, perm, pool)
+#define ap_vfs_file_read(thefile, buf, nbytes) \
+    apr_file_read(thefile, buf, nbytes)
+#define ap_vfs_file_write(thefile, buf, nbytes) \
+    apr_file_write(thefile, buf, nbytes)
+#define ap_vfs_file_seek(thefile, where, offset) \
+    apr_file_seek(thefile, where, offset)
+#define ap_vfs_file_eof(thefile) \
+    apr_file_eof(thefile)
+#define ap_vfs_file_close(thefile) \
+    apr_file_close(thefile)
+#define ap_vfs_file_remove(r, path, pool) \
+    apr_file_remove(path, pool)
+#define ap_vfs_file_rename(r, from_path, to_path, p) \
+    apr_file_rename(from_path, to_path, p)
+#define ap_vfs_file_perms_set(r, fname, perms) \
+    apr_file_perms_set(fname, perms)
+#define ap_vfs_dir_open(r, new, dirname, pool) \
+    apr_dir_open(new, dirname, pool)
+#define ap_vfs_dir_read(finfo, wanted, thedir) \
+    apr_dir_read(finfo, wanted, thedir)
+#define ap_vfs_dir_close(thedir) \
+    apr_dir_close(thedir)
+#define ap_vfs_dir_make(r, path, perm, pool) \
+    apr_dir_make(path, perm, pool)
+#define ap_vfs_dir_remove(r, path, pool) \
+    apr_dir_remove(path, pool)
+
+#else
+
+/* VFS Public Interface */
+
+#define AP_DECLARE(x) x
+typedef int apr_status_t; // DRB
+
+//..typedef struct vfs_file_t vfs_file_t;
+typedef struct vfs_dir_t vfs_dir_t;
+typedef struct vfs_mount vfs_mount;
+
+#if 0 // DRB
+AP_DECLARE(apr_status_t) ap_vfs_stat(const request_rec *r,
+                                     apr_finfo_t *finfo, 
+                                     const char *fname, 
+                                     apr_int32_t wanted,
+                                     apr_pool_t *pool);
+
+AP_DECLARE(apr_status_t) ap_vfs_file_open(const request_rec *r,
+                                          vfs_file_t **new, 
+                                          const char *fname, 
+                                          apr_int32_t flag, 
+                                          apr_fileperms_t perm, 
+                                          apr_pool_t *pool);
+
+AP_DECLARE(apr_status_t) ap_vfs_file_read(vfs_file_t *thefile,
+					  void *buf,
+					  apr_size_t *nbytes);
+
+AP_DECLARE(apr_status_t) ap_vfs_file_write(vfs_file_t *thefile,
+					   const void *buf,
+					   apr_size_t *nbytes);
+
+AP_DECLARE(apr_status_t) ap_vfs_file_seek(vfs_file_t *thefile, 
+					  apr_seek_where_t where,
+					  apr_off_t *offset);
+
+AP_DECLARE(apr_status_t) ap_vfs_file_eof(vfs_file_t *file);
+
+AP_DECLARE(apr_status_t) ap_vfs_file_close(vfs_file_t *file);
+
+AP_DECLARE(apr_status_t) ap_vfs_file_remove(const request_rec *r,
+                                            const char *path,
+                                            apr_pool_t *pool);
+
+AP_DECLARE(apr_status_t) ap_vfs_file_rename(const request_rec *r,
+                                            const char *from_path, 
+                                            const char *to_path,
+                                            apr_pool_t *p);
+
+AP_DECLARE(apr_status_t) ap_vfs_file_perms_set(const request_rec *r,
+                                               const char *fname, 
+                                               apr_fileperms_t perms);
+
+AP_DECLARE(apr_status_t) ap_vfs_dir_open(const request_rec *r,
+                                         vfs_dir_t **new,
+                                         const char *dirname, 
+                                         apr_pool_t *pool);
+
+AP_DECLARE(apr_status_t) ap_vfs_dir_read(apr_finfo_t *finfo,
+                                         apr_int32_t wanted,
+                                         vfs_dir_t *thedir);
+
+AP_DECLARE(apr_status_t) ap_vfs_dir_close(vfs_dir_t *thedir);
+
+AP_DECLARE(apr_status_t) ap_vfs_dir_make(const request_rec *r,
+                                         const char *path,
+                                         apr_fileperms_t perm, 
+                                         apr_pool_t *pool);
+
+AP_DECLARE(apr_status_t) ap_vfs_dir_remove(const request_rec *r,
+                                           const char *path,
+                                           apr_pool_t *pool);
+
+/* VFS Provider interface */
+
+struct vfs_mount {
+    vfs_mount     *next;
+    apr_uint32_t  flags;
+    const char    *mountpoint;
+    void          *userdata;
+
+    /* VFS implementation hooks */
+    apr_status_t (*stat)          (const vfs_mount *mnt, const request_rec *r,
+				   apr_finfo_t *finfo, const char *fname, 
+				   apr_int32_t wanted, apr_pool_t *pool);
+    apr_status_t (*file_open)     (const vfs_mount *mnt, const request_rec *r,
+				   vfs_file_t **new,  const char *fname, 
+				   apr_int32_t flag,  apr_fileperms_t perm, 
+				   apr_pool_t *pool);
+    apr_status_t (*file_read)     (const vfs_mount *mnt, vfs_file_t *thefile,
+				   void *buf, apr_size_t *nbytes);
+    apr_status_t (*file_write)    (const vfs_mount *mnt, vfs_file_t *thefile,
+				   const void *buf, apr_size_t *nbytes);
+    apr_status_t (*file_seek)     (const vfs_mount *mnt, vfs_file_t *thefile, 
+				   apr_seek_where_t where, apr_off_t *offset);
+    apr_status_t (*file_eof)      (const vfs_mount *mnt, vfs_file_t *file);
+    apr_status_t (*file_close)    (const vfs_mount *mnt, vfs_file_t *file);
+    apr_status_t (*file_remove)   (const vfs_mount *mnt, const request_rec *r,
+				   const char *path, apr_pool_t *pool);
+    apr_status_t (*file_rename)   (const vfs_mount *mnt, const request_rec *r,
+				   const char *from_path, const char *to_path,
+				   apr_pool_t *p);
+    apr_status_t (*file_perms_set)(const vfs_mount *mnt, const request_rec *r,
+				   const char *fname,  apr_fileperms_t perms);
+    apr_status_t (*dir_open)      (const vfs_mount *mnt, const request_rec *r,
+				   vfs_dir_t **new, const char *dirname, 
+				   apr_pool_t *pool);
+    apr_status_t (*dir_read)      (const vfs_mount *mnt, apr_finfo_t *finfo,
+				   apr_int32_t wanted, vfs_dir_t *thedir);
+    apr_status_t (*dir_close)     (const vfs_mount *mnt, vfs_dir_t *thedir);
+    apr_status_t (*dir_make)      (const vfs_mount *mnt, const request_rec *r,
+				   const char *path, apr_fileperms_t perm, 
+				   apr_pool_t *pool);
+    apr_status_t (*dir_remove)    (const vfs_mount *mnt, const request_rec *r,
+				   const char *path, apr_pool_t *pool);
+};
+
+/* VFS file type */
+
+struct vfs_file_t {
+    vfs_mount    *mnt;
+    void         *userdata;
+};
+
+/* VFS directory type */
+
+struct vfs_dir_t {
+    vfs_mount    *mnt;
+    void         *userdata;
+};
+
+/* linked list of vfs_mount structures */
+
+extern vfs_mount *ap_vfs_mount_head;
+#else
+
+#define VFS_IRWXU 1
+#define VFS_IRWXG 2
+#define VFS_IRWXO 4
+#define ssize_t int
+#define loff_t  int
+#define filldir_t int
+#define VFS_ISDIR(x)   ( x & 1) // ((x) && (x) type == VFS_DIR) //! Macro to know if a given entry represents a directory 
+#define VFS_ISREG( x ) ( x & 2)
+
+#define vfs_t int                
+typedef struct 
+{
+        int st_size;
+        time_t st_mtime;
+        int st_mode;
+} vfs_stat_t;
+
+typedef struct 
+{
+        char *name;
+} vfs_dirent_t;
+
+
+/* VFS file type */
+
+struct vfs_file_t {
+    vfs_mount    *mnt;
+    void         *userdata;
+};
+
+/* VFS directory type */
+
+struct vfs_dir_t {
+    vfs_mount    *mnt;
+    void         *userdata;
+};
+
+int *vfs_openfs( void );
+vfs_file_t *vfs_open( int *fd, const char *arg, const char *mode );
+ssize_t vfs_read( void *buf,int what, int BufferSize, vfs_file_t *fp );
+int vfs_write(  void *buf,int what, int BufferSize, vfs_file_t *fp  );
+int vfs_eof( vfs_file_t *fp );
+int vfs_close( vfs_file_t *fp  );
+int vfs_stat( int *fd, const char *fname, vfs_stat_t *st );
+char *vfs_getcwd( int *fd, void *x, int y );
+vfs_dir_t    *vfs_opendir( int *fd, char *cwd );
+vfs_dirent_t *vfs_readdir( vfs_dir_t *fd );
+int vfs_closedir( vfs_dir_t *fd );
+int vfs_mkdir( int *fd, const char *arg, int mode );
+int vfs_rmdir( int *fd, const char *arg );
+int vfs_rename( int *fd, char *was, const char *arg );
+int vfs_remove( int *fd, const char *arg );
+int vfs_chdir( int *fd, const char *arg );
+
+
+//#define vfs_eof( a )
+//#define vfs_close( a )         fclose( a )
+//#define vfs_readdir( a )       readdir( a )
+//#define vfs_stat( a, b, c )    fstat( a, b )
+//#define vfs_closedir( a )      closedir( a )
+
+#endif // DRB
+
+#endif /* AP_USE_VFS */
+
+#endif /* APACHE_VFS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Tue Nov 30 00:45:55 2010 +0000
@@ -0,0 +1,12 @@
+#include "mbed.h"
+
+DigitalOut myled(LED1);
+
+int main() {
+    while(1) {
+        myled = 1;
+        wait(0.2);
+        myled = 0;
+        wait(0.2);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Tue Nov 30 00:45:55 2010 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/e2ac27c8e93e