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

Dependencies:   mbed

Revision:
0:c9ac16a7dfb4
--- /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 );
+}
+
+
+