Add APPE command

This commit is contained in:
Michael Theall 2016-01-15 17:28:59 -06:00
parent 17dfdfcf01
commit 60bab0735e
2 changed files with 119 additions and 116 deletions

View File

@ -26,6 +26,7 @@ Create a **ftbrony** (double check that it is spelt **exactly** like this) direc
Supported Commands Supported Commands
------------------ ------------------
- APPE
- CDUP - CDUP
- CWD - CWD
- DELE - DELE
@ -57,7 +58,6 @@ Planned Commands
---------------- ----------------
- ALLO - ALLO
- APPE
- NLST - NLST
- REST - REST
- STOU - STOU

View File

@ -94,7 +94,6 @@ typedef enum
SESSION_RECV = BIT(3), /*!< data transfer in source mode */ SESSION_RECV = BIT(3), /*!< data transfer in source mode */
SESSION_SEND = BIT(4), /*!< data transfer in sink mode */ SESSION_SEND = BIT(4), /*!< data transfer in sink mode */
SESSION_RENAME = BIT(5), /*!< last command was RNFR and buffer contains path */ SESSION_RENAME = BIT(5), /*!< last command was RNFR and buffer contains path */
SESSION_RETRY = BIT(6),
} session_flags_t; } session_flags_t;
/*! ftp session */ /*! ftp session */
@ -288,7 +287,8 @@ ftp_set_socket_options(int fd)
* @param[in] connected whether this socket is connected * @param[in] connected whether this socket is connected
*/ */
static void static void
ftp_closesocket(int fd, int connected) ftp_closesocket(int fd,
bool connected)
{ {
int rc; int rc;
struct sockaddr_in addr; struct sockaddr_in addr;
@ -328,7 +328,7 @@ ftp_session_close_cmd(ftp_session_t *session)
{ {
/* close command socket */ /* close command socket */
if(session->cmd_fd >= 0) if(session->cmd_fd >= 0)
ftp_closesocket(session->cmd_fd, 1); ftp_closesocket(session->cmd_fd, true);
session->cmd_fd = -1; session->cmd_fd = -1;
} }
@ -346,7 +346,7 @@ ftp_session_close_pasv(ftp_session_t *session)
inet_ntoa(session->pasv_addr.sin_addr), inet_ntoa(session->pasv_addr.sin_addr),
ntohs(session->pasv_addr.sin_port)); ntohs(session->pasv_addr.sin_port));
ftp_closesocket(session->pasv_fd, 0); ftp_closesocket(session->pasv_fd, false);
} }
session->pasv_fd = -1; session->pasv_fd = -1;
@ -360,7 +360,7 @@ ftp_session_close_data(ftp_session_t *session)
{ {
/* close data connection */ /* close data connection */
if(session->data_fd >= 0) if(session->data_fd >= 0)
ftp_closesocket(session->data_fd, 1); ftp_closesocket(session->data_fd, true);
session->data_fd = -1; session->data_fd = -1;
/* clear send/recv flags */ /* clear send/recv flags */
@ -458,18 +458,24 @@ ftp_session_read_file(ftp_session_t *session)
/*! open file for writing for ftp session /*! open file for writing for ftp session
* *
* @param[in] session ftp session * @param[in] session ftp session
* @param[in] append whether to append
* *
* @returns -1 for error * @returns -1 for error
* *
* @note truncates file * @note truncates file
*/ */
static int static int
ftp_session_open_file_write(ftp_session_t *session) ftp_session_open_file_write(ftp_session_t *session,
bool append)
{ {
int rc; int rc;
const char *mode = "wb";
if(append)
mode = "ab";
/* open file in write and create mode with truncation */ /* open file in write and create mode with truncation */
session->fp = fopen(session->buffer, "wb"); session->fp = fopen(session->buffer, mode);
if(session->fp == NULL) if(session->fp == NULL)
{ {
console_print(RED "fopen '%s': %d %s\n" RESET, session->buffer, errno, strerror(errno)); console_print(RED "fopen '%s': %d %s\n" RESET, session->buffer, errno, strerror(errno));
@ -718,7 +724,7 @@ ftp_session_new(int listen_fd)
if(session == NULL) if(session == NULL)
{ {
console_print(RED "failed to allocate session\n" RESET); console_print(RED "failed to allocate session\n" RESET);
ftp_closesocket(new_fd, 1); ftp_closesocket(new_fd, true);
return; return;
} }
@ -801,7 +807,7 @@ ftp_session_accept(ftp_session_t *session)
rc = ftp_set_socket_nonblocking(new_fd); rc = ftp_set_socket_nonblocking(new_fd);
if(rc != 0) if(rc != 0)
{ {
ftp_closesocket(new_fd, 1); ftp_closesocket(new_fd, true);
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA); ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
ftp_send_response(session, 425, "Failed to establish connection\r\n"); ftp_send_response(session, 425, "Failed to establish connection\r\n");
return -1; return -1;
@ -849,7 +855,7 @@ ftp_session_connect(ftp_session_t *session)
rc = ftp_set_socket_options(session->data_fd); rc = ftp_set_socket_options(session->data_fd);
if(rc != 0) if(rc != 0)
{ {
ftp_closesocket(session->data_fd, 0); ftp_closesocket(session->data_fd, false);
session->data_fd = -1; session->data_fd = -1;
return -1; return -1;
} }
@ -860,7 +866,7 @@ ftp_session_connect(ftp_session_t *session)
if(rc != 0) if(rc != 0)
{ {
console_print(RED "connect: %d %s\n" RESET, errno, strerror(errno)); console_print(RED "connect: %d %s\n" RESET, errno, strerror(errno));
ftp_closesocket(session->data_fd, 0); ftp_closesocket(session->data_fd, false);
session->data_fd = -1; session->data_fd = -1;
return -1; return -1;
} }
@ -1212,7 +1218,7 @@ ftp_exit(void)
/* stop listening for new clients */ /* stop listening for new clients */
if(listenfd >= 0) if(listenfd >= 0)
ftp_closesocket(listenfd, 0); ftp_closesocket(listenfd, false);
#ifdef _3DS #ifdef _3DS
/* deinitialize SOC service */ /* deinitialize SOC service */
@ -1510,6 +1516,101 @@ store_transfer(ftp_session_t *session)
return 0; return 0;
} }
/*! ftp_xfer_file mode */
typedef enum
{
XFER_FILE_RETR, /*!< Retrieve a file */
XFER_FILE_STOR, /*!< Store a file */
XFER_FILE_APPE, /*!< Append a file */
} xfer_file_mode_t;
/*! Transfer a file
*
* @param[in] session ftp session
* @param[in] args ftp arguments
* @param[in] mode transfer mode
*
* @returns failure
*/
static int
ftp_xfer_file(ftp_session_t *session,
const char *args,
xfer_file_mode_t mode)
{
int rc;
if(build_path(session, args) != 0)
{
rc = errno;
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
return ftp_send_response(session, 553, "%s\r\n", strerror(rc));
}
if(mode == XFER_FILE_RETR)
rc = ftp_session_open_file_read(session);
else
rc = ftp_session_open_file_write(session, mode == XFER_FILE_APPE);
if(rc != 0)
{
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
return ftp_send_response(session, 450, "failed to open file\r\n");
}
if(session->flags & SESSION_PORT)
{
ftp_session_set_state(session, DATA_TRANSFER_STATE, CLOSE_PASV);
rc = ftp_session_connect(session);
if(rc != 0)
{
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
return ftp_send_response(session, 425, "can't open data connection\r\n");
}
session->flags &= ~(SESSION_RECV|SESSION_SEND);
if(mode == XFER_FILE_RETR)
{
session->flags |= SESSION_SEND;
session->transfer = retrieve_transfer;
}
else
{
session->flags |= SESSION_RECV;
session->transfer = store_transfer;
}
session->bufferpos = 0;
session->buffersize = 0;
return ftp_send_response(session, 150, "Ready\r\n");
}
else if(session->flags & SESSION_PASV)
{
session->flags &= ~(SESSION_RECV|SESSION_SEND);
if(mode == XFER_FILE_RETR)
{
session->flags |= SESSION_SEND;
session->transfer = retrieve_transfer;
}
else
{
session->flags |= SESSION_RECV;
session->transfer = store_transfer;
}
session->bufferpos = 0;
session->buffersize = 0;
ftp_session_set_state(session, DATA_CONNECT_STATE, CLOSE_DATA);
return 0;
}
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
return ftp_send_response(session, 503, "Bad sequence of commands\r\n");
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * *
* F T P C O M M A N D S * * F T P C O M M A N D S *
@ -1527,12 +1628,9 @@ FTP_DECLARE(ALLO)
FTP_DECLARE(APPE) FTP_DECLARE(APPE)
{ {
/* TODO */
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : ""); console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA); return ftp_xfer_file(session, args, XFER_FILE_APPE);
return ftp_send_response(session, 502, "unavailable\r\n");
} }
FTP_DECLARE(CDUP) FTP_DECLARE(CDUP)
@ -1936,57 +2034,9 @@ FTP_DECLARE(REST)
FTP_DECLARE(RETR) FTP_DECLARE(RETR)
{ {
int rc;
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : ""); console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
if(build_path(session, args) != 0) return ftp_xfer_file(session, args, XFER_FILE_RETR);
{
rc = errno;
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
return ftp_send_response(session, 553, "%s\r\n", strerror(rc));
}
if(ftp_session_open_file_read(session) != 0)
{
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
return ftp_send_response(session, 450, "failed to open file\r\n");
}
if(session->flags & SESSION_PORT)
{
ftp_session_set_state(session, DATA_TRANSFER_STATE, CLOSE_PASV);
rc = ftp_session_connect(session);
if(rc != 0)
{
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
return ftp_send_response(session, 425, "can't open data connection\r\n");
}
session->flags &= ~(SESSION_RECV|SESSION_SEND);
session->flags |= SESSION_SEND;
session->transfer = retrieve_transfer;
session->bufferpos = 0;
session->buffersize = 0;
return ftp_send_response(session, 150, "Ready\r\n");
}
else if(session->flags & SESSION_PASV)
{
session->flags &= ~(SESSION_RECV|SESSION_SEND);
session->flags |= SESSION_SEND;
session->transfer = retrieve_transfer;
session->bufferpos = 0;
session->buffersize = 0;
ftp_session_set_state(session, DATA_CONNECT_STATE, CLOSE_DATA);
return 0;
}
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
return ftp_send_response(session, 503, "Bad sequence of commands\r\n");
} }
FTP_DECLARE(RMD) FTP_DECLARE(RMD)
@ -2063,59 +2113,12 @@ FTP_DECLARE(RNTO)
FTP_DECLARE(STOR) FTP_DECLARE(STOR)
{ {
int rc;
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : ""); console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
if(build_path(session, args) != 0) return ftp_xfer_file(session, args, XFER_FILE_STOR);
{
rc = errno;
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
return ftp_send_response(session, 553, "%s\r\n", strerror(rc));
}
if(ftp_session_open_file_write(session) != 0)
{
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
return ftp_send_response(session, 450, "failed to open file\r\n");
}
if(session->flags & SESSION_PORT)
{
ftp_session_set_state(session, DATA_TRANSFER_STATE, CLOSE_PASV);
rc = ftp_session_connect(session);
if(rc != 0)
{
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
return ftp_send_response(session, 425, "can't open data connection\r\n");
}
session->flags &= ~(SESSION_RECV|SESSION_SEND);
session->flags |= SESSION_RECV;
session->transfer = store_transfer;
session->bufferpos = 0;
session->buffersize = 0;
return ftp_send_response(session, 150, "Ready\r\n");
}
else if(session->flags & SESSION_PASV)
{
session->flags &= ~(SESSION_RECV|SESSION_SEND);
session->flags |= SESSION_RECV;
session->transfer = store_transfer;
session->bufferpos = 0;
session->buffersize = 0;
ftp_session_set_state(session, DATA_CONNECT_STATE, CLOSE_DATA);
return 0;
}
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
return ftp_send_response(session, 503, "Bad sequence of commands\r\n");
} }
FTP_DECLARE(STOU) FTP_DECLARE(STOU)
{ {
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : ""); console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");