| /* |
| * Copyright (c) 2004, Adam Dunkels. |
| * 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 Institute nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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. |
| * |
| * This file is part of the Contiki operating system. |
| * |
| * Author: Adam Dunkels <adam@sics.se> |
| * |
| * $Id: ftpc.c,v 1.3 2005/04/17 22:42:33 oliverschmidt Exp $ |
| */ |
| #include "contiki.h" |
| #include "ftpc.h" |
| |
| #include <string.h> |
| #include <stdio.h> |
| |
| #define ISO_nl 0x0a |
| #define ISO_cr 0x0d |
| |
| #define DATAPORT 6510 |
| |
| #define MAX_FILENAMELEN 32 |
| |
| struct ftp_dataconn { |
| unsigned char type; |
| unsigned char conntype; |
| #define CONNTYPE_LIST 0 |
| #define CONNTYPE_FILE 1 |
| |
| u16_t port; |
| |
| unsigned char filenameptr; |
| char filename[MAX_FILENAMELEN]; |
| |
| |
| }; |
| |
| struct ftp_connection { |
| unsigned char type; |
| #define TYPE_CONTROL 1 |
| #define TYPE_DATA 2 |
| #define TYPE_ABORT 3 |
| #define TYPE_CLOSE 4 |
| |
| unsigned char state; |
| #define STATE_NONE 0 |
| #define STATE_INITIAL 1 |
| #define STATE_SEND_USER 2 |
| #define STATE_USER_SENT 3 |
| #define STATE_SEND_PASS 4 |
| #define STATE_PASS_SENT 5 |
| #define STATE_SEND_PORT 6 |
| #define STATE_PORT_SENT 7 |
| #define STATE_SEND_OPTIONS 8 |
| #define STATE_OPTION_SENT 9 |
| #define STATE_CONNECTED 10 |
| #define STATE_SEND_NLST 11 |
| #define STATE_NLST_SENT 12 |
| #define STATE_SEND_RETR 13 |
| #define STATE_RETR_SENT 14 |
| |
| #define STATE_SEND_CWD 15 |
| #define STATE_CWD_SENT 16 |
| |
| #define STATE_SEND_CDUP 17 |
| #define STATE_CDUP_SENT 18 |
| |
| #define STATE_SEND_QUIT 19 |
| #define STATE_QUIT_SENT 20 |
| |
| unsigned char connected_confirmed; |
| |
| struct ftp_dataconn dataconn; |
| |
| char code[3]; |
| unsigned char codeptr; |
| |
| unsigned char optionsptr; |
| |
| char filename[MAX_FILENAMELEN]; |
| |
| }; |
| |
| #define NUM_OPTIONS 1 |
| static const struct { |
| unsigned char num; |
| char *commands[NUM_OPTIONS]; |
| } options = { |
| NUM_OPTIONS, |
| {"TYPE I\r\n"} |
| }; |
| |
| static struct ftp_connection *waiting_for_dataconn; |
| |
| MEMB(connections, sizeof(struct ftp_connection), 1); |
| |
| /*---------------------------------------------------------------------------*/ |
| void |
| ftpc_init(void) |
| { |
| memb_init(&connections); |
| /* tcp_listen(HTONS(DATAPORT));*/ |
| } |
| /*---------------------------------------------------------------------------*/ |
| void * |
| ftpc_connect(u16_t *ipaddr, u16_t port) |
| { |
| struct ftp_connection *c; |
| |
| c = (struct ftp_connection *)memb_alloc(&connections); |
| if(c == NULL) { |
| return NULL; |
| } |
| c->type = TYPE_CONTROL; |
| c->state = STATE_INITIAL; |
| c->connected_confirmed = 0; |
| c->codeptr = 0; |
| c->dataconn.type = TYPE_DATA; |
| c->dataconn.port = DATAPORT; |
| tcp_listen(HTONS(DATAPORT)); |
| |
| if(tcp_connect(ipaddr, port, c) == NULL) { |
| memb_free(&connections, c); |
| return NULL; |
| } |
| |
| return c; |
| } |
| /*---------------------------------------------------------------------------*/ |
| static void |
| handle_input(struct ftp_connection *c) |
| { |
| int code; |
| |
| code = (c->code[0] - '0') * 100 + |
| (c->code[1] - '0') * 10 + |
| (c->code[2] - '0'); |
| /* printf("Handle input code %d state %d\n", code, c->state);*/ |
| |
| if(c->state == STATE_INITIAL) { |
| if(code == 220) { |
| c->state = STATE_SEND_USER; |
| } |
| } else if(c->state == STATE_USER_SENT) { |
| if(code == 331) { |
| c->state = STATE_SEND_PASS; |
| } |
| } else if(c->state == STATE_PASS_SENT) { |
| if(code == 230) { |
| c->state = STATE_SEND_OPTIONS; |
| c->optionsptr = 0; |
| } |
| } else if(c->state == STATE_PORT_SENT) { |
| c->state = STATE_CONNECTED; |
| if(c->connected_confirmed == 0) { |
| ftpc_connected(c); |
| c->connected_confirmed = 1; |
| } |
| } else if(c->state == STATE_OPTION_SENT) { |
| if(c->optionsptr >= options.num) { |
| c->state = STATE_SEND_PORT; |
| } else { |
| c->state = STATE_SEND_OPTIONS; |
| } |
| } else if((c->state == STATE_NLST_SENT || |
| c->state == STATE_RETR_SENT || |
| c->state == STATE_CONNECTED)) { |
| if(code == 226 || code == 550) { |
| tcp_unlisten(htons(c->dataconn.port)); |
| ++c->dataconn.port; |
| tcp_listen(htons(c->dataconn.port)); |
| c->state = STATE_SEND_PORT; |
| } |
| |
| if(code == 550) { |
| ftpc_list_file(NULL); |
| } |
| } else if(c->state == STATE_CWD_SENT || |
| c->state == STATE_CDUP_SENT) { |
| c->state = STATE_CONNECTED; |
| ftpc_cwd_done(code); |
| /* } else if(c->state == STATE_) { |
| c->state = STATE_CONNECTED;*/ |
| } |
| } |
| /*---------------------------------------------------------------------------*/ |
| static void |
| newdata(struct ftp_connection *c) |
| { |
| u16_t i; |
| u8_t d; |
| |
| for(i = 0; i < uip_datalen(); ++i) { |
| d = ((char *)uip_appdata)[i]; |
| if(c->codeptr < sizeof(c->code)) { |
| c->code[c->codeptr] = d; |
| ++c->codeptr; |
| } |
| |
| if(d == ISO_nl) { |
| handle_input(c); |
| c->codeptr = 0; |
| } |
| } |
| } |
| /*---------------------------------------------------------------------------*/ |
| static void |
| acked(struct ftp_connection *c) |
| { |
| switch(c->state) { |
| case STATE_SEND_USER: |
| c->state = STATE_USER_SENT; |
| break; |
| case STATE_SEND_PASS: |
| c->state = STATE_PASS_SENT; |
| break; |
| case STATE_SEND_PORT: |
| c->state = STATE_PORT_SENT; |
| break; |
| case STATE_SEND_OPTIONS: |
| ++c->optionsptr; |
| c->state = STATE_OPTION_SENT; |
| break; |
| case STATE_SEND_NLST: |
| c->state = STATE_NLST_SENT; |
| break; |
| case STATE_SEND_RETR: |
| c->state = STATE_RETR_SENT; |
| break; |
| case STATE_SEND_CWD: |
| c->state = STATE_CWD_SENT; |
| break; |
| case STATE_SEND_CDUP: |
| c->state = STATE_CDUP_SENT; |
| break; |
| case STATE_SEND_QUIT: |
| c->state = STATE_QUIT_SENT; |
| uip_close(); |
| break; |
| } |
| } |
| /*---------------------------------------------------------------------------*/ |
| static void |
| senddata(struct ftp_connection *c) |
| { |
| u16_t len; |
| |
| switch(c->state) { |
| case STATE_SEND_USER: |
| strcpy(uip_appdata, "USER "); |
| strncpy(uip_appdata + 5, ftpc_username(), uip_mss() - 7); |
| len = strlen(ftpc_username()); |
| strcpy(uip_appdata + 5 + len, "\r\n"); |
| uip_send(uip_appdata, len + 2 + 5); |
| break; |
| case STATE_SEND_PASS: |
| strcpy(uip_appdata, "PASS "); |
| strncpy(uip_appdata + 5, ftpc_password(), uip_mss() - 7); |
| len = strlen(ftpc_password()); |
| strcpy(uip_appdata + 5 + len, "\r\n"); |
| uip_send(uip_appdata, len + 2 + 5); |
| break; |
| case STATE_SEND_PORT: |
| len = sprintf(uip_appdata, "PORT %d,%d,%d,%d,%d,%d\n", |
| uip_ipaddr1(uip_hostaddr), |
| uip_ipaddr2(uip_hostaddr), |
| uip_ipaddr3(uip_hostaddr), |
| uip_ipaddr4(uip_hostaddr), |
| (c->dataconn.port) >> 8, |
| (c->dataconn.port) & 0xff); |
| uip_send(uip_appdata, len); |
| break; |
| case STATE_SEND_OPTIONS: |
| len = strlen(options.commands[c->optionsptr]); |
| uip_send(options.commands[c->optionsptr], len); |
| break; |
| case STATE_SEND_NLST: |
| uip_send("NLST\r\n", 6); |
| break; |
| case STATE_SEND_RETR: |
| len = sprintf(uip_appdata, "RETR %s\r\n", c->filename); |
| uip_send(uip_appdata, len); |
| break; |
| case STATE_SEND_CWD: |
| len = sprintf(uip_appdata, "CWD %s\r\n", c->filename); |
| uip_send(uip_appdata, len); |
| break; |
| case STATE_SEND_CDUP: |
| uip_send("CDUP\r\n", 6); |
| break; |
| case STATE_SEND_QUIT: |
| uip_send("QUIT\r\n", 6); |
| break; |
| } |
| } |
| /*---------------------------------------------------------------------------*/ |
| void |
| ftpc_appcall(void *state) |
| { |
| int i, t; |
| struct ftp_connection *c = (struct ftp_connection *)state; |
| struct ftp_dataconn *d = (struct ftp_dataconn *)state; |
| |
| if(uip_connected()) { |
| if(state == NULL) { |
| if(waiting_for_dataconn != NULL) { |
| d = &waiting_for_dataconn->dataconn; |
| waiting_for_dataconn = NULL; |
| tcp_markconn(uip_conn, d); |
| d->filenameptr = 0; |
| |
| } else { |
| uip_abort(); |
| } |
| } else { |
| /* tcp_listen(uip_conn->lport);*/ |
| senddata(c); |
| } |
| return; |
| } |
| |
| if(c->type == TYPE_ABORT) { |
| uip_abort(); |
| return; |
| } |
| |
| if(c->type == TYPE_CLOSE) { |
| uip_close(); |
| c->type = TYPE_CONTROL; |
| return; |
| } |
| |
| if(c->type == TYPE_CONTROL) { |
| if(uip_closed()) { |
| c->dataconn.type = TYPE_ABORT; |
| ftpc_closed(); |
| memb_free(&connections, c); |
| } |
| if(uip_aborted()) { |
| c->dataconn.type = TYPE_ABORT; |
| ftpc_aborted(); |
| memb_free(&connections, c); |
| } |
| if(uip_timedout()) { |
| c->dataconn.type = TYPE_ABORT; |
| ftpc_timedout(); |
| memb_free(&connections, c); |
| } |
| |
| |
| if(uip_acked()) { |
| acked(c); |
| } |
| if(uip_newdata()) { |
| newdata(c); |
| } |
| if(uip_rexmit() || |
| uip_newdata() || |
| uip_acked()) { |
| senddata(c); |
| } else if(uip_poll()) { |
| senddata(c); |
| } |
| } else { |
| if(d->conntype == CONNTYPE_LIST) { |
| if(uip_newdata()) { |
| for(i = 0; i < uip_datalen(); ++i) { |
| t = ((char *)uip_appdata)[i]; |
| |
| if(d->filenameptr < sizeof(d->filename) - 1 && |
| t != ISO_cr && |
| t != ISO_nl) { |
| d->filename[d->filenameptr] = t; |
| ++d->filenameptr; |
| } |
| |
| if(t == ISO_nl) { |
| d->filename[d->filenameptr] = 0; |
| ftpc_list_file(d->filename); |
| d->filenameptr = 0; |
| } |
| |
| } |
| } |
| if(uip_closed()) { |
| ftpc_list_file(NULL); |
| } |
| } else { |
| if(uip_newdata()) { |
| ftpc_data(uip_appdata, uip_datalen()); |
| /* printf("Received %d data bytes: '%s'\n", |
| uip_datalen(), uip_appdata);*/ |
| } else if(uip_closed() || uip_timedout() || uip_aborted()) { |
| ftpc_data(NULL, 0); |
| } |
| } |
| } |
| } |
| /*---------------------------------------------------------------------------*/ |
| char |
| ftpc_list(void *conn) |
| { |
| struct ftp_connection *c; |
| |
| c = conn; |
| |
| if(c == NULL || |
| c->state != STATE_CONNECTED) { |
| return 0; |
| } |
| |
| c->state = STATE_SEND_NLST; |
| c->dataconn.conntype = CONNTYPE_LIST; |
| waiting_for_dataconn = c; |
| return 1; |
| } |
| /*---------------------------------------------------------------------------*/ |
| char |
| ftpc_get(void *conn, char *filename) |
| { |
| struct ftp_connection *c; |
| |
| c = conn; |
| |
| if(c == NULL || |
| c->state != STATE_CONNECTED) { |
| return 0; |
| } |
| |
| strncpy(c->filename, filename, sizeof(c->filename)); |
| |
| c->state = STATE_SEND_RETR; |
| c->dataconn.conntype = CONNTYPE_FILE; |
| waiting_for_dataconn = c; |
| return 1; |
| } |
| /*---------------------------------------------------------------------------*/ |
| void |
| ftpc_close(void *conn) |
| { |
| struct ftp_connection *c; |
| |
| c = conn; |
| |
| if(c == NULL) { |
| return; |
| } |
| |
| c->type = TYPE_CLOSE; |
| } |
| /*---------------------------------------------------------------------------*/ |
| void |
| ftpc_cwd(void *conn, char *dirname) |
| { |
| struct ftp_connection *c; |
| |
| c = conn; |
| |
| if(c == NULL || |
| c->state != STATE_CONNECTED) { |
| return; |
| } |
| |
| strncpy(c->filename, dirname, sizeof(c->filename)); |
| c->state = STATE_SEND_CWD; |
| } |
| /*---------------------------------------------------------------------------*/ |
| void |
| ftpc_cdup(void *conn) |
| { |
| struct ftp_connection *c; |
| |
| c = conn; |
| |
| if(c == NULL || |
| c->state != STATE_CONNECTED) { |
| return; |
| } |
| |
| c->state = STATE_SEND_CDUP; |
| } |
| /*---------------------------------------------------------------------------*/ |