ftp_server_events.c
Go to the documentation of this file.
1 /**
2  * @file ftp_server_events.c
3  * @brief FTP server (event handlers)
4  *
5  * @section License
6  *
7  * Copyright (C) 2010-2018 Oryx Embedded SARL. All rights reserved.
8  *
9  * This file is part of CycloneTCP Open.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24  *
25  * @author Oryx Embedded SARL (www.oryx-embedded.com)
26  * @version 1.9.0
27  **/
28 
29 //Switch to the appropriate trace level
30 #define TRACE_LEVEL FTP_TRACE_LEVEL
31 
32 //Dependencies
33 #include "ftp/ftp_server.h"
34 #include "ftp/ftp_server_events.h"
36 #include "ftp/ftp_server_misc.h"
37 #include "str.h"
38 #include "path.h"
39 #include "error.h"
40 #include "debug.h"
41 
42 //Check TCP/IP stack configuration
43 #if (FTP_SERVER_SUPPORT == ENABLED)
44 
45 //Abbreviated months
46 static const char months[13][4] =
47 {
48  " ",
49  "Jan",
50  "Feb",
51  "Mar",
52  "Apr",
53  "May",
54  "Jun",
55  "Jul",
56  "Aug",
57  "Sep",
58  "Oct",
59  "Nov",
60  "Dec"
61 };
62 
63 
64 /**
65  * @brief Control connection event handler
66  * @param[in] context Pointer to the FTP server context
67  * @param[in] connection Pointer to the client connection
68  * @param[in] eventFlags Event to be processed
69  **/
70 
72  FtpClientConnection *connection, uint_t eventFlags)
73 {
74  error_t error;
75  size_t n;
76 
77  //Send buffer is available for writing?
78  if(eventFlags == SOCKET_EVENT_TX_READY)
79  {
80  //Send data back to the client
81  error = socketSend(connection->controlSocket, connection->response +
82  connection->responsePos, connection->responseLength, &n, 0);
83 
84  //Failed to send data?
85  if(error != NO_ERROR && error != ERROR_TIMEOUT)
86  {
87  //Close connection with the client
88  ftpServerCloseConnection(context, connection);
89  //Exit immediately
90  return;
91  }
92 
93  //Advance data pointer
94  connection->responsePos += n;
95  //Number of bytes still available in the response buffer
96  connection->responseLength -= n;
97  }
98  //Data is pending in the receive buffer?
99  else if(eventFlags == SOCKET_EVENT_RX_READY)
100  {
101  //Read data from the client
102  error = socketReceive(connection->controlSocket,
103  connection->command + connection->commandLength,
104  FTP_SERVER_MAX_LINE_LEN - connection->commandLength, &n, 0);
105 
106  //Failed to receive data?
107  if(error == ERROR_END_OF_STREAM)
108  {
109  //Gracefully disconnect from the remote host
111  //Exit immediately
112  return;
113  }
114  else if(error)
115  {
116  //Close connection with the client
117  ftpServerCloseConnection(context, connection);
118  //Exit immediately
119  return;
120  }
121 
122  //Number of bytes available in the command buffer
123  connection->commandLength += n;
124  //Process incoming command
125  ftpServerProcessCmd(context, connection);
126  }
127  //Data are transmitted and acknowledged?
128  else if(eventFlags == SOCKET_EVENT_TX_ACKED)
129  {
130  //Disable transmission
132  //Next state
134  }
135  //Transmission is shut down?
136  else if(eventFlags == SOCKET_EVENT_TX_SHUTDOWN)
137  {
138  //Disable reception
140  //Next state
142  }
143  //Reception is shut down?
144  else if(eventFlags == SOCKET_EVENT_RX_SHUTDOWN)
145  {
146  //Properly close connection
147  ftpServerCloseConnection(context, connection);
148  }
149 }
150 
151 
152 /**
153  * @brief Data connection event handler
154  * @param[in] context Pointer to the FTP server context
155  * @param[in] connection Pointer to the client connection
156  * @param[in] eventFlags Event to be processed
157  **/
158 
160  FtpClientConnection *connection, uint_t eventFlags)
161 {
162  //Any connection attempt?
163  if(connection->dataState == FTP_DATA_STATE_LISTEN)
164  {
165  //Accept data connection
166  ftpServerAcceptDataConnection(connection);
167  }
168  //Ready to send data?
169  else if(connection->dataState == FTP_DATA_STATE_SEND)
170  {
171  //Send more data to the remote host
172  ftpServerSendData(context, connection);
173  }
174  //Any data pending in the receive buffer?
175  else if(connection->dataState == FTP_DATA_STATE_RECEIVE)
176  {
177  //Process incoming data
178  ftpServerReceiveData(context, connection);
179  }
180  //Data are transmitted and acknowledged?
181  else if(connection->dataState == FTP_DATA_STATE_WAIT_ACK)
182  {
183  //Disable transmission
185  //Next state
187  }
188  //Transmission is shut down?
189  else if(connection->dataState == FTP_DATA_STATE_SHUTDOWN_TX)
190  {
191  //Disable reception
193  //Next state
195  }
196  //Reception is shut down?
197  else if(connection->dataState == FTP_DATA_STATE_SHUTDOWN_RX)
198  {
199  //Close the data connection
200  ftpServerCloseDataConnection(connection);
201 
202  //Back to idle state
203  connection->controlState = FTP_CONTROL_STATE_IDLE;
204 
205  //Transfer status
206  strcpy(connection->response, "226 Transfer complete\r\n");
207  //Debug message
208  TRACE_DEBUG("FTP server: %s", connection->response);
209 
210  //Number of bytes in the response buffer
211  connection->responseLength = strlen(connection->response);
212  connection->responsePos = 0;
213  }
214 }
215 
216 
217 /**
218  * @brief Send data on the data connection
219  * @param[in] context Pointer to the FTP server context
220  * @param[in] connection Pointer to the client connection
221  **/
222 
224 {
225  error_t error;
226  size_t n;
227 
228  //Any data waiting for transmission?
229  if(connection->bufferLength > 0)
230  {
231  //Send more data
232  error = socketSend(connection->dataSocket, connection->buffer +
233  connection->bufferPos, connection->bufferLength, &n, 0);
234 
235  //Failed to send data?
236  if(error != NO_ERROR && error != ERROR_TIMEOUT)
237  {
238  //Close the data connection
239  ftpServerCloseDataConnection(connection);
240 
241  //Release previously allocated resources
242  if(connection->file != NULL)
243  {
244  fsCloseFile(connection->file);
245  connection->file = NULL;
246  }
247 
248  if(connection->dir != NULL)
249  {
250  fsCloseDir(connection->dir);
251  connection->dir = NULL;
252  }
253 
254  //Back to idle state
255  connection->controlState = FTP_CONTROL_STATE_IDLE;
256 
257  //Transfer status
258  strcpy(connection->response, "451 Transfer aborted\r\n");
259  //Debug message
260  TRACE_DEBUG("FTP server: %s", connection->response);
261 
262  //Number of bytes in the response buffer
263  connection->responseLength = strlen(connection->response);
264  connection->responsePos = 0;
265 
266  //Exit immediately
267  return;
268  }
269 
270  //Advance data pointer
271  connection->bufferPos += n;
272  //Number of bytes still available in the buffer
273  connection->bufferLength -= n;
274  }
275 
276  //Empty transmission buffer?
277  if(connection->bufferLength == 0)
278  {
279  //File transfer in progress?
280  if(connection->controlState == FTP_CONTROL_STATE_RETR)
281  {
282  //Read more data
283  error = fsReadFile(connection->file,
284  connection->buffer, FTP_SERVER_BUFFER_SIZE, &n);
285 
286  //End of stream?
287  if(error)
288  {
289  //Close file
290  fsCloseFile(connection->file);
291  connection->file = NULL;
292 
293  //Wait for all the data to be transmitted and acknowledged
294  connection->dataState = FTP_DATA_STATE_WAIT_ACK;
295 
296  //Exit immediately
297  return;
298  }
299  }
300  //Directory listing in progress?
301  else if(connection->controlState == FTP_CONTROL_STATE_LIST)
302  {
303  uint_t perm;
304  time_t currentTime;
305  time_t modified;
306  char_t *path;
307  FsDirEntry dirEntry;
308 
309  //Read a new entry in the directory
310  error = fsReadDir(connection->dir, &dirEntry);
311 
312  //End of stream?
313  if(error)
314  {
315  //Close directory
316  fsCloseDir(connection->dir);
317  connection->dir = NULL;
318 
319  //Wait for all the data to be transmitted and acknowledged
320  connection->dataState = FTP_DATA_STATE_WAIT_ACK;
321 
322  //Exit immediately
323  return;
324  }
325 
326  //Point to the scratch buffer
327  path = connection->buffer;
328 
329  //Get the pathname of the directory being listed
330  strcpy(path, connection->path);
331  //Retrieve the full pathname
332  pathCombine(path, dirEntry.name, FTP_SERVER_MAX_PATH_LEN);
333  pathCanonicalize(path);
334 
335  //Get permissions for the specified file
336  perm = ftpServerGetFilePermissions(context, connection, path);
337 
338  //Enforce access rights
339  if(perm & FTP_FILE_PERM_LIST)
340  {
341  //Format links, owner, group and size fields
342  n = sprintf(connection->buffer, "---------- 1 owner group %10" PRIu32,
343  dirEntry.size);
344 
345  //Check whether the current entry is a directory
346  if(dirEntry.attributes & FS_FILE_ATTR_DIRECTORY)
347  connection->buffer[0] = 'd';
348 
349  //Read access permitted?
350  if(perm & FTP_FILE_PERM_READ)
351  {
352  connection->buffer[1] = 'r';
353  connection->buffer[4] = 'r';
354  connection->buffer[7] = 'r';
355  }
356 
357  //Write access permitted?
358  if(perm & FTP_FILE_PERM_WRITE)
359  {
360  //Make sure the file is not marked as read-only
361  if(!(dirEntry.attributes & FS_FILE_ATTR_READ_ONLY))
362  {
363  connection->buffer[2] = 'w';
364  connection->buffer[5] = 'w';
365  connection->buffer[8] = 'w';
366  }
367  }
368 
369  //Get current time
370  currentTime = getCurrentUnixTime();
371  //Get modification time
372  modified = convertDateToUnixTime(&dirEntry.modified);
373 
374  //Check whether the modification time is within the previous 180 days
375  if(currentTime > modified && currentTime < (modified + FTP_SERVER_180_DAYS))
376  {
377  //The format of the date/time field is Mmm dd hh:mm
378  n += sprintf(connection->buffer + n, " %s %02" PRIu8 " %02" PRIu8 ":%02" PRIu8,
379  months[MIN(dirEntry.modified.month, 12)], dirEntry.modified.day,
380  dirEntry.modified.hours, dirEntry.modified.minutes);
381  }
382  else
383  {
384  //The format of the date/time field is Mmm dd yyyy
385  n += sprintf(connection->buffer + n, " %s %02" PRIu8 " %04" PRIu16,
386  months[MIN(dirEntry.modified.month, 12)], dirEntry.modified.day,
387  dirEntry.modified.year);
388  }
389 
390  //Append filename
391  n += sprintf(connection->buffer + n, " %s\r\n", dirEntry.name);
392  //Debug message
393  TRACE_DEBUG("FTP server: %s", connection->buffer);
394  }
395  else
396  {
397  //Insufficient access rights
398  n = 0;
399  }
400  }
401  //Invalid state?
402  else
403  {
404  //The FTP server has encountered a critical error
405  ftpServerCloseConnection(context, connection);
406  //Exit immediately
407  return;
408  }
409 
410  //Number of bytes in the buffer
411  connection->bufferPos = 0;
412  connection->bufferLength = n;
413  }
414 }
415 
416 
417 /**
418  * @brief Receive data on the data connection
419  * @param[in] context Pointer to the FTP server context
420  * @param[in] connection Pointer to the client connection
421  **/
422 
424 {
425  error_t error;
426  bool_t eof;
427  size_t n;
428 
429  //File transfer in progress?
430  if(connection->controlState == FTP_CONTROL_STATE_STOR ||
431  connection->controlState == FTP_CONTROL_STATE_APPE)
432  {
433  //Read incoming data
434  error = socketReceive(connection->dataSocket,
435  connection->buffer + connection->bufferPos,
436  FTP_SERVER_BUFFER_SIZE - connection->bufferLength, &n, 0);
437 
438  //Any error to report?
439  if(error)
440  {
441  //Cannot read more data
442  eof = TRUE;
443  }
444  else
445  {
446  //Successful read operation
447  eof = FALSE;
448 
449  //Advance data pointer
450  connection->bufferPos += n;
451  connection->bufferLength += n;
452  }
453 
454  //Read data until the buffer is full or the end of the file is reached
455  if(eof || connection->bufferLength >= FTP_SERVER_BUFFER_SIZE)
456  {
457  //Any data to be written?
458  if(connection->bufferLength > 0)
459  {
460  //Write data to the specified file
461  error = fsWriteFile(connection->file,
462  connection->buffer, connection->bufferLength);
463 
464  //Any error to report?
465  if(error)
466  {
467  //Close the data connection
468  ftpServerCloseDataConnection(connection);
469 
470  //Release previously allocated resources
471  fsCloseFile(connection->file);
472  connection->file = NULL;
473 
474  //Back to idle state
475  connection->controlState = FTP_CONTROL_STATE_IDLE;
476 
477  //Transfer status
478  strcpy(connection->response, "451 Transfer aborted\r\n");
479  //Debug message
480  TRACE_DEBUG("FTP server: %s", connection->response);
481 
482  //Number of bytes in the response buffer
483  connection->responseLength = strlen(connection->response);
484  connection->responsePos = 0;
485 
486  //Exit immediately
487  return;
488  }
489  }
490 
491  //Flush reception buffer
492  connection->bufferLength = 0;
493  connection->bufferPos = 0;
494  }
495 
496  //End of stream?
497  if(eof)
498  {
499  //Close file
500  fsCloseFile(connection->file);
501  connection->file = NULL;
502 
503  //Graceful shutdown sequence
504  connection->dataState = FTP_DATA_STATE_WAIT_ACK;
505  }
506  }
507  //Invalid state?
508  else
509  {
510  //The FTP server has encountered a critical error
511  ftpServerCloseConnection(context, connection);
512  }
513 }
514 
515 #endif
uint32_t size
Definition: fs_port.h:110
size_t responsePos
Current position in the response buffer.
Definition: ftp_server.h:258
void ftpServerCloseDataConnection(FtpClientConnection *connection)
Close data connection.
size_t bufferLength
Number of bytes available in the I/O buffer.
Definition: ftp_server.h:260
Helper functions for FTP server.
error_t socketReceive(Socket *socket, void *data, size_t size, size_t *received, uint_t flags)
Receive data from a connected socket.
Definition: socket.c:584
char char_t
Definition: compiler_port.h:41
uint16_t year
Definition: date_time.h:46
#define FTP_SERVER_BUFFER_SIZE
Definition: ftp_server.h:93
void ftpServerProcessCmd(FtpServerContext *context, FtpClientConnection *connection)
FTP command processing.
void ftpServerCloseConnection(FtpServerContext *context, FtpClientConnection *connection)
Close client connection properly.
Debugging facilities.
Socket * controlSocket
Control connection socket.
Definition: ftp_server.h:242
FtpControlConnState controlState
Control connection state.
Definition: ftp_server.h:241
error_t fsWriteFile(FsFile *file, void *data, size_t length)
Write data to the specified file.
size_t commandLength
Number of bytes available in the command buffer.
Definition: ftp_server.h:255
uint8_t minutes
Definition: date_time.h:51
void ftpServerReceiveData(FtpServerContext *context, FtpClientConnection *connection)
Receive data on the data connection.
String manipulation helper functions.
void pathCombine(char_t *path, const char_t *more, size_t maxLen)
Concatenate two paths.
Definition: path.c:330
error_t fsReadFile(FsFile *file, void *data, size_t size, size_t *length)
Read data from the specified file.
FTP server (command processing)
#define TRUE
Definition: os_port.h:48
FTP server context.
Definition: ftp_server.h:321
uint8_t day
Definition: date_time.h:48
uint_t ftpServerGetFilePermissions(FtpServerContext *context, FtpClientConnection *connection, const char_t *path)
Get permissions for the specified file or directory.
Error codes description.
char_t response[FTP_SERVER_MAX_LINE_LEN+1]
Response buffer.
Definition: ftp_server.h:256
DateTime modified
Definition: fs_port.h:111
FtpDataConnState dataState
Data connection state.
Definition: ftp_server.h:243
time_t convertDateToUnixTime(const DateTime *date)
Convert date to Unix timestamp.
Definition: date_time.c:256
size_t responseLength
Number of bytes available in the response buffer.
Definition: ftp_server.h:257
void ftpServerAcceptDataConnection(FtpClientConnection *connection)
Accept data connection.
uint8_t month
Definition: date_time.h:47
void pathCanonicalize(char_t *path)
Simplify a path.
Definition: path.c:110
void fsCloseDir(FsDir *dir)
Close a directory stream.
char_t path[FTP_SERVER_MAX_PATH_LEN+1]
Pathname.
Definition: ftp_server.h:253
#define MIN(a, b)
Definition: os_port.h:60
error_t socketShutdown(Socket *socket, uint_t how)
Disable reception, transmission, or both.
Definition: socket.c:760
time_t getCurrentUnixTime(void)
Get current time.
Definition: date_time.c:178
Success.
Definition: error.h:42
Path manipulation helper functions.
error_t
Error codes.
Definition: error.h:40
uint8_t hours
Definition: date_time.h:50
#define FTP_SERVER_180_DAYS
unsigned int uint_t
Definition: compiler_port.h:43
size_t bufferPos
Current position in the I/O buffer.
Definition: ftp_server.h:261
error_t fsReadDir(FsDir *dir, FsDirEntry *dirEntry)
Read an entry from the specified directory stream.
FsDir * dir
Directory pointer.
Definition: ftp_server.h:246
FTP server (event handlers)
Socket * dataSocket
Data connection socket.
Definition: ftp_server.h:244
void ftpServerSendData(FtpServerContext *context, FtpClientConnection *connection)
Send data on the data connection.
void ftpServerDataEventHandler(FtpServerContext *context, FtpClientConnection *connection, uint_t eventFlags)
Data connection event handler.
char_t * buffer
Memory buffer for I/O operations.
Definition: ftp_server.h:259
uint32_t attributes
Definition: fs_port.h:109
void ftpServerControlEventHandler(FtpServerContext *context, FtpClientConnection *connection, uint_t eventFlags)
Control connection event handler.
#define FTP_SERVER_MAX_LINE_LEN
Definition: ftp_server.h:86
char_t command[FTP_SERVER_MAX_LINE_LEN+1]
Incoming command.
Definition: ftp_server.h:254
FsFile * file
File pointer.
Definition: ftp_server.h:245
uint8_t n
#define FTP_SERVER_MAX_PATH_LEN
Definition: ftp_server.h:121
#define FALSE
Definition: os_port.h:44
error_t socketSend(Socket *socket, const void *data, size_t length, size_t *written, uint_t flags)
Send data to a connected socket.
Definition: socket.c:490
FTP server (File Transfer Protocol)
int bool_t
Definition: compiler_port.h:47
FTP client connection.
Definition: ftp_server.h:236
Directory entry.
Definition: fs_port.h:107
void fsCloseFile(FsFile *file)
Close a file.
#define TRACE_DEBUG(...)
Definition: debug.h:98
char_t name[FS_MAX_NAME_LEN+1]
Definition: fs_port.h:112