blob: 92e50fb56fb437aaf4c4d9c75076354dc20a4c6e [file] [log] [blame]
/*
* 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;
}
/*---------------------------------------------------------------------------*/