contiki/apps/ftp/ftp.c

591 lines
18 KiB
C

/*
* 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: ftp.c,v 1.6 2008/02/08 22:50:23 oliverschmidt Exp $
*/
/* Note to self: It would be nice to have a "View" option in the download dialog. */
#include <string.h>
#include <stddef.h>
#include "ftpc.h"
#include "contiki.h"
#include "ctk/ctk.h"
#include "cfs/cfs.h"
#include "net/resolv.h"
#define MAX_USERNAMELEN 16
#define MAX_PASSWORDLEN 16
#define MAX_HOSTNAMELEN 32
#define MAX_FILENAMELEN 16
#define FILES_WIDTH 17
#define FILES_HEIGHT 18
PROCESS(ftp_process, "FTP client");
AUTOSTART_PROCESSES(&ftp_process);
static void *connection;
/* --- The main window --- */
static struct ctk_window window;
static struct ctk_label localtextlabel =
{CTK_LABEL(1, 0, FILES_WIDTH, 1, "Local files")};
static struct ctk_label remotetextlabel =
{CTK_LABEL(1 + FILES_WIDTH + 1, 0,
FILES_WIDTH, 1, "Remote files")};
static char leftptr[FILES_HEIGHT];
static struct ctk_label leftptrlabel =
{CTK_LABEL(0, 1, 1, FILES_HEIGHT, leftptr)};
static char midptr[FILES_HEIGHT];
static struct ctk_label midptrlabel =
{CTK_LABEL(1 + FILES_WIDTH, 1, 1, FILES_HEIGHT, midptr)};
static char rightptr[FILES_HEIGHT];
static struct ctk_label rightptrlabel =
{CTK_LABEL(1 + FILES_WIDTH + 1 + FILES_WIDTH, 1,
1, FILES_HEIGHT, rightptr)};
static char localfiles[FILES_WIDTH * FILES_HEIGHT];
static struct ctk_label localfileslabel =
{CTK_LABEL(1, 1,
FILES_WIDTH, FILES_HEIGHT, localfiles)};
static char remotefiles[FILES_WIDTH * FILES_HEIGHT];
static struct ctk_label remotefileslabel =
{CTK_LABEL(FILES_WIDTH + 1 + 1, 1,
FILES_WIDTH, FILES_HEIGHT, remotefiles)};
static struct ctk_button reloadbutton =
{CTK_BUTTON(0, 1 + FILES_HEIGHT, 6, "Reload")};
static struct ctk_button connectionbutton =
{CTK_BUTTON(8, 1 + FILES_HEIGHT, 13, "Connection...")};
static struct ctk_button quitbutton =
{CTK_BUTTON(1 + FILES_WIDTH + 1 + FILES_WIDTH - 5,
1 + FILES_HEIGHT, 4, "Quit")};
static char statustext[3 + FILES_WIDTH * 2 + 1];
static struct ctk_label statuslabel =
{CTK_LABEL(0, FILES_HEIGHT + 2, 3 + FILES_WIDTH * 2, 1, statustext)};
/* --- The download/upload dialogs --- */
static char remotefilename[MAX_FILENAMELEN + 1];
static char localfilename[MAX_FILENAMELEN + 1];
static struct ctk_window dialog;
static struct ctk_label downloadlabel =
{CTK_LABEL(6, 1, 13, 1, "Download file")};
static struct ctk_label uploadlabel =
{CTK_LABEL(7, 1, 11, 1, "Upload file")};
static struct ctk_label localfilenametextlabel =
{CTK_LABEL(2, 3, 15, 1, "Local filename")};
static struct ctk_label localfilenamelabel =
{CTK_LABEL(3, 5, 16, 1, localfilename)};
static struct ctk_textentry localfilenameentry =
{CTK_TEXTENTRY(2, 5, 16, 1, localfilename, sizeof(localfilename) - 1)};
static struct ctk_label remotefilenametextlabel =
{CTK_LABEL(2, 7, 15, 1, "Remote filename")};
static struct ctk_label remotefilenamelabel =
{CTK_LABEL(3, 9, 16, 1, remotefilename)};
static struct ctk_textentry remotefilenameentry =
{CTK_TEXTENTRY(2, 9, 16, 1, remotefilename, sizeof(remotefilename) - 1)};
static struct ctk_button downloadbutton =
{CTK_BUTTON(0, 11, 13, "Download file")};
static struct ctk_button uploadbutton =
{CTK_BUTTON(0, 11, 11, "Upload file")};
static struct ctk_button cancelbutton =
{CTK_BUTTON(16, 11, 6, "Cancel")};
/* --- The connection window --- */
static char hostname[MAX_HOSTNAMELEN + 1];
static char username[MAX_USERNAMELEN + 1];
static char password[MAX_PASSWORDLEN + 1];
static struct ctk_window connectionwindow;
static struct ctk_label serverlabel =
{CTK_LABEL(0, 1, 10, 1, "FTP server")};
static struct ctk_textentry serverentry =
{CTK_TEXTENTRY(0, 2, 16, 1, hostname, MAX_HOSTNAMELEN)};
static struct ctk_button anonymousbutton =
{CTK_BUTTON(10, 4, 9, "Anonymous")};
static struct ctk_label userlabel =
{CTK_LABEL(0, 4, 8, 1, "Username")};
static struct ctk_textentry userentry =
{CTK_TEXTENTRY(0, 5, 16, 1, username, sizeof(username) - 1)};
static struct ctk_label passwordlabel =
{CTK_LABEL(0, 7, 8, 1, "Password")};
static struct ctk_textentry passwordentry =
{CTK_TEXTENTRY(0, 8, 16, 1, password, sizeof(password) - 1)};
static struct ctk_button connectbutton =
{CTK_BUTTON(0, 10, 7, "Connect")};
static struct ctk_button closeconnectionbutton =
{CTK_BUTTON(0, 10, 16, "Close connection")};
static struct ctk_button closebutton =
{CTK_BUTTON(18, 10, 5, "Close")};
static struct cfs_dir dir;
static struct cfs_dirent dirent;
static unsigned char localfileptr = 0;
static unsigned char remotefileptr = 0;
static unsigned char ptrstate;
#define PTRSTATE_LOCALFILES 0
#define PTRSTATE_REMOTEFILES 1
static unsigned char localptr, remoteptr;
static int fd = -1;
/*---------------------------------------------------------------------------*/
static void
make_uploaddialog(void)
{
ctk_dialog_new(&dialog, 24, 13);
CTK_WIDGET_ADD(&dialog, &uploadlabel);
CTK_WIDGET_ADD(&dialog, &localfilenametextlabel);
CTK_WIDGET_ADD(&dialog, &localfilenamelabel);
CTK_WIDGET_ADD(&dialog, &remotefilenametextlabel);
CTK_WIDGET_ADD(&dialog, &remotefilenameentry);
CTK_WIDGET_ADD(&dialog, &uploadbutton);
CTK_WIDGET_ADD(&dialog, &cancelbutton);
CTK_WIDGET_FOCUS(&dialog, &uploadbutton);
}
/*---------------------------------------------------------------------------*/
static void
make_downloaddialog(void)
{
ctk_dialog_new(&dialog, 24, 13);
CTK_WIDGET_ADD(&dialog, &downloadlabel);
CTK_WIDGET_ADD(&dialog, &localfilenametextlabel);
CTK_WIDGET_ADD(&dialog, &localfilenameentry);
CTK_WIDGET_ADD(&dialog, &remotefilenametextlabel);
CTK_WIDGET_ADD(&dialog, &remotefilenamelabel);
CTK_WIDGET_ADD(&dialog, &downloadbutton);
CTK_WIDGET_ADD(&dialog, &cancelbutton);
CTK_WIDGET_FOCUS(&dialog, &downloadbutton);
}
/*---------------------------------------------------------------------------*/
static void
show_statustext(char *text1, char *text2)
{
int len;
len = (int)strlen(text1);
if(len < sizeof(statustext)) {
strncpy(statustext, text1, sizeof(statustext));
strncpy(statustext + len, text2, sizeof(statustext) - len);
CTK_WIDGET_REDRAW(&statuslabel);
}
}
/*---------------------------------------------------------------------------*/
static void
close_file(void)
{
if(fd != -1) {
cfs_close(fd);
fd = -1;
}
}
/*---------------------------------------------------------------------------*/
static void
quit(void)
{
close_file();
ctk_window_close(&window);
process_exit(&ftp_process);
LOADER_UNLOAD();
}
/*---------------------------------------------------------------------------*/
static void
clearptr(void)
{
rightptr[remoteptr] = ' ';
midptr[remoteptr] = ' ';
leftptr[localptr] = ' ';
midptr[localptr] = ' ';
}
/*---------------------------------------------------------------------------*/
static void
showptr(void)
{
if(ptrstate == PTRSTATE_LOCALFILES) {
rightptr[remoteptr] = ' ';
midptr[remoteptr] = ' ';
leftptr[localptr] = '>';
midptr[localptr] = '<';
} else {
leftptr[localptr] = ' ';
midptr[localptr] = ' ';
rightptr[remoteptr] = '<';
midptr[remoteptr] = '>';
}
CTK_WIDGET_REDRAW(&leftptrlabel);
CTK_WIDGET_REDRAW(&midptrlabel);
CTK_WIDGET_REDRAW(&rightptrlabel);
}
/*---------------------------------------------------------------------------*/
static void
start_loaddir(void)
{
memset(localfiles, 0, sizeof(localfiles));
localfileptr = 0;
cfs_opendir(&dir, ".");
process_post(&ftp_process, PROCESS_EVENT_CONTINUE, NULL);
}
/*---------------------------------------------------------------------------*/
static void
start_loadremote(void)
{
memset(remotefiles, 0, sizeof(remotefiles));
remotefileptr = 0;
clearptr();
remoteptr = 0;
showptr();
ftpc_list(connection);
}
/*---------------------------------------------------------------------------*/
static void
make_connectionwindow(void)
{
ctk_dialog_new(&connectionwindow, 25, 11);
CTK_WIDGET_ADD(&connectionwindow, &serverlabel);
CTK_WIDGET_ADD(&connectionwindow, &serverentry);
CTK_WIDGET_ADD(&connectionwindow, &userlabel);
CTK_WIDGET_ADD(&connectionwindow, &anonymousbutton);
CTK_WIDGET_ADD(&connectionwindow, &userentry);
CTK_WIDGET_ADD(&connectionwindow, &passwordlabel);
CTK_WIDGET_ADD(&connectionwindow, &passwordentry);
if(connection == NULL) {
CTK_WIDGET_ADD(&connectionwindow, &connectbutton);
} else {
CTK_WIDGET_ADD(&connectionwindow, &closeconnectionbutton);
}
CTK_WIDGET_ADD(&connectionwindow, &closebutton);
CTK_WIDGET_FOCUS(&connectionwindow, &serverentry);
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(ftp_process, ev, data)
{
u16_t ipaddr[2], *ipaddrptr;
PROCESS_BEGIN();
ftpc_init();
memset(statustext, 0, sizeof(statustext));
memset(remotefiles, 0, sizeof(remotefiles));
memset(localfiles, 0, sizeof(localfiles));
memset(leftptr, 0, sizeof(leftptr));
memset(midptr, 0, sizeof(midptr));
memset(rightptr, 0, sizeof(rightptr));
ptrstate = PTRSTATE_REMOTEFILES;
localptr = remoteptr = 0;
connection = NULL;
ctk_window_new(&window,
3 + FILES_WIDTH * 2, 3 + FILES_HEIGHT,
"FTP Client");
CTK_WIDGET_ADD(&window, &localtextlabel);
CTK_WIDGET_ADD(&window, &remotetextlabel);
CTK_WIDGET_ADD(&window, &leftptrlabel);
CTK_WIDGET_ADD(&window, &localfileslabel);
CTK_WIDGET_ADD(&window, &midptrlabel);
CTK_WIDGET_ADD(&window, &remotefileslabel);
CTK_WIDGET_ADD(&window, &rightptrlabel);
CTK_WIDGET_ADD(&window, &reloadbutton);
CTK_WIDGET_ADD(&window, &connectionbutton);
CTK_WIDGET_ADD(&window, &quitbutton);
CTK_WIDGET_ADD(&window, &statuslabel);
CTK_WIDGET_FOCUS(&window, &connectionbutton);
ctk_window_open(&window);
showptr();
start_loaddir();
while(1) {
PROCESS_WAIT_EVENT();
if(ev == PROCESS_EVENT_CONTINUE) {
if(cfs_readdir(&dir, &dirent) == 0 &&
localfileptr < FILES_HEIGHT) {
strncpy(&localfiles[localfileptr * FILES_WIDTH],
dirent.name, FILES_WIDTH);
CTK_WIDGET_REDRAW(&localfileslabel);
++localfileptr;
process_post(&ftp_process, PROCESS_EVENT_CONTINUE, NULL);
} else{
cfs_closedir(&dir);
}
} else if(ev == PROCESS_EVENT_EXIT) {
quit();
} else if(ev == tcpip_event) {
ftpc_appcall(data);
#if UIP_UDP
} else if(ev == resolv_event_found) {
/* Either found a hostname, or not. */
if((char *)data != NULL &&
(ipaddrptr = resolv_lookup((char *)data)) != NULL) {
connection = ftpc_connect(ipaddrptr, HTONS(21));
show_statustext("Connecting to ", hostname);
} else {
show_statustext("Host not found: ", hostname);
}
#endif /* UIP_UDP */
} else if(
#if CTK_CONF_WINDOWCLOSE
ev == ctk_signal_window_close &&
#endif /* CTK_CONF_WINDOWCLOSE */
data == (process_data_t)&window) {
quit();
} else if(ev == ctk_signal_widget_activate) {
if((struct ctk_button *)data == &quitbutton) {
quit();
} else if((struct ctk_button *)data == &cancelbutton) {
ctk_dialog_close();
} else if((struct ctk_button *)data == &downloadbutton) {
ctk_dialog_close();
close_file();
fd = cfs_open(localfilename, CFS_WRITE);
if(fd != -1) {
show_statustext("Downloading ", remotefilename);
ftpc_get(connection, remotefilename);
} else {
show_statustext("Could not create ", localfilename);
}
} else if((struct ctk_button *)data == &reloadbutton) {
start_loaddir();
} else if((struct ctk_button *)data == &connectionbutton) {
make_connectionwindow();
ctk_dialog_open(&connectionwindow);
} else if((struct ctk_button *)data == &closebutton) {
ctk_dialog_close();
} else if((struct ctk_button *)data == &anonymousbutton) {
strcpy(username, "ftp");
strcpy(password, "contiki@ftp");
CTK_WIDGET_REDRAW(&userentry);
CTK_WIDGET_REDRAW(&passwordentry);
} else if((struct ctk_button *)data == &closeconnectionbutton) {
ctk_dialog_close();
ftpc_close(connection);
} else if((struct ctk_button *)data == &connectbutton) {
ctk_dialog_close();
#if UIP_UDP
if(uiplib_ipaddrconv(hostname, (unsigned char *)ipaddr) == 0) {
ipaddrptr = resolv_lookup(hostname);
if(ipaddrptr == NULL) {
resolv_query(hostname);
show_statustext("Resolving host ", hostname);
break;
}
connection = ftpc_connect(ipaddrptr, HTONS(21));
show_statustext("Connecting to ", hostname);
} else {
connection = ftpc_connect(ipaddr, HTONS(21));
show_statustext("Connecting to ", hostname);
}
#else /* UIP_UDP */
uiplib_ipaddrconv(hostname, (unsigned char *)ipaddr);
connection = ftpc_connect(ipaddr, HTONS(21));
show_statustext("Connecting to ", hostname);
#endif /* UIP_UDP */
}
/* if((struct ctk_button *)data == &closebutton) {
ftpc_close(connection);
}*/
} else if(ev == ctk_signal_keypress) {
/* if((ctk_arch_key_t)data == ' ') {
if(ptrstate == PTRSTATE_LOCALFILES) {
ptrstate = PTRSTATE_REMOTEFILES;
} else {
ptrstate = PTRSTATE_LOCALFILES;
}
} else */ if((ctk_arch_key_t)(size_t)data == CH_CURS_UP) {
clearptr();
if(ptrstate == PTRSTATE_LOCALFILES) {
if(localptr > 0) {
--localptr;
}
} else {
if(remoteptr > 0) {
--remoteptr;
}
}
} else if((ctk_arch_key_t)(size_t)data == CH_CURS_DOWN) {
clearptr();
if(ptrstate == PTRSTATE_LOCALFILES) {
if(localptr < FILES_HEIGHT - 1) {
++localptr;
}
} else {
if(remoteptr < FILES_HEIGHT - 1) {
++remoteptr;
}
}
} else if((ctk_arch_key_t)(size_t)data == CH_ENTER) {
if(ptrstate == PTRSTATE_LOCALFILES) {
strncpy(localfilename,
&localfiles[localptr * FILES_WIDTH], FILES_WIDTH);
strncpy(remotefilename,
&localfiles[localptr * FILES_WIDTH], FILES_WIDTH);
make_uploaddialog();
ctk_dialog_open(&dialog);
} else {
strncpy(localfilename,
&remotefiles[remoteptr * FILES_WIDTH], FILES_WIDTH);
strncpy(remotefilename,
&remotefiles[remoteptr * FILES_WIDTH], FILES_WIDTH);
ftpc_cwd(connection, remotefilename);
/* make_downloaddialog();
ctk_dialog_open(&dialog);*/
}
} else if((ctk_arch_key_t)(size_t)data == 'u') {
ftpc_cdup(connection);
}
showptr();
}
}
PROCESS_END();
}
/*---------------------------------------------------------------------------*/
void
ftpc_closed(void)
{
strcpy(statustext, "Connection closed");
CTK_WIDGET_REDRAW(&statuslabel);
connection = NULL;
}
/*---------------------------------------------------------------------------*/
void
ftpc_aborted(void)
{
strcpy(statustext, "Connection reset");
CTK_WIDGET_REDRAW(&statuslabel);
connection = NULL;
}
/*---------------------------------------------------------------------------*/
void
ftpc_timedout(void)
{
strcpy(statustext, "Connection timed out");
CTK_WIDGET_REDRAW(&statuslabel);
connection = NULL;
}
/*---------------------------------------------------------------------------*/
char *
ftpc_username(void)
{
return username;
}
/*---------------------------------------------------------------------------*/
char *
ftpc_password(void)
{
return password;
}
/*---------------------------------------------------------------------------*/
void
ftpc_list_file(char *filename)
{
if(remotefileptr < FILES_HEIGHT && filename != NULL) {
strncpy(&remotefiles[remotefileptr * FILES_WIDTH], filename, FILES_WIDTH);
CTK_WIDGET_REDRAW(&remotefileslabel);
++remotefileptr;
}
if(filename == NULL) {
strcpy(statustext, "Connected");
CTK_WIDGET_REDRAW(&statuslabel);
}
}
/*---------------------------------------------------------------------------*/
void
ftpc_cwd_done(unsigned short status)
{
if(status == FTPC_COMPLETED ||
status == FTPC_OK) {
start_loadremote();
} else {
make_downloaddialog();
ctk_dialog_open(&dialog);
}
}
/*---------------------------------------------------------------------------*/
void
ftpc_connected(void *connection)
{
strcpy(statustext, "Loading remote directory");
CTK_WIDGET_REDRAW(&statuslabel);
start_loadremote();
}
/*---------------------------------------------------------------------------*/
void
ftpc_data(u8_t *data, u16_t len)
{
if(data == NULL) {
show_statustext("Download complete", "");
close_file();
start_loaddir();
} else {
cfs_write(fd, data, len);
}
}
/*---------------------------------------------------------------------------*/