]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/FTP/FreeRTOS_FTP_server.c
Check socket binding result before doing anything with socket. (This is to address...
[freertos] / FreeRTOS-Plus / Demo / Common / Demo_IP_Protocols / FTP / FreeRTOS_FTP_server.c
1 /*\r
2  * FreeRTOS+TCP V2.0.3\r
3  * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\r
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
6  * this software and associated documentation files (the "Software"), to deal in\r
7  * the Software without restriction, including without limitation the rights to\r
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
9  * the Software, and to permit persons to whom the Software is furnished to do so,\r
10  * subject to the following conditions:\r
11  *\r
12  * The above copyright notice and this permission notice shall be included in all\r
13  * copies or substantial portions of the Software.\r
14  *\r
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21  *\r
22  * http://aws.amazon.com/freertos\r
23  * http://www.FreeRTOS.org\r
24  */\r
25 \r
26 /* Standard includes. */\r
27 #include <stdint.h>\r
28 #include <stdio.h>\r
29 #include <stdlib.h>\r
30 #include <time.h>\r
31 \r
32 /* FreeRTOS includes. */\r
33 #include "FreeRTOS.h"\r
34 #include "task.h"\r
35 #include "portmacro.h"\r
36 \r
37 /* FreeRTOS+TCP includes. */\r
38 #include "FreeRTOS_IP.h"\r
39 #include "FreeRTOS_TCP_IP.h"\r
40 #include "FreeRTOS_Sockets.h"\r
41 #include "FreeRTOS_Stream_Buffer.h"\r
42 \r
43 /* FreeRTOS Protocol includes. */\r
44 #include "FreeRTOS_FTP_commands.h"\r
45 #include "FreeRTOS_TCP_server.h"\r
46 #include "FreeRTOS_server_private.h"\r
47 \r
48 /* Remove the whole file if FTP is not supported. */\r
49 #if( ipconfigUSE_FTP == 1 )\r
50 \r
51 #ifndef HTTP_SERVER_BACKLOG\r
52         #define HTTP_SERVER_BACKLOG                     ( 12 )\r
53 #endif\r
54 \r
55 #if !defined( ARRAY_SIZE )\r
56         #define ARRAY_SIZE( x ) ( BaseType_t ) (sizeof( x ) / sizeof( x )[ 0 ] )\r
57 #endif\r
58 \r
59 #if defined(__WIN32__) && !defined(ipconfigFTP_FS_USES_BACKSLAH)\r
60         #define ipconfigFTP_FS_USES_BACKSLAH    1\r
61 #endif\r
62 \r
63 /* Some defines to make the code more readbale */\r
64 #define pcCOMMAND_BUFFER        pxClient->pxParent->pcCommandBuffer\r
65 #define pcNEW_DIR                       pxClient->pxParent->pcNewDir\r
66 #define pcFILE_BUFFER           pxClient->pxParent->pcFileBuffer\r
67 \r
68 /* This FTP server will only do binary transfers */\r
69 #define TMODE_BINARY    1\r
70 #define TMODE_ASCII             2\r
71 #define TMODE_7BITS             3\r
72 #define TMODE_8BITS             4\r
73 \r
74 /* Ascii character definitions. */\r
75 #define ftpASCII_CR     13\r
76 #define ftpASCII_LF 10\r
77 \r
78 #if defined( FTP_WRITES_ALIGNED ) || defined( ipconfigFTP_WRITES_ALIGNED )\r
79         #error Name change : please rename the define to the new name 'ipconfigFTP_ZERO_COPY_ALIGNED_WRITES'\r
80 #endif\r
81 \r
82 /*\r
83  * ipconfigFTP_ZERO_COPY_ALIGNED_WRITES : experimental optimisation option.\r
84  * If non-zero, receiving data will be done with the zero-copy method and also\r
85  * writes to disk will be done with sector-alignment as much as possible.\r
86  */\r
87 #ifndef ipconfigFTP_ZERO_COPY_ALIGNED_WRITES\r
88         #define ipconfigFTP_ZERO_COPY_ALIGNED_WRITES                    0\r
89 #endif\r
90 \r
91 /*\r
92  * This module only has 2 public functions:\r
93  */\r
94 BaseType_t xFTPClientWork( TCPClient_t *pxClient );\r
95 void vFTPClientDelete( TCPClient_t *pxClient );\r
96 \r
97 /*\r
98  * Process a single command.\r
99  */\r
100 static BaseType_t prvProcessCommand( FTPClient_t *pxClient, BaseType_t xIndex, char *pcRestCommand );\r
101 \r
102 /*\r
103  * Create a socket for a data connection to the FTP client.\r
104  */\r
105 static BaseType_t prvTransferConnect( FTPClient_t *pxClient, BaseType_t xDoListen );\r
106 \r
107 /*\r
108  * Either call listen() or connect() to start the transfer connection.\r
109  */\r
110 static BaseType_t prvTransferStart( FTPClient_t *pxClient );\r
111 \r
112 /*\r
113  * See if the socket has got connected or disconnected. Close the socket if\r
114  * necessary.\r
115  */\r
116 static void prvTransferCheck( FTPClient_t *pxClient );\r
117 \r
118 /*\r
119  * Close the data socket and issue some informative logging.\r
120  */\r
121 static void prvTransferCloseSocket( FTPClient_t *pxClient );\r
122 \r
123 /*\r
124  * Close the file handle (pxReadHandle or pxWriteHandle).\r
125  */\r
126 static void prvTransferCloseFile( FTPClient_t *pxClient );\r
127 \r
128 /*\r
129  * Close a directory (-handle).\r
130  */\r
131 static void prvTransferCloseDir( FTPClient_t *pxClient );\r
132 \r
133 /*\r
134  * Translate a string (indicating a transfer type) to a number.\r
135  */\r
136 static BaseType_t prvGetTransferType( const char *pcType );\r
137 \r
138 #if( ipconfigHAS_PRINTF != 0 )\r
139         /*\r
140          * For nice logging: write an amount (number of bytes), e.g. 3512200 as\r
141          * "3.45 MB"\r
142          */\r
143         static const char *pcMkSize( uint32_t ulAmount, char *pcBuffer, BaseType_t xBufferSize );\r
144 #endif\r
145 \r
146 #if( ipconfigHAS_PRINTF != 0 )\r
147         /*\r
148          * Calculate the average as bytes-per-second, when amount and milliseconds\r
149          * are known.\r
150          */\r
151         static uint32_t ulGetAverage( uint32_t ulAmount, TickType_t xDeltaMs );\r
152 #endif\r
153 \r
154 /*\r
155  * A port command looks like: PORT h1,h2,h3,h4,p1,p2. Parse it and translate it\r
156  * to an IP-address and a port number.\r
157  */\r
158 static UBaseType_t prvParsePortData( const char *pcCommand, uint32_t *pulIPAddress );\r
159 \r
160 /*\r
161  * CWD: Change current working directory.\r
162  */\r
163 \r
164 static BaseType_t prvChangeDir( FTPClient_t *pxClient, char *pcDirectory );\r
165 \r
166 /*\r
167  * RNFR: Rename from ...\r
168  */\r
169 static BaseType_t prvRenameFrom( FTPClient_t *pxClient, const char *pcFileName );\r
170 \r
171 /*\r
172  * RNTO: Rename to ...\r
173  */\r
174 static BaseType_t prvRenameTo( FTPClient_t *pxClient, const char *pcFileName );\r
175 \r
176 /*\r
177  * SITE: Change file permissions.\r
178  */\r
179 static BaseType_t prvSiteCmd( FTPClient_t *pxClient, char *pcRestCommand );\r
180 \r
181 /*\r
182  * DELE: Delete a file.\r
183  */\r
184 static BaseType_t prvDeleteFile( FTPClient_t *pxClient, char *pcFileName );\r
185 \r
186 /*\r
187  * SIZE: get the size of a file (xSendDate = 0).\r
188  * MDTM: get data and time properties (xSendDate = 1).\r
189  */\r
190 static BaseType_t prvSizeDateFile( FTPClient_t *pxClient, char *pcFileName, BaseType_t xSendDate );\r
191 \r
192 /*\r
193  * MKD: Make / create a directory (xDoRemove = 0).\r
194  * RMD: Remove a directory (xDoRemove = 1).\r
195  */\r
196 static BaseType_t prvMakeRemoveDir( FTPClient_t *pxClient, const char *pcDirectory, BaseType_t xDoRemove );\r
197 \r
198 /*\r
199  * The next three commands: LIST, RETR and STOR all require a data socket.\r
200  * The data connection is either started with a 'PORT' or a 'PASV' command.\r
201  * Each of the commands has a prepare- (Prep) and a working- (Work) function.\r
202  * The Work function should be called as long as the data socket is open, and\r
203  * there is data to be transmitted.\r
204  */\r
205 \r
206 /*\r
207  * LIST: Send a directory listing in Unix style.\r
208  */\r
209 static BaseType_t prvListSendPrep( FTPClient_t *pxClient );\r
210 static BaseType_t prvListSendWork( FTPClient_t *pxClient );\r
211 \r
212 /*\r
213  * RETR: Send a file to the FTP client.\r
214  */\r
215 static BaseType_t prvRetrieveFilePrep( FTPClient_t *pxClient, char *pcFileName );\r
216 static BaseType_t prvRetrieveFileWork( FTPClient_t *pxClient );\r
217 \r
218 /*\r
219  * STOR: Receive a file from the FTP client and store it.\r
220  */\r
221 static BaseType_t prvStoreFilePrep( FTPClient_t *pxClient, char *pcFileName );\r
222 static BaseType_t prvStoreFileWork( FTPClient_t *pxClient );\r
223 \r
224 /*\r
225  * Print/format a single directory entry in Unix style.\r
226  */\r
227 static BaseType_t prvGetFileInfoStat( FF_DirEnt_t *pxEntry, char *pcLine, BaseType_t xMaxLength );\r
228 \r
229 /*\r
230  * Send a reply to a socket, either the command- or the data-socket.\r
231  */\r
232 static BaseType_t prvSendReply( Socket_t xSocket, const char *pcBuffer, BaseType_t xLength );\r
233 \r
234 /*\r
235  * Prepend the root directory (if any), plus the current working directory\r
236  * (always), to get an absolute path.\r
237  */\r
238 BaseType_t xMakeAbsolute( FTPClient_t *pxClient, char *pcBuffer, BaseType_t xBufferLength, const char *pcPath );\r
239 \r
240 /*\r
241 \r
242 ####### ##### ######        #     #                ##\r
243  #   ## # # #  #    #       #     #                 #\r
244  #        #    #    #       #     #                 #\r
245  #        #    #    #       #  #  #  ####  ### ##   #    #\r
246  #####    #    #####        #  #  # #    #  # #  #  #   #\r
247  #   #    #    #            #  #  # #    #  ##   #  ####\r
248  #        #    #             ## ##  #    #  #       #   #\r
249  #        #    #             ## ##  #    #  #       #    #\r
250 ####     #### ####           ## ##   ####  ####    ##   ##\r
251 \r
252  *      xFTPClientWork()\r
253  *      will be called by FreeRTOS_TCPServerWork(), after select has expired().\r
254  *      FD_ISSET will not be used.  This work function will always be called at\r
255  *      regular intervals, and also after a select() event has occurred.\r
256  */\r
257 BaseType_t xFTPClientWork( TCPClient_t *pxTCPClient )\r
258 {\r
259 FTPClient_t *pxClient = ( FTPClient_t * ) pxTCPClient;\r
260 BaseType_t xRc;\r
261 \r
262         if( pxClient->bits.bHelloSent == pdFALSE_UNSIGNED )\r
263         {\r
264         BaseType_t xLength;\r
265 \r
266                 pxClient->bits.bHelloSent = pdTRUE_UNSIGNED;\r
267 \r
268                 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
269                         "220 Welcome to the FreeRTOS+TCP FTP server\r\n" );\r
270                 prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );\r
271         }\r
272 \r
273         /* Call recv() in a non-blocking way, to see if there is an FTP command\r
274         sent to this server. */\r
275         xRc = FreeRTOS_recv( pxClient->xSocket, ( void * )pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), 0 );\r
276 \r
277         if( xRc > 0 )\r
278         {\r
279         BaseType_t xIndex;\r
280         const FTPCommand_t *pxCommand;\r
281         char *pcRestCommand;\r
282 \r
283                 if( xRc < ( BaseType_t ) sizeof( pcCOMMAND_BUFFER ) )\r
284                 {\r
285                         pcCOMMAND_BUFFER[ xRc ] = '\0';\r
286                 }\r
287 \r
288                 while( xRc && ( ( pcCOMMAND_BUFFER[ xRc - 1 ] == ftpASCII_CR ) || ( pcCOMMAND_BUFFER[ xRc - 1 ] == ftpASCII_LF ) ) )\r
289                 {\r
290                         pcCOMMAND_BUFFER[ --xRc ] = '\0';\r
291                 }\r
292 \r
293                 /* Now iterate through a list of FTP commands, and look for a match. */\r
294                 pxCommand = xFTPCommands;\r
295                 pcRestCommand = pcCOMMAND_BUFFER;\r
296                 for( xIndex = 0; xIndex < FTP_CMD_COUNT - 1; xIndex++, pxCommand++ )\r
297                 {\r
298                 BaseType_t xLength;\r
299 \r
300                         /* The length of each command is stored as well, just to be a bit\r
301                         quicker here. */\r
302                         xLength = pxCommand->xCommandLength;\r
303 \r
304                         if( ( xRc >= xLength ) && ( memcmp( ( const void * ) pxCommand->pcCommandName, ( const void * ) pcCOMMAND_BUFFER, xLength ) == 0 ) )\r
305                         {\r
306                                 /* A match with an existing command is found.  Skip any\r
307                                 whitespace to get the first parameter. */\r
308                                 pcRestCommand += xLength;\r
309                                 while( ( *pcRestCommand == ' ' ) || ( *pcRestCommand == '\t' ) )\r
310                                 {\r
311                                         pcRestCommand++;\r
312                                 }\r
313                                 break;\r
314                         }\r
315                 }\r
316 \r
317                 /* If the command received was not recognised, xIndex will point to a\r
318                 fake entry called 'ECMD_UNKNOWN'. */\r
319                 prvProcessCommand( pxClient, xIndex, pcRestCommand );\r
320         }\r
321         else if( xRc < 0 )\r
322         {\r
323                 /* The connection will be closed and the client will be deleted. */\r
324                 FreeRTOS_printf( ( "xFTPClientWork: xRc = %ld\n", xRc ) );\r
325         }\r
326 \r
327         /* Does it have an open data connection? */\r
328         if( pxClient->xTransferSocket != FREERTOS_NO_SOCKET )\r
329         {\r
330                 /* See if the connection has changed. */\r
331                 prvTransferCheck( pxClient );\r
332 \r
333                 /* "pcConnectionAck" contains a string like:\r
334                 "Response:      150 Accepted data connection from 192.168.2.3:6789"\r
335                 The socket can only be used once this acknowledgement has been sent. */\r
336                 if( ( pxClient->xTransferSocket != FREERTOS_NO_SOCKET ) && ( pxClient->pcConnectionAck[ 0 ] == '\0' ) )\r
337                 {\r
338                 BaseType_t xClientRc = 0;\r
339 \r
340                         if( pxClient->bits1.bDirHasEntry )\r
341                         {\r
342                                 /* Still listing a directory. */\r
343                                 xClientRc = prvListSendWork( pxClient );\r
344                         }\r
345                         else if( pxClient->pxReadHandle != NULL )\r
346                         {\r
347                                 /* Sending a file. */\r
348                                 xClientRc = prvRetrieveFileWork( pxClient );\r
349                         }\r
350                         else if( pxClient->pxWriteHandle != NULL )\r
351                         {\r
352                                 /* Receiving a file. */\r
353                                 xClientRc = prvStoreFileWork( pxClient );\r
354                         }\r
355 \r
356                         if( xClientRc < 0 )\r
357                         {\r
358                                 prvTransferCloseSocket( pxClient );\r
359                                 prvTransferCloseFile( pxClient );\r
360                         }\r
361                 }\r
362         }\r
363 \r
364         return xRc;\r
365 }\r
366 /*-----------------------------------------------------------*/\r
367 \r
368 static void prvTransferCloseDir( FTPClient_t *pxClient )\r
369 {\r
370         /* Nothing to close for +FAT. */\r
371         ( void ) pxClient;\r
372 }\r
373 /*-----------------------------------------------------------*/\r
374 \r
375 void vFTPClientDelete( TCPClient_t *pxTCPClient )\r
376 {\r
377 FTPClient_t *pxClient = ( FTPClient_t * ) pxTCPClient;\r
378 \r
379         /* Close any directory-listing-handles (not used by +FAT ). */\r
380         prvTransferCloseDir( pxClient );\r
381         /* Close the data-socket. */\r
382         prvTransferCloseSocket( pxClient );\r
383         /* Close any open file handle. */\r
384         prvTransferCloseFile( pxClient );\r
385 \r
386         /* Close the FTP command socket */\r
387         if( pxClient->xSocket != FREERTOS_NO_SOCKET )\r
388         {\r
389                 FreeRTOS_FD_CLR( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_ALL );\r
390                 FreeRTOS_closesocket( pxClient->xSocket );\r
391                 pxClient->xSocket = FREERTOS_NO_SOCKET;\r
392         }\r
393 }\r
394 /*-----------------------------------------------------------*/\r
395 \r
396 static BaseType_t prvProcessCommand( FTPClient_t *pxClient, BaseType_t xIndex, char *pcRestCommand )\r
397 {\r
398 const FTPCommand_t *pxFTPCommand = &( xFTPCommands[ xIndex ] );\r
399 const char *pcMyReply = NULL;\r
400 BaseType_t xResult = 0;\r
401 \r
402         if( ( pxFTPCommand->ucCommandType != ECMD_PASS ) && ( pxFTPCommand->ucCommandType != ECMD_PORT ) )\r
403         {\r
404                 FreeRTOS_printf( ( "       %s %s\n", pxFTPCommand->pcCommandName, pcRestCommand ) );\r
405         }\r
406 \r
407         if( ( pxFTPCommand->checkLogin != pdFALSE ) && ( pxClient->bits.bLoggedIn == pdFALSE_UNSIGNED ) )\r
408         {\r
409                 pcMyReply = REPL_530; /* Please first log in. */\r
410         }\r
411         else if( ( pxFTPCommand->checkNullArg != pdFALSE ) && ( ( pcRestCommand == NULL ) || ( pcRestCommand[ 0 ] == '\0' ) ) )\r
412         {\r
413                 pcMyReply = REPL_501; /* Command needs a parameter. */\r
414         }\r
415 \r
416         if( pcMyReply == NULL )\r
417         {\r
418                 switch( pxFTPCommand->ucCommandType )\r
419                 {\r
420                 case ECMD_USER: /* User. */\r
421                         /* User name has been entered, expect password. */\r
422                         pxClient->bits.bStatusUser = pdTRUE_UNSIGNED;\r
423 \r
424                         #if( ipconfigFTP_HAS_USER_PASSWORD_HOOK != 0 )/*_RB_ Needs defaulting and adding to the web documentation. */\r
425                         {\r
426                                 /* Save the user name in 'pcFileName'. */\r
427                                 snprintf( pxClient->pcFileName, sizeof( pxClient->pcFileName ), "%s", pcRestCommand );\r
428 \r
429                                 /* The USER name is presented to the application.  The function\r
430                                 may return a const string like "331 Please enter your\r
431                                 password\r\n". */\r
432                                 pcMyReply = pcApplicationFTPUserHook( pxClient->pcFileName );\r
433                                 if( pcMyReply == NULL )\r
434                                 {\r
435                                         pcMyReply = REPL_331_ANON;\r
436                                 }\r
437                         }\r
438                         #else\r
439                         {\r
440                                 /* No password checks, any password will be accepted. */\r
441                                 pcMyReply = REPL_331_ANON;\r
442                         }\r
443                         #endif /* ipconfigFTP_HAS_USER_PASSWORD_HOOK != 0 */\r
444 \r
445                         #if( ipconfigFTP_HAS_USER_PROPERTIES_HOOK != 0 )/*_RB_ Needs defaulting and adding to the web documentation. */\r
446                         {\r
447                         FTPUserProperties_t xProperties;\r
448 \r
449                                 xProperties.pcRootDir = pxClient->pcRootDir;\r
450                                 xProperties.xReadOnly = pdFALSE;\r
451                                 xProperties.usPortNumber = pxClient->usClientPort;\r
452                                 vApplicationFTPUserPropertiesHook( pxClient->pcFileName, &( xProperties ) );\r
453 \r
454                                 if( xProperties.pcRootDir != NULL )\r
455                                 {\r
456                                         pxClient->pcRootDir = xProperties.pcRootDir;\r
457                                 }\r
458                                 pxClient->bits.bReadOnly = ( xProperties.xReadOnly != pdFALSE_UNSIGNED );\r
459                         }\r
460                         #endif /* ipconfigFTP_HAS_USER_PROPERTIES_HOOK */\r
461                         break;\r
462 \r
463                 case ECMD_PASS: /* Password. */\r
464                         pxClient->ulRestartOffset = 0;\r
465                         if( pxClient->bits.bStatusUser == pdFALSE_UNSIGNED )\r
466                         {\r
467                                 pcMyReply = REPL_503;   /* "503 Bad sequence of commands.\r\n". */\r
468                         }\r
469                         else\r
470                         {\r
471                         BaseType_t xAllow;\r
472 \r
473                                 pxClient->bits.bStatusUser = pdFALSE_UNSIGNED;\r
474                                 #if( ipconfigFTP_HAS_USER_PASSWORD_HOOK != 0 )\r
475                                 {\r
476                                         xAllow = xApplicationFTPPasswordHook( pxClient->pcFileName, pcRestCommand );\r
477                                 }\r
478                                 #else\r
479                                 {\r
480                                         xAllow = 1;\r
481                                 }\r
482                                 #endif /* ipconfigFTP_HAS_USER_PASSWORD_HOOK */\r
483 \r
484                                 if( xAllow > 0 )\r
485                                 {\r
486                                         pxClient->bits.bLoggedIn = pdTRUE_UNSIGNED;  /* Client has now logged in. */\r
487                                         pcMyReply = "230 OK.  Current directory is /\r\n";\r
488                                 }\r
489                                 else\r
490                                 {\r
491                                         pcMyReply = "530 Login incorrect\r\n"; /* 530 Login incorrect. */\r
492                                 }\r
493 \r
494                                 strcpy( pxClient->pcCurrentDir, ( const char * ) "/" );\r
495                         }\r
496                         break;\r
497 \r
498                 case ECMD_SYST: /* System. */\r
499                         snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "215 UNIX Type: L8\r\n" );\r
500                         pcMyReply = pcCOMMAND_BUFFER;\r
501                         break;\r
502 \r
503                 case ECMD_PWD:  /* Get working directory. */\r
504                         xMakeRelative( pxClient, pcFILE_BUFFER, sizeof( pcFILE_BUFFER ), pxClient->pcCurrentDir );\r
505                         snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), REPL_257_PWD, pcFILE_BUFFER );\r
506                         pcMyReply = pcCOMMAND_BUFFER;\r
507                         break;\r
508 \r
509                 case ECMD_REST:\r
510                         if( pxClient->bits.bReadOnly != pdFALSE_UNSIGNED )\r
511                         {\r
512                                 pcMyReply = REPL_553_READ_ONLY;\r
513                         }\r
514                         else\r
515                         {\r
516                         const char *pcPtr = pcRestCommand;\r
517 \r
518                                 while( *pcPtr == ' ' )\r
519                                 {\r
520                                         pcPtr++;\r
521                                 }\r
522 \r
523                                 if( ( *pcPtr >= '0' ) && ( *pcPtr <= '9' ) )\r
524                                 {\r
525                                         sscanf( pcPtr, "%lu", &pxClient->ulRestartOffset );\r
526                                         snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
527                                                 "350 Restarting at %lu. Send STORE or RETRIEVE\r\n", pxClient->ulRestartOffset );\r
528                                         pcMyReply = pcCOMMAND_BUFFER;\r
529                                 }\r
530                                 else\r
531                                 {\r
532                                         pcMyReply = REPL_500; /* 500 Syntax error, command unrecognised. */\r
533                                 }\r
534                         }\r
535                         break;\r
536 \r
537                 case ECMD_NOOP: /* NOP operation */\r
538                         if( pxClient->xTransferSocket != FREERTOS_NO_SOCKET )\r
539                         {\r
540                                 pcMyReply = REPL_200_PROGRESS;\r
541                         }\r
542                         else\r
543                         {\r
544                                 pcMyReply = REPL_200;\r
545                         }\r
546                         break;\r
547 \r
548                 case ECMD_TYPE: /* Ask or set transfer type. */\r
549                         {\r
550                                 /* e.g. "TYPE I" for Images (binary). */\r
551                         BaseType_t xType = prvGetTransferType( pcRestCommand );\r
552 \r
553                                 if( xType < 0 )\r
554                                 {\r
555                                         /* TYPE not recognised. */\r
556                                         pcMyReply = REPL_500;\r
557                                 }\r
558                                 else\r
559                                 {\r
560                                         pxClient->xTransType = xType;\r
561                                         pcMyReply = REPL_200;\r
562                                 }\r
563                         }\r
564                         break;\r
565 \r
566                 case ECMD_PASV: /* Enter passive mode. */\r
567                         /* Connect passive: Server will listen() and wait for a connection.\r
568                         Start up a new data connection with 'xDoListen' set to true. */\r
569                         if( prvTransferConnect( pxClient, pdTRUE ) != pdTRUE )\r
570                         {\r
571                                 pcMyReply = REPL_502;\r
572                         }\r
573                         else\r
574                         {\r
575                         uint32_t ulIP;\r
576                         uint16_t ulPort;\r
577                         struct freertos_sockaddr xLocalAddress;\r
578                         struct freertos_sockaddr xRemoteAddress;\r
579 \r
580                                 FreeRTOS_GetLocalAddress( pxClient->xTransferSocket, &xLocalAddress );\r
581                                 FreeRTOS_GetRemoteAddress( pxClient->xSocket, &xRemoteAddress );\r
582 \r
583                                 ulIP = FreeRTOS_ntohl( xLocalAddress.sin_addr );\r
584                                 pxClient->ulClientIP = FreeRTOS_ntohl( xRemoteAddress.sin_addr );\r
585                                 ulPort = FreeRTOS_ntohs( xLocalAddress.sin_port );\r
586 \r
587                                 pxClient->usClientPort = FreeRTOS_ntohs( xRemoteAddress.sin_port );\r
588 \r
589                                 /* REPL_227_D "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d). */\r
590                                 snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), REPL_227_D,\r
591                                         ( unsigned )ulIP >> 24,\r
592                                         ( unsigned )( ulIP >> 16 ) & 0xFF,\r
593                                         ( unsigned )( ulIP >> 8 ) & 0xFF,\r
594                                         ( unsigned )ulIP & 0xFF,\r
595                                         ( unsigned )ulPort >> 8,\r
596                                         ( unsigned )ulPort & 0xFF );\r
597 \r
598                                 pcMyReply = pcCOMMAND_BUFFER;\r
599                         }\r
600                         break;\r
601 \r
602                 case ECMD_PORT: /* Active connection to the client. */\r
603                         /* The client uses this command to tell the server to what\r
604                         client-side port the server should contact; use of this command\r
605                         indicates an active data transfer. e.g. PORT 192,168,1,2,4,19. */\r
606                         {\r
607                         uint32_t ulIPAddress = 0;\r
608                         UBaseType_t uxPort;\r
609 \r
610                                 uxPort = prvParsePortData( pcRestCommand, &ulIPAddress );\r
611                                 FreeRTOS_printf( ("       PORT %lxip:%ld\n", ulIPAddress, uxPort ) );\r
612 \r
613                                 if( uxPort == 0u )\r
614                                 {\r
615                                         pcMyReply = REPL_501;\r
616                                 }\r
617                                 else if( prvTransferConnect( pxClient, pdFALSE ) != pdTRUE )\r
618                                 {\r
619                                         /* Call prvTransferConnect() with 'xDoListen' = false for an\r
620                                         active connect(). */\r
621                                         pcMyReply = REPL_501;\r
622                                 }\r
623                                 else\r
624                                 {\r
625                                         pxClient->usClientPort = ( uint16_t ) uxPort;\r
626                                         pxClient->ulClientIP = ulIPAddress;\r
627                                         FreeRTOS_printf( ("Client address %lxip:%lu\n", ulIPAddress, uxPort ) );\r
628                                         pcMyReply = REPL_200;\r
629                                 }\r
630                         }\r
631                         break;\r
632 \r
633                 case ECMD_CWD:  /* Change current working directory. */\r
634                         prvChangeDir( pxClient, pcRestCommand );\r
635                         break;\r
636 \r
637                 case ECMD_RNFR:\r
638                         if( pxClient->bits.bReadOnly != pdFALSE_UNSIGNED )\r
639                         {\r
640                                 pcMyReply = REPL_553_READ_ONLY;\r
641                         }\r
642                         else\r
643                         {\r
644                                 prvRenameFrom( pxClient, pcRestCommand );\r
645                         }\r
646                         break;\r
647 \r
648                 case ECMD_RNTO:\r
649                         if( pxClient->bits.bInRename == pdFALSE_UNSIGNED )\r
650                         {\r
651                                 pcMyReply = REPL_503;   /* "503 Bad sequence of commands. */\r
652                         }\r
653                         else\r
654                         {\r
655                                 prvRenameTo( pxClient, pcRestCommand );\r
656                         }\r
657                         break;\r
658 \r
659                 case ECMD_SITE: /* Set file permissions */\r
660                         if( pxClient->bits.bReadOnly != pdFALSE_UNSIGNED )\r
661                         {\r
662                                 pcMyReply = REPL_553_READ_ONLY;\r
663                         }\r
664                         else if( prvSiteCmd( pxClient, pcRestCommand ) == pdFALSE )\r
665                         {\r
666                                 pcMyReply = REPL_202;\r
667                         }\r
668                         break;\r
669 \r
670                 case ECMD_DELE:\r
671                         if( pxClient->bits.bReadOnly != pdFALSE_UNSIGNED )\r
672                         {\r
673                                 pcMyReply = REPL_553_READ_ONLY;\r
674                         }\r
675                         else\r
676                         {\r
677                                 prvDeleteFile( pxClient, pcRestCommand );\r
678                         }\r
679                         break;\r
680 \r
681                 case ECMD_MDTM:\r
682                         prvSizeDateFile( pxClient, pcRestCommand, pdTRUE );\r
683                         break;\r
684 \r
685                 case ECMD_SIZE:\r
686                         if( pxClient->pxWriteHandle != NULL )\r
687                         {\r
688                                 /* This SIZE query is probably about a file which is now being\r
689                                 received.  If so, return the value of pxClient->ulRecvBytes,\r
690                                 pcRestCommand points to 'pcCommandBuffer', make it free by\r
691                                 copying it to pcNewDir. */\r
692 \r
693                                 xMakeAbsolute( pxClient, pcNEW_DIR, sizeof( pcNEW_DIR ), pcRestCommand );\r
694 \r
695                                 if( strcmp( pcNEW_DIR, pcRestCommand ) == 0 )\r
696                                 {\r
697                                 BaseType_t xCount;\r
698                                         for( xCount = 0; xCount < 3 && pxClient->pxWriteHandle; xCount++ )\r
699                                         {\r
700                                                 prvStoreFileWork( pxClient );\r
701                                         }\r
702                                         if( pxClient->pxWriteHandle != NULL )\r
703                                         {\r
704                                                 /* File being queried is still open, return number of\r
705                                                 bytes received until now. */\r
706                                                 snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "213 %lu\r\n", pxClient->ulRecvBytes );\r
707                                                 pcMyReply = pcCOMMAND_BUFFER;\r
708                                         } /* otherwise, do a normal stat(). */\r
709                                 }\r
710                                 strcpy( pcRestCommand, pcNEW_DIR );\r
711                         }\r
712                         if( pcMyReply == NULL )\r
713                         {\r
714                                 prvSizeDateFile( pxClient, pcRestCommand, pdFALSE );\r
715                         }\r
716                         break;\r
717                 case ECMD_MKD:\r
718                 case ECMD_RMD:\r
719                         if( pxClient->bits.bReadOnly != pdFALSE_UNSIGNED )\r
720                         {\r
721                                 pcMyReply = REPL_553_READ_ONLY;\r
722                         }\r
723                         else\r
724                         {\r
725                                 prvMakeRemoveDir( pxClient, pcRestCommand, pxFTPCommand->ucCommandType == ECMD_RMD );\r
726                         }\r
727                         break;\r
728                 case ECMD_CDUP:\r
729                         prvChangeDir( pxClient, ".." );\r
730                         break;\r
731 \r
732                 case ECMD_QUIT:\r
733                         prvSendReply( pxClient->xSocket, REPL_221, 0 );\r
734                         pxClient->bits.bLoggedIn = pdFALSE_UNSIGNED;\r
735                         break;\r
736                 case ECMD_LIST:\r
737                 case ECMD_RETR:\r
738                 case ECMD_STOR:\r
739                         if( ( pxClient->xTransferSocket == FREERTOS_NO_SOCKET ) &&\r
740                                 ( ( pxFTPCommand->ucCommandType != ECMD_STOR ) ||\r
741                                   ( pxClient->bits1.bEmptyFile == pdFALSE_UNSIGNED ) ) )\r
742                         {\r
743                                 /* Sending "425 Can't open data connection." :\r
744                                 Before receiving any of these commands, there must have been a\r
745                                 PORT or PASV command, which causes the creation of a data socket. */\r
746                                 /* There is one exception: a STOR command is received while the\r
747                                 data connection has already been closed.  This is tested with the\r
748                                 'bEmptyFile' flag. */\r
749                                 pcMyReply = REPL_425;\r
750                         }\r
751                         else\r
752                         {\r
753                                 /* In case an empty file was received ( bits1.bEmptyFile ), the\r
754                                 transfer socket never delivered any data.  Check if the transfer\r
755                                 socket is still open: */\r
756                                 if( pxClient->xTransferSocket != FREERTOS_NO_SOCKET )\r
757                                 {\r
758                                         prvTransferCheck( pxClient );\r
759                                 }\r
760                                 switch( pxFTPCommand->ucCommandType )\r
761                                 {\r
762                                 case ECMD_LIST:\r
763                                         prvListSendPrep( pxClient );\r
764                                         break;\r
765                                 case ECMD_RETR:\r
766                                         prvRetrieveFilePrep( pxClient, pcRestCommand );\r
767                                         break;\r
768                                 case ECMD_STOR:\r
769                                         if( pxClient->bits.bReadOnly != pdFALSE_UNSIGNED )\r
770                                         {\r
771                                                 pcMyReply = REPL_553_READ_ONLY;\r
772                                         }\r
773                                         else\r
774                                         {\r
775                                                 prvStoreFilePrep( pxClient, pcRestCommand );\r
776                                                 if( pxClient->bits1.bEmptyFile != pdFALSE_UNSIGNED )\r
777                                                 {\r
778                                                         /* Although the 'xTransferSocket' is closed already,\r
779                                                         call this function just for the logging. */\r
780                                                         prvTransferCloseSocket( pxClient );\r
781 \r
782                                                         /* Close an empty file. */\r
783                                                         prvTransferCloseFile( pxClient );\r
784                                                 }\r
785                                         }\r
786                                         break;\r
787                                 }\r
788                         }\r
789                         break;\r
790 \r
791                 case ECMD_FEAT:\r
792                         {\r
793                                 static const char pcFeatAnswer[] =\r
794                                         "211-Features:\x0a"\r
795                                         /* The MDTM command is only allowed when\r
796                                         there is support for date&time. */\r
797                                 #if( ffconfigTIME_SUPPORT != 0 )\r
798                                         " MDTM\x0a"\r
799                                 #endif\r
800                                         " REST STREAM\x0a"\r
801                                         " SIZE\x0d\x0a"\r
802                                         "211 End\x0d\x0a";\r
803                                 pcMyReply = pcFeatAnswer;\r
804                         }\r
805                         break;\r
806 \r
807                 case ECMD_UNKNOWN:\r
808                         FreeRTOS_printf( ("ftp::processCmd: Cmd %s unknown\n", pcRestCommand ) );\r
809                         pcMyReply = REPL_500;\r
810                         break;\r
811                 }\r
812         }\r
813         if( pxFTPCommand->ucCommandType != ECMD_RNFR )\r
814         {\r
815                 pxClient->bits.bInRename = pdFALSE_UNSIGNED;\r
816         }\r
817 \r
818         if( pcMyReply != NULL )\r
819         {\r
820                 xResult = prvSendReply( pxClient->xSocket, pcMyReply, strlen( pcMyReply ) );\r
821         }\r
822 \r
823         return xResult;\r
824 }\r
825 /*-----------------------------------------------------------*/\r
826 \r
827 static BaseType_t prvTransferConnect( FTPClient_t *pxClient, BaseType_t xDoListen )\r
828 {\r
829 Socket_t xSocket;\r
830 BaseType_t xResult;\r
831 \r
832         /* Open a socket for a data connection with the FTP client.\r
833         Happens after a PORT or a PASV command. */\r
834 \r
835         /* Make sure the previous socket is deleted and flags reset */\r
836         prvTransferCloseSocket( pxClient );\r
837 \r
838         pxClient->bits1.bEmptyFile = pdFALSE_UNSIGNED;\r
839 \r
840         xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_STREAM, FREERTOS_IPPROTO_TCP );\r
841 \r
842         if( ( xSocket != FREERTOS_NO_SOCKET ) && ( xSocket != FREERTOS_INVALID_SOCKET ) )\r
843         {\r
844         BaseType_t xSmallTimeout = pdMS_TO_TICKS( 100 );\r
845         struct freertos_sockaddr xAddress;\r
846 \r
847         #if( ipconfigFTP_TX_BUFSIZE > 0 )\r
848                 WinProperties_t xWinProps;\r
849         #endif\r
850                 xAddress.sin_addr = FreeRTOS_GetIPAddress( );   /* Single NIC, currently not used */\r
851                 xAddress.sin_port = FreeRTOS_htons( 0 );                /* Bind to any available port number */\r
852 \r
853                 BaseType_t xBindResult;\r
854                 xBindResult = FreeRTOS_bind( xSocket, &xAddress, sizeof( xAddress ) );\r
855                 if ( xBindResult != 0 )\r
856                 {\r
857                         FreeRTOS_printf( ( "FreeRTOS_bind() failed\n" ) );\r
858                         return xBindResult;\r
859                 }\r
860 \r
861                 #if( ipconfigFTP_TX_BUFSIZE > 0 )\r
862                 {\r
863                         /* Fill in the buffer and window sizes that will be used by the\r
864                         socket. */\r
865                         xWinProps.lTxBufSize = ipconfigFTP_TX_BUFSIZE;\r
866                         xWinProps.lTxWinSize = ipconfigFTP_TX_WINSIZE;\r
867                         xWinProps.lRxBufSize = ipconfigFTP_RX_BUFSIZE;\r
868                         xWinProps.lRxWinSize = ipconfigFTP_RX_WINSIZE;\r
869 \r
870                         /* Set the window and buffer sizes. */\r
871                         FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_WIN_PROPERTIES, ( void * ) &xWinProps,     sizeof( xWinProps ) );\r
872                 }\r
873                 #endif\r
874 \r
875                 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, ( void * ) &xSmallTimeout, sizeof( BaseType_t ) );\r
876                 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_SNDTIMEO, ( void * ) &xSmallTimeout, sizeof( BaseType_t ) );\r
877 \r
878                 /* The same instance of the socket will be used for the connection and\r
879                 data transport. */\r
880                 if( xDoListen != pdFALSE )\r
881                 {\r
882                 BaseType_t xTrueValue = pdTRUE;\r
883                         FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_REUSE_LISTEN_SOCKET, ( void * ) &xTrueValue, sizeof( xTrueValue ) );\r
884                 }\r
885                 pxClient->bits1.bIsListen = xDoListen;\r
886                 pxClient->xTransferSocket = xSocket;\r
887 \r
888                 if( xDoListen != pdFALSE )\r
889                 {\r
890                         FreeRTOS_FD_SET( xSocket, pxClient->pxParent->xSocketSet, eSELECT_EXCEPT | eSELECT_READ );\r
891                         /* Calling FreeRTOS_listen( ) */\r
892                         xResult = prvTransferStart( pxClient );\r
893                         if( xResult >= 0 )\r
894                         {\r
895                                 xResult = pdTRUE;\r
896                         }\r
897                 }\r
898                 else\r
899                 {\r
900                         FreeRTOS_FD_SET( xSocket, pxClient->pxParent->xSocketSet, eSELECT_EXCEPT | eSELECT_READ | eSELECT_WRITE );\r
901                         xResult = pdTRUE;\r
902                 }\r
903         }\r
904         else\r
905         {\r
906                 FreeRTOS_printf( ( "FreeRTOS_socket() failed\n" ) );\r
907                 xResult = -pdFREERTOS_ERRNO_ENOMEM;\r
908         }\r
909 \r
910         /* An active socket (PORT) should connect() later. */\r
911         return xResult;\r
912 }\r
913 /*-----------------------------------------------------------*/\r
914 \r
915 static BaseType_t prvTransferStart( FTPClient_t *pxClient )\r
916 {\r
917 BaseType_t xResult;\r
918 \r
919         /* A transfer socket has been opened, now either call listen() for 'PASV'\r
920         or connect() for the 'PORT' command. */\r
921         if( pxClient->bits1.bIsListen != pdFALSE_UNSIGNED )\r
922         {\r
923                 xResult = FreeRTOS_listen( pxClient->xTransferSocket, 1 );\r
924         }\r
925         else\r
926         {\r
927         struct freertos_sockaddr xAddress;\r
928 \r
929                 xAddress.sin_addr = FreeRTOS_htonl( pxClient->ulClientIP );\r
930                 xAddress.sin_port = FreeRTOS_htons( pxClient->usClientPort );\r
931                 /* Start an active connection for this data socket */\r
932                 xResult = FreeRTOS_connect( pxClient->xTransferSocket, &xAddress, sizeof( xAddress ) );\r
933         }\r
934 \r
935         return xResult;\r
936 }\r
937 /*-----------------------------------------------------------*/\r
938 \r
939 static void prvTransferCheck( FTPClient_t *pxClient )\r
940 {\r
941 BaseType_t xRxSize;\r
942 \r
943         /* A data transfer is busy. Check if there are changes in connectedness. */\r
944         xRxSize = FreeRTOS_rx_size( pxClient->xTransferSocket );\r
945 \r
946         if( pxClient->bits1.bClientConnected == pdFALSE_UNSIGNED )\r
947         {\r
948                 /* The time to receive a small file can be so short, that we don't even\r
949                 see that the socket gets connected and disconnected. Therefore, check\r
950                 the sizeof of the RX buffer. */\r
951                 {\r
952                 struct freertos_sockaddr xAddress;\r
953                 Socket_t xNexSocket;\r
954                 socklen_t xSocketLength = sizeof( xAddress );\r
955 \r
956                         if( pxClient->bits1.bIsListen != pdFALSE_UNSIGNED )\r
957                         {\r
958                                 xNexSocket = FreeRTOS_accept( pxClient->xTransferSocket, &xAddress, &xSocketLength);\r
959                                 if( ( ( xNexSocket != FREERTOS_NO_SOCKET ) && ( xNexSocket != FREERTOS_INVALID_SOCKET ) ) ||\r
960                                         xRxSize > 0 )\r
961                                 {\r
962                                         pxClient->bits1.bClientConnected = pdTRUE_UNSIGNED;\r
963                                 }\r
964                         }\r
965                         else\r
966                         {\r
967                                 if( FreeRTOS_issocketconnected( pxClient->xTransferSocket ) > 0 ||\r
968                                         xRxSize > 0 )\r
969                                 {\r
970                                         pxClient->bits1.bClientConnected = pdTRUE_UNSIGNED;\r
971                                 }\r
972                         }\r
973                         if(     pxClient->bits1.bClientConnected != pdFALSE_UNSIGNED )\r
974                         {\r
975                                 pxClient->bits1.bEmptyFile = pdFALSE_UNSIGNED;\r
976                                 #if( ipconfigHAS_PRINTF != 0 )\r
977                                 {\r
978                                         struct freertos_sockaddr xRemoteAddress, xLocalAddress;\r
979                                         FreeRTOS_GetRemoteAddress( pxClient->xTransferSocket, &xRemoteAddress );\r
980                                         FreeRTOS_GetLocalAddress( pxClient->xTransferSocket, &xLocalAddress );\r
981                                         FreeRTOS_printf( ( "%s Connected from %u to %u\n",\r
982                                                 pxClient->bits1.bIsListen != pdFALSE_UNSIGNED ? "PASV" : "PORT",\r
983                                                 ( unsigned ) FreeRTOS_ntohs( xLocalAddress.sin_port ),\r
984                                                 ( unsigned ) FreeRTOS_ntohs( xRemoteAddress.sin_port ) ) );\r
985                                 }\r
986                                 #endif /* ipconfigHAS_PRINTF */\r
987                                 FreeRTOS_FD_CLR( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );\r
988                                 FreeRTOS_FD_SET( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_READ|eSELECT_EXCEPT );\r
989                         }\r
990                 }\r
991         }\r
992 \r
993         if ( pxClient->bits1.bClientConnected != pdFALSE_UNSIGNED )\r
994         {\r
995                 if( pxClient->pcConnectionAck[ 0 ] != '\0' )\r
996                 {\r
997                 BaseType_t xLength;\r
998                 BaseType_t xRemotePort;\r
999                 struct freertos_sockaddr xRemoteAddress;\r
1000 \r
1001                         FreeRTOS_GetRemoteAddress( pxClient->xTransferSocket, &xRemoteAddress );\r
1002                         xRemotePort = FreeRTOS_ntohs( xRemoteAddress.sin_port );\r
1003 \r
1004                         /* Tell on the command port 21 we have a data connection */\r
1005                         xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
1006                                 pxClient->pcConnectionAck, pxClient->ulClientIP, xRemotePort );\r
1007 \r
1008                         prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );\r
1009                         pxClient->pcConnectionAck[ 0 ] = '\0';\r
1010                 }\r
1011 \r
1012                 if( ( FreeRTOS_issocketconnected( pxClient->xTransferSocket ) == pdFALSE ) && FreeRTOS_rx_size( pxClient->xTransferSocket ) == 0 )\r
1013                 {\r
1014                         prvTransferCloseSocket( pxClient );\r
1015                         prvTransferCloseFile( pxClient );\r
1016                 }\r
1017         }\r
1018 }\r
1019 /*-----------------------------------------------------------*/\r
1020 \r
1021 static void prvTransferCloseSocket( FTPClient_t *pxClient )\r
1022 {\r
1023         if( pxClient->xTransferSocket != FREERTOS_NO_SOCKET )\r
1024         {\r
1025                 /* DEBUGGING ONLY */\r
1026                 BaseType_t xRxSize = FreeRTOS_rx_size( pxClient->xTransferSocket );\r
1027                 if( xRxSize > 0 )\r
1028                 {\r
1029                 BaseType_t xRxSize2;\r
1030                 BaseType_t xStatus;\r
1031                         prvStoreFileWork( pxClient );\r
1032                         xStatus = FreeRTOS_connstatus( pxClient->xTransferSocket );\r
1033                         xRxSize2 = FreeRTOS_rx_size( pxClient->xTransferSocket );\r
1034                         FreeRTOS_printf( ( "FTP: WARNING: %s: RX size = %ld -> %ld (%s)\n",\r
1035                                 FreeRTOS_GetTCPStateName( xStatus ),\r
1036                                 xRxSize, xRxSize2, pxClient->pcFileName ) );\r
1037                         if( xRxSize2 > 1 )\r
1038                         {\r
1039                                 return;\r
1040                         }\r
1041 \r
1042                         /* Remove compiler warnings in case FreeRTOS_printf() is not\r
1043                         defined. */\r
1044                         ( void ) xStatus;\r
1045                 }\r
1046         }\r
1047 \r
1048         if( ( pxClient->pxWriteHandle != NULL ) || ( pxClient->pxReadHandle != NULL ) )\r
1049         {\r
1050         BaseType_t xLength;\r
1051         char pcStrBuf[ 32 ];\r
1052 \r
1053                 if( pxClient->bits1.bHadError == pdFALSE_UNSIGNED )\r
1054                 {\r
1055                         xLength = snprintf( pxClient->pcClientAck, sizeof( pxClient->pcClientAck ),\r
1056                                         "226 Closing connection %d bytes transmitted\r\n", ( int ) pxClient->ulRecvBytes );\r
1057                 }\r
1058                 else\r
1059                 {\r
1060                         xLength = snprintf( pxClient->pcClientAck, sizeof( pxClient->pcClientAck ),\r
1061                                         "451 Requested action aborted after %d bytes\r\n", ( int ) pxClient->ulRecvBytes );\r
1062                 }\r
1063 \r
1064                 /* Tell on the command socket the data connection is now closed. */\r
1065                 prvSendReply( pxClient->xSocket, pxClient->pcClientAck, xLength );\r
1066 \r
1067                 #if( ipconfigHAS_PRINTF != 0 )\r
1068                 {\r
1069                 TickType_t xDelta;\r
1070                 uint32_t ulAverage;\r
1071                         xDelta = xTaskGetTickCount( ) - pxClient->xStartTime;\r
1072                         ulAverage = ulGetAverage( pxClient->ulRecvBytes, xDelta );\r
1073 \r
1074                         FreeRTOS_printf( ("FTP: %s: '%s' %lu Bytes (%s/sec)\n",\r
1075                                 pxClient->pxReadHandle ? "sent" : "recv",\r
1076                                 pxClient->pcFileName,\r
1077                                 pxClient->ulRecvBytes,\r
1078                                 pcMkSize( ulAverage, pcStrBuf, sizeof( pcStrBuf ) ) ) );\r
1079                 }\r
1080                 #endif\r
1081         }\r
1082 \r
1083         if( pxClient->xTransferSocket != FREERTOS_NO_SOCKET )\r
1084         {\r
1085                 FreeRTOS_FD_CLR( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_ALL );\r
1086                 FreeRTOS_closesocket( pxClient->xTransferSocket );\r
1087                 pxClient->xTransferSocket = FREERTOS_NO_SOCKET;\r
1088                 if( pxClient->ulRecvBytes == 0ul )\r
1089                 {\r
1090                         /* Received zero bytes: an empty file */\r
1091                         pxClient->bits1.bEmptyFile = pdTRUE_UNSIGNED;\r
1092                 }\r
1093                 else\r
1094                 {\r
1095                         pxClient->bits1.bEmptyFile = pdFALSE_UNSIGNED;\r
1096                 }\r
1097         }\r
1098         pxClient->bits1.bIsListen = pdFALSE_UNSIGNED;\r
1099         pxClient->bits1.bDirHasEntry = pdFALSE_UNSIGNED;\r
1100         pxClient->bits1.bClientConnected = pdFALSE_UNSIGNED;\r
1101         pxClient->bits1.bHadError = pdFALSE_UNSIGNED;\r
1102 }\r
1103 /*-----------------------------------------------------------*/\r
1104 \r
1105 static void prvTransferCloseFile( FTPClient_t *pxClient )\r
1106 {\r
1107         if( pxClient->pxWriteHandle != NULL )\r
1108         {\r
1109                 ff_fclose( pxClient->pxWriteHandle );\r
1110                 pxClient->pxWriteHandle = NULL;\r
1111                 #if( ipconfigFTP_HAS_RECEIVED_HOOK != 0 )\r
1112                 {\r
1113                         vApplicationFTPReceivedHook( pxClient->pcFileName, pxClient->ulRecvBytes, pxClient );\r
1114                 }\r
1115                 #endif\r
1116 \r
1117         }\r
1118         if( pxClient->pxReadHandle != NULL )\r
1119         {\r
1120                 ff_fclose( pxClient->pxReadHandle );\r
1121                 pxClient->pxReadHandle = NULL;\r
1122         }\r
1123         /* These two field are only used for logging / file-statistics */\r
1124         pxClient->ulRecvBytes = 0ul;\r
1125         pxClient->xStartTime = 0ul;\r
1126 }\r
1127 /*-----------------------------------------------------------*/\r
1128 \r
1129 /**\r
1130  * Guess the transfer type, given the client requested type.\r
1131  * Actually in unix there is no difference between binary and\r
1132  * ascii mode when we work with file descriptors.\r
1133  * If #type is not recognized as a valid client request, -1 is returned.\r
1134  */\r
1135 static BaseType_t prvGetTransferType( const char *pcType )\r
1136 {\r
1137 BaseType_t xResult = -1;\r
1138 \r
1139         if( pcType != NULL )\r
1140         {\r
1141                 BaseType_t xLength = strlen( pcType );\r
1142                 if( xLength == 0 )\r
1143                 {\r
1144                         return -1;\r
1145                 }\r
1146                 switch( pcType[ 0 ] ) {\r
1147                 case 'I':\r
1148                         xResult = TMODE_BINARY;\r
1149                         break;\r
1150                 case 'A':\r
1151                         xResult = TMODE_ASCII;\r
1152                         break;\r
1153                 case 'L':\r
1154                         if( xLength >= 3 )\r
1155                         {\r
1156                                 if( pcType[ 2 ] == '7' )\r
1157                                 {\r
1158                                         xResult = TMODE_7BITS;\r
1159                                 }\r
1160                                 else if( pcType[ 2 ] == '8' )\r
1161                                 {\r
1162                                         xResult = TMODE_7BITS;\r
1163                                 }\r
1164                         }\r
1165                         break;\r
1166                 }\r
1167         }\r
1168         return xResult;\r
1169 }\r
1170 /*-----------------------------------------------------------*/\r
1171 \r
1172 #if( ipconfigHAS_PRINTF != 0 )\r
1173         #define SIZE_1_GB       ( 1024ul * 1024ul * 1024ul )\r
1174         #define SIZE_1_MB       ( 1024ul * 1024ul )\r
1175         #define SIZE_1_KB       ( 1024ul )\r
1176 \r
1177         static const char *pcMkSize( uint32_t ulAmount, char *pcBuffer, BaseType_t xBufferSize )\r
1178         {\r
1179         uint32_t ulGB, ulMB, ulKB, ulByte;\r
1180 \r
1181                 ulGB = ( ulAmount / SIZE_1_GB );\r
1182                 ulAmount -= ( ulGB * SIZE_1_GB );\r
1183                 ulMB = ( ulAmount / SIZE_1_MB );\r
1184                 ulAmount -= ( ulMB * SIZE_1_MB );\r
1185                 ulKB = ( ulAmount / SIZE_1_KB );\r
1186                 ulAmount -= ( ulKB * SIZE_1_KB );\r
1187                 ulByte = ( ulAmount );\r
1188 \r
1189                 if (ulGB != 0ul )\r
1190                 {\r
1191                         snprintf( pcBuffer, xBufferSize, "%lu.%02lu GB", ulGB, (100 * ulMB) / SIZE_1_KB );\r
1192                 }\r
1193                 else if( ulMB != 0ul )\r
1194                 {\r
1195                         snprintf( pcBuffer, xBufferSize, "%lu.%02lu MB", ulMB, (100 * ulKB) / SIZE_1_KB );\r
1196                 }\r
1197                 else if( ulKB != 0ul )\r
1198                 {\r
1199                         snprintf(pcBuffer, xBufferSize, "%lu.%02lu KB", ulKB, (100 * ulByte) / SIZE_1_KB );\r
1200                 }\r
1201                 else\r
1202                 {\r
1203                         snprintf( pcBuffer, xBufferSize, "%lu bytes", ulByte );\r
1204                 }\r
1205 \r
1206                 return pcBuffer;\r
1207         }\r
1208         /*-----------------------------------------------------------*/\r
1209 #endif /* ipconfigHAS_PRINTF != 0 */\r
1210 \r
1211 #if( ipconfigHAS_PRINTF != 0 )\r
1212         static uint32_t ulGetAverage( uint32_t ulAmount, TickType_t xDeltaMs )\r
1213         {\r
1214         uint32_t ulAverage;\r
1215 \r
1216                 /* Get the average amount of bytes per seconds. Ideally this is\r
1217                 calculated by Multiplying with 1000 and dividing by milliseconds:\r
1218                         ulAverage = ( 1000ul * ulAmount ) / xDeltaMs;\r
1219                 Now get a maximum precision, while avoiding an arithmetic overflow:\r
1220                 */\r
1221                 if( xDeltaMs == 0ul )\r
1222                 {\r
1223                         /* Time is zero, there is no average  */\r
1224                         ulAverage = 0ul;\r
1225                 }\r
1226                 else if( ulAmount >= ( ~0ul / 10ul ) )\r
1227                 {\r
1228                         /* More than 409 MB has been transferred, do not multiply. */\r
1229                         ulAverage = ( ulAmount / ( xDeltaMs / 1000ul ) );\r
1230                 }\r
1231                 else if( ulAmount >= ( ~0ul / 100ul ) )\r
1232                 {\r
1233                         /* Between 409 and 41 MB has been transferred, can multiply by 10. */\r
1234                         ulAverage = ( ( ulAmount * 10ul ) / ( xDeltaMs / 100ul ) );\r
1235                 }\r
1236                 else if( ulAmount >= ( ~0ul / 1000ul ) )\r
1237                 {\r
1238                         /* Between 4.1 MB and 41 has been transferred, can multiply by 100. */\r
1239                         ulAverage = ( ( ulAmount * 100ul ) / ( xDeltaMs / 10ul ) );\r
1240                 }\r
1241                 else\r
1242                 {\r
1243                         /* Less than 4.1 MB: can multiply by 1000. */\r
1244                         ulAverage = ( ( ulAmount * 1000ul ) / xDeltaMs );\r
1245                 }\r
1246 \r
1247                 return ulAverage;\r
1248         }\r
1249         /*-----------------------------------------------------------*/\r
1250 #endif /* ipconfigHAS_PRINTF != 0 */\r
1251 \r
1252 static UBaseType_t prvParsePortData( const char *pcCommand, uint32_t *pulIPAddress )\r
1253 {\r
1254 /*_HT_ Using 'unsigned' here because when sscanf() sees '%u', it expects a pointer to 'unsigned'.\r
1255 Not sure about the sscanf() format for UBaseType_t ? */\r
1256 unsigned h1, h2, h3, h4, p1, p2;\r
1257 char sep;\r
1258 UBaseType_t uxResult;\r
1259 \r
1260         /* Expect PORT h1,h2,h3,h4,p1,p2 */\r
1261         if (sscanf (pcCommand, "%u%c%u%c%u%c%u%c%u%c%u", &h1, &sep, &h2, &sep, &h3, &sep, &h4, &sep, &p1, &sep, &p2) != 11)\r
1262         {\r
1263                 uxResult= 0u;\r
1264         }\r
1265         else\r
1266         {\r
1267                 /* Put in network byte order. */\r
1268                 *pulIPAddress =\r
1269                         ( ( uint32_t ) h1 << 24 ) |\r
1270                         ( ( uint32_t ) h2 << 16 ) |\r
1271                         ( ( uint32_t ) h3 << 8 ) |\r
1272                         ( ( uint32_t ) h4 );\r
1273                 uxResult = ( p1 << 8 ) | p2;\r
1274         }\r
1275         return uxResult;\r
1276 }\r
1277 /*-----------------------------------------------------------*/\r
1278 \r
1279 /*\r
1280 \r
1281  ####                                  #######   #   ###\r
1282 #    #   #                              #   ##   #     #\r
1283 #    #   #                              #    #         #\r
1284 #      ######  ####  ### ##   ####      #   #  ###     #    ####\r
1285  ##      #    #    #  # #  # #    #     #####    #     #   #    #\r
1286    ##    #    #    #  ##   # ######     #   #    #     #   ######\r
1287 #    #   #    #    #  #      #          #        #     #   #\r
1288 #    #   # ## #    #  #      #   ##     #        #     #   #   ##\r
1289  ####     ##   ####  ####     ####     ####    ##### #####  ####\r
1290 \r
1291 */\r
1292 \r
1293 static BaseType_t prvStoreFilePrep( FTPClient_t *pxClient, char *pcFileName )\r
1294 {\r
1295 BaseType_t xResult;\r
1296 FF_FILE *pxNewHandle;\r
1297 size_t uxFileSize = 0ul;\r
1298 int iErrorNo;\r
1299 \r
1300         /* Close previous handle (if any) and reset file transfer parameters. */\r
1301         prvTransferCloseFile( pxClient );\r
1302 \r
1303         xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcFileName );\r
1304 \r
1305         pxNewHandle = NULL;\r
1306 \r
1307         if( pxClient->ulRestartOffset != 0 )\r
1308         {\r
1309         size_t uxOffset = pxClient->ulRestartOffset;\r
1310         int32_t lRc;\r
1311 \r
1312                 pxClient->ulRestartOffset = 0ul; /* Only use 1 time. */\r
1313                 pxNewHandle = ff_fopen( pxClient->pcFileName, "ab" );\r
1314 \r
1315                 if( pxNewHandle != NULL )\r
1316                 {\r
1317                         uxFileSize = pxNewHandle->ulFileSize;\r
1318 \r
1319                         if( uxOffset <= uxFileSize )\r
1320                         {\r
1321                                 lRc = ff_fseek( pxNewHandle, uxOffset, FF_SEEK_SET );\r
1322                         }\r
1323                         else\r
1324                         {\r
1325                                 /* Won't even try to seek after EOF */\r
1326                                 lRc = -pdFREERTOS_ERRNO_EINVAL;\r
1327                         }\r
1328                         if( lRc != 0 )\r
1329                         {\r
1330                         BaseType_t xLength;\r
1331 \r
1332                                 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
1333                                         "450 Seek invalid %u length %u\r\n",\r
1334                                         ( unsigned ) uxOffset, ( unsigned ) uxFileSize );\r
1335 \r
1336                                 /* "Requested file action not taken". */\r
1337                                 prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );\r
1338 \r
1339                                 FreeRTOS_printf( ( "ftp::storeFile: create %s: Seek %u length %u\n",\r
1340                                         pxClient->pcFileName, ( unsigned ) uxOffset, ( unsigned ) uxFileSize ) );\r
1341 \r
1342                                 ff_fclose( pxNewHandle );\r
1343                                 pxNewHandle = NULL;\r
1344                         }\r
1345                 }\r
1346         }\r
1347         else\r
1348         {\r
1349                 pxNewHandle = ff_fopen( pxClient->pcFileName, "wb" );\r
1350         }\r
1351 \r
1352         if( pxNewHandle == NULL )\r
1353         {\r
1354                 iErrorNo = stdioGET_ERRNO();\r
1355                 if( iErrorNo == pdFREERTOS_ERRNO_ENOSPC )\r
1356                 {\r
1357                         prvSendReply( pxClient->xSocket, REPL_552, 0 );\r
1358                 }\r
1359                 else\r
1360                 {\r
1361                         /* "Requested file action not taken". */\r
1362                         prvSendReply( pxClient->xSocket, REPL_450, 0 );\r
1363                 }\r
1364                 FreeRTOS_printf( ( "ftp::storeFile: create %s: %s (errno %d)\n",\r
1365                         pxClient->pcFileName,\r
1366                         ( const char* ) strerror( iErrorNo ), iErrorNo ) );\r
1367 \r
1368                 xResult = pdFALSE;\r
1369         }\r
1370         else\r
1371         {\r
1372                 if( pxClient->bits1.bIsListen )\r
1373                 {\r
1374                         /* True if PASV is used. */\r
1375                         snprintf( pxClient->pcConnectionAck, sizeof( pxClient->pcConnectionAck ),\r
1376                                 "150 Accepted data connection from %%xip:%%u\r\n" );\r
1377                         prvTransferCheck( pxClient );\r
1378                 }\r
1379                 else\r
1380                 {\r
1381                 BaseType_t xLength;\r
1382 \r
1383                         xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "150 Opening BIN connection to store file\r\n" );\r
1384                         prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );\r
1385                         pxClient->pcConnectionAck[ 0 ] = '\0';\r
1386                         prvTransferStart( pxClient ); /* Now active connect. */\r
1387                 }\r
1388 \r
1389                 pxClient->pxWriteHandle = pxNewHandle;\r
1390 \r
1391                 /* To get some statistics about the performance. */\r
1392                 pxClient->xStartTime = xTaskGetTickCount( );\r
1393 \r
1394                 xResult = pdTRUE;\r
1395         }\r
1396 \r
1397         return xResult;\r
1398 }\r
1399 /*-----------------------------------------------------------*/\r
1400 \r
1401 #if( ipconfigFTP_ZERO_COPY_ALIGNED_WRITES == 0 )\r
1402 \r
1403         static BaseType_t prvStoreFileWork( FTPClient_t *pxClient )\r
1404         {\r
1405         BaseType_t xRc, xWritten;\r
1406 \r
1407                 /* Read from the data socket until all has been read or until a negative value\r
1408                 is returned. */\r
1409                 for( ; ; )\r
1410                 {\r
1411                 char *pcBuffer;\r
1412 \r
1413                         /* The "zero-copy" method: */\r
1414                         xRc = FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) &pcBuffer,\r
1415                                 0x20000u, FREERTOS_ZERO_COPY | FREERTOS_MSG_DONTWAIT );\r
1416                         if( xRc <= 0 )\r
1417                         {\r
1418                                 break;\r
1419                         }\r
1420                         pxClient->ulRecvBytes += xRc;\r
1421                         xWritten = ff_fwrite( pcBuffer, 1, xRc, pxClient->pxWriteHandle );\r
1422                         FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) NULL, xRc, 0 );\r
1423                         if( xWritten != xRc )\r
1424                         {\r
1425                                 xRc = -1;\r
1426                                 /* bHadError: a transfer got aborted because of an error. */\r
1427                                 pxClient->bits1.bHadError = pdTRUE_UNSIGNED;\r
1428                                 break;\r
1429                         }\r
1430                 }\r
1431                 return xRc;\r
1432         }\r
1433 \r
1434 #else   /* ipconfigFTP_ZERO_COPY_ALIGNED_WRITES != 0 */\r
1435 \r
1436         #if !defined( ipconfigFTP_PREFERRED_WRITE_SIZE )\r
1437                 /* If you store data on flash, it may be profitable to give 'ipconfigFTP_PREFERRED_WRITE_SIZE'\r
1438                 the same size as the size of the flash' erase blocks, e.g. 4KB */\r
1439                 #define ipconfigFTP_PREFERRED_WRITE_SIZE        512ul\r
1440         #endif\r
1441 \r
1442         static BaseType_t prvStoreFileWork( FTPClient_t *pxClient )\r
1443         {\r
1444         BaseType_t xRc, xWritten;\r
1445 \r
1446                 /* Read from the data socket until all has been read or until a negative\r
1447                 value is returned. */\r
1448                 for( ; ; )\r
1449                 {\r
1450                 char *pcBuffer;\r
1451                 UBaseType_t xStatus;\r
1452 \r
1453                         /* The "zero-copy" method: */\r
1454                         xRc = FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) &pcBuffer,\r
1455                                 0x20000u, FREERTOS_ZERO_COPY | FREERTOS_MSG_DONTWAIT );\r
1456 \r
1457                         if( xRc <= 0 )\r
1458                         {\r
1459                                 /* There are no data or the connection is closed. */\r
1460                                 break;\r
1461                         }\r
1462                         xStatus = FreeRTOS_connstatus( pxClient->xTransferSocket );\r
1463                         if( xStatus != eESTABLISHED )\r
1464                         {\r
1465                                 /* The connection is not established (any more), therefore\r
1466                                 accept any amount of bytes, probably the last few bytes. */\r
1467                         }\r
1468                         else\r
1469                         {\r
1470                                 if( xRc >= ipconfigFTP_PREFERRED_WRITE_SIZE )\r
1471                                 {\r
1472                                         /* More than a sector to write, round down to a multiple of\r
1473                                         PREFERRED_WRITE_SIZE bytes. */\r
1474                                         xRc = ( xRc / ipconfigFTP_PREFERRED_WRITE_SIZE ) * ipconfigFTP_PREFERRED_WRITE_SIZE;\r
1475                                 }\r
1476                                 else\r
1477                                 {\r
1478                                 const StreamBuffer_t *pxBuffer = FreeRTOS_get_rx_buf( pxClient->xTransferSocket );\r
1479                                 size_t uxSpace = pxBuffer->LENGTH - pxBuffer->uxTail;\r
1480 \r
1481                                         if( uxSpace >= ipconfigFTP_PREFERRED_WRITE_SIZE )\r
1482                                         {\r
1483                                                 /* At this moment there are les than PREFERRED_WRITE_SIZE bytes in the RX\r
1484                                                 buffer, but there is space for more. Just return and\r
1485                                                 wait for more. */\r
1486                                                 xRc = 0;\r
1487                                         }\r
1488                                         else\r
1489                                         {\r
1490                                                 /* Now reading beyond the end of the circular buffer,\r
1491                                                 use a normal read. */\r
1492                                                 pcBuffer = pcFILE_BUFFER;\r
1493                                                 xRc = FreeRTOS_recvcount( pxClient->xTransferSocket );\r
1494                                                 xRc = ( xRc / ipconfigFTP_PREFERRED_WRITE_SIZE ) * ipconfigFTP_PREFERRED_WRITE_SIZE;\r
1495                                                 if( xRc > 0 )\r
1496                                                 {\r
1497                                                         xRc = FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) pcBuffer,\r
1498                                                                 sizeof( pcFILE_BUFFER ), FREERTOS_MSG_DONTWAIT );\r
1499                                                 }\r
1500                                         }\r
1501                                 }\r
1502                         }\r
1503                         if( xRc == 0 )\r
1504                         {\r
1505                                 break;\r
1506                         }\r
1507                         pxClient->ulRecvBytes += xRc;\r
1508 \r
1509                         xWritten = ff_fwrite( pcBuffer, 1, xRc, pxClient->pxWriteHandle );\r
1510                         if( pcBuffer != pcFILE_BUFFER )\r
1511                         {\r
1512                                 FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) NULL, xRc, 0 );\r
1513                         }\r
1514                         if( xWritten != xRc )\r
1515                         {\r
1516                                 xRc = -1;\r
1517                                 /* bHadError: a transfer got aborted because of an error. */\r
1518                                 pxClient->bits1.bHadError = pdTRUE_UNSIGNED;\r
1519                                 break;\r
1520                         }\r
1521                 }\r
1522                 return xRc;\r
1523         }\r
1524 \r
1525 #endif /* ipconfigFTP_ZERO_COPY_ALIGNED_WRITES */\r
1526 /*-----------------------------------------------------------*/\r
1527 \r
1528 /*\r
1529 ######                          #                           #######   #   ###\r
1530  #    #          #              #                            #   ##   #     #\r
1531  #    #          #                                           #    #         #\r
1532  #    #  ####  ###### ### ##  ###    ####  #    #  ####      #   #  ###     #    ####\r
1533  ###### #    #   #     # #  #   #   #    # #    # #    #     #####    #     #   #    #\r
1534  #  ##  ######   #     ##   #   #   ###### #    # ######     #   #    #     #   ######\r
1535  #   #  #        #     #        #   #      #    # #          #        #     #   #\r
1536  #    # #   ##   # ##  #        #   #   ##  #  #  #   ##     #        #     #   #   ##\r
1537 ###  ##  ####     ##  ####    #####  ####    ##    ####     ####    ##### #####  ####\r
1538 */\r
1539 static BaseType_t prvRetrieveFilePrep( FTPClient_t *pxClient, char *pcFileName )\r
1540 {\r
1541 BaseType_t xResult = pdTRUE;\r
1542 size_t uxFileSize;\r
1543 \r
1544         /* Close previous handle (if any) and reset file transfer parameters */\r
1545         prvTransferCloseFile( pxClient );\r
1546 \r
1547         xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcFileName );\r
1548 \r
1549         pxClient->pxReadHandle = ff_fopen( pxClient->pcFileName, "rb" );\r
1550         if( pxClient->pxReadHandle == NULL )\r
1551         {\r
1552         int iErrno = stdioGET_ERRNO();\r
1553                 /* "Requested file action not taken". */\r
1554                 prvSendReply( pxClient->xSocket, REPL_450, 0 );\r
1555                 FreeRTOS_printf( ("prvRetrieveFilePrep: open '%s': errno %d: %s\n",\r
1556                         pxClient->pcFileName, iErrno, ( const char * ) strerror( iErrno ) ) );\r
1557                 uxFileSize = 0ul;\r
1558                 xResult = pdFALSE;\r
1559         }\r
1560         else\r
1561         {\r
1562                 uxFileSize = pxClient->pxReadHandle->ulFileSize;\r
1563                 pxClient->uxBytesLeft = uxFileSize;\r
1564                 if( pxClient->ulRestartOffset != 0ul )\r
1565                 {\r
1566                 size_t uxOffset = pxClient->ulRestartOffset;\r
1567                 int32_t iRc;\r
1568 \r
1569                         /* Only use 1 time. */\r
1570                         pxClient->ulRestartOffset = 0;\r
1571 \r
1572                         if( uxOffset < uxFileSize )\r
1573                         {\r
1574                                 iRc = ff_fseek( pxClient->pxReadHandle, uxOffset, FF_SEEK_SET );\r
1575                         }\r
1576                         else\r
1577                         {\r
1578                                 iRc = -pdFREERTOS_ERRNO_EINVAL;\r
1579                         }\r
1580                         if( iRc != 0 )\r
1581                         {\r
1582                         BaseType_t xLength;\r
1583 \r
1584                                 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
1585                                         "450 Seek invalid %u length %u\r\n", ( unsigned ) uxOffset, ( unsigned ) uxFileSize );\r
1586 \r
1587                                 /* "Requested file action not taken". */\r
1588                                 prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );\r
1589 \r
1590                                 FreeRTOS_printf( ( "prvRetrieveFilePrep: create %s: Seek %u length %u\n",\r
1591                                         pxClient->pcFileName, ( unsigned ) uxOffset, ( unsigned ) uxFileSize ) );\r
1592 \r
1593                                 ff_fclose( pxClient->pxReadHandle );\r
1594                                 pxClient->pxReadHandle = NULL;\r
1595                                 xResult = pdFALSE;\r
1596                         }\r
1597                         else\r
1598                         {\r
1599                                 pxClient->uxBytesLeft = uxFileSize - pxClient->ulRestartOffset;\r
1600                         }\r
1601                 }\r
1602         }\r
1603         if( xResult != pdFALSE )\r
1604         {\r
1605                 if( pxClient->bits1.bIsListen != pdFALSE_UNSIGNED )\r
1606                 {\r
1607                         /* True if PASV is used. */\r
1608                         snprintf( pxClient->pcConnectionAck, sizeof( pxClient->pcConnectionAck ),\r
1609                                 "150%cAccepted data connection from %%xip:%%u\r\n%s",\r
1610                                 pxClient->xTransType == TMODE_ASCII ? '-' : ' ',\r
1611                                 pxClient->xTransType == TMODE_ASCII ? "150 NOTE: ASCII mode requested, but binary mode used\r\n" : "" );\r
1612                 } else {\r
1613                 BaseType_t xLength;\r
1614 \r
1615                         xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "150%cOpening data connection to %lxip:%u\r\n%s",\r
1616                                 pxClient->xTransType == TMODE_ASCII ? '-' : ' ',\r
1617                                 pxClient->ulClientIP,\r
1618                                 pxClient->usClientPort,\r
1619                                 pxClient->xTransType == TMODE_ASCII ? "150 NOTE: ASCII mode requested, but binary mode used\r\n" : "" );\r
1620                         prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );\r
1621                         pxClient->pcConnectionAck[ 0 ] = '\0';\r
1622                         prvTransferStart( pxClient );\r
1623                 }\r
1624 \r
1625                 /* Prepare the ACK which will be sent when all data has been sent. */\r
1626                 snprintf( pxClient->pcClientAck, sizeof( pxClient->pcClientAck ), "%s", REPL_226 );\r
1627 \r
1628                 /* To get some statistics about the performance. */\r
1629                 pxClient->xStartTime = xTaskGetTickCount( );\r
1630                 if( uxFileSize == 0ul )\r
1631                 {\r
1632                         FreeRTOS_shutdown( pxClient->xTransferSocket, FREERTOS_SHUT_RDWR );\r
1633                 }\r
1634         }\r
1635 \r
1636         return xResult;\r
1637 }\r
1638 /*-----------------------------------------------------------*/\r
1639 \r
1640 static BaseType_t prvRetrieveFileWork( FTPClient_t *pxClient )\r
1641 {\r
1642 size_t uxSpace;\r
1643 size_t uxCount, uxItemsRead;\r
1644 BaseType_t xRc = 0;\r
1645 BaseType_t xSetEvent = pdFALSE;\r
1646 \r
1647         do\r
1648         {\r
1649         #if( ipconfigFTP_TX_ZERO_COPY != 0 )\r
1650                 char *pcBuffer;\r
1651                 BaseType_t xBufferLength;\r
1652         #endif /* ipconfigFTP_TX_ZERO_COPY */\r
1653 \r
1654                 /* Take the lesser of the two: tx_space (number of bytes that can be\r
1655                 queued for transmission) and uxBytesLeft (the number of bytes left to\r
1656                 read from the file) */\r
1657                 uxSpace = FreeRTOS_tx_space( pxClient->xTransferSocket );\r
1658 \r
1659                 if( uxSpace == 0 )\r
1660                 {\r
1661                         FreeRTOS_FD_SET( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE | eSELECT_EXCEPT );\r
1662                         xRc = FreeRTOS_select( pxClient->pxParent->xSocketSet, 200 );\r
1663                         uxSpace = FreeRTOS_tx_space( pxClient->xTransferSocket );\r
1664                 }\r
1665 \r
1666                 uxCount = FreeRTOS_min_uint32( pxClient->uxBytesLeft, uxSpace );\r
1667 \r
1668                 if( uxCount == 0 )\r
1669                 {\r
1670                         break;\r
1671                 }\r
1672 \r
1673                 #if( ipconfigFTP_TX_ZERO_COPY == 0 )\r
1674                 {\r
1675                         if( uxCount > sizeof( pcFILE_BUFFER ) )\r
1676                         {\r
1677                                 uxCount = sizeof( pcFILE_BUFFER );\r
1678                         }\r
1679                         uxItemsRead = ff_fread( pcFILE_BUFFER, 1, uxCount, pxClient->pxReadHandle );\r
1680                         if( uxItemsRead != uxCount )\r
1681                         {\r
1682                                 FreeRTOS_printf( ( "prvRetrieveFileWork: Got %u Expected %u\n", ( unsigned )uxItemsRead, ( unsigned ) uxCount ) );\r
1683                                 xRc = FreeRTOS_shutdown( pxClient->xTransferSocket, FREERTOS_SHUT_RDWR );\r
1684                                 pxClient->uxBytesLeft = 0u;\r
1685                                 break;\r
1686                         }\r
1687                         pxClient->uxBytesLeft -= uxCount;\r
1688 \r
1689                         if( pxClient->uxBytesLeft == 0u )\r
1690                         {\r
1691                         BaseType_t xTrueValue = 1;\r
1692 \r
1693                                 FreeRTOS_setsockopt( pxClient->xTransferSocket, 0, FREERTOS_SO_CLOSE_AFTER_SEND, ( void * ) &xTrueValue, sizeof( xTrueValue ) );\r
1694                         }\r
1695 \r
1696                         xRc = FreeRTOS_send( pxClient->xTransferSocket, pcFILE_BUFFER, uxCount, 0 );\r
1697                 }\r
1698                 #else /* ipconfigFTP_TX_ZERO_COPY != 0 */\r
1699                 {\r
1700                         /* Use zero-copy transmission:\r
1701                         FreeRTOS_get_tx_head() returns a direct pointer to the TX stream and\r
1702                         set xBufferLength to know how much space there is left. */\r
1703                         pcBuffer = ( char * )FreeRTOS_get_tx_head( pxClient->xTransferSocket, &xBufferLength );\r
1704                         if( ( pcBuffer != NULL ) && ( xBufferLength >= 512 ) )\r
1705                         {\r
1706                                 /* Will read disk data directly to the TX stream of the socket. */\r
1707                                 uxCount = FreeRTOS_min_uint32( uxCount, ( uint32_t )xBufferLength );\r
1708                                 if( uxCount > ( size_t ) 0x40000u )\r
1709                                 {\r
1710                                         uxCount = ( size_t ) 0x40000u;\r
1711                                 }\r
1712                         }\r
1713                         else\r
1714                         {\r
1715                                 /* Use the normal file i/o buffer. */\r
1716                                 pcBuffer = pcFILE_BUFFER;\r
1717                                 if( uxCount > sizeof( pcFILE_BUFFER ) )\r
1718                                 {\r
1719                                         uxCount = sizeof( pcFILE_BUFFER );\r
1720                                 }\r
1721                         }\r
1722 \r
1723                         if ( pxClient->uxBytesLeft >= 1024u )\r
1724                         {\r
1725                                 uxCount &= ~( ( size_t ) 512u - 1u );\r
1726                         }\r
1727 \r
1728                         if( uxCount <= 0u )\r
1729                         {\r
1730                                 /* Nothing to send after rounding down to a multiple of a sector size. */\r
1731                                 break;\r
1732                         }\r
1733 \r
1734                         uxItemsRead = ff_fread( pcBuffer, 1, uxCount, pxClient->pxReadHandle );\r
1735 \r
1736                         if( uxCount != uxItemsRead )\r
1737                         {\r
1738                                 FreeRTOS_printf( ( "prvRetrieveFileWork: Got %u Expected %u\n", ( unsigned )uxItemsRead, ( unsigned )uxCount ) );\r
1739                                 xRc = FreeRTOS_shutdown( pxClient->xTransferSocket, FREERTOS_SHUT_RDWR );\r
1740                                 pxClient->uxBytesLeft = 0u;\r
1741                                 break;\r
1742                         }\r
1743                         pxClient->uxBytesLeft -= uxCount;\r
1744 \r
1745                         if( pxClient->uxBytesLeft == 0u )\r
1746                         {\r
1747                         BaseType_t xTrueValue = 1;\r
1748 \r
1749                                 FreeRTOS_setsockopt( pxClient->xTransferSocket, 0, FREERTOS_SO_CLOSE_AFTER_SEND, ( void * ) &xTrueValue, sizeof( xTrueValue ) );\r
1750                         }\r
1751                         if( pcBuffer != pcFILE_BUFFER )\r
1752                         {\r
1753                                 pcBuffer = NULL;\r
1754                         }\r
1755                         xRc = FreeRTOS_send( pxClient->xTransferSocket, pcBuffer, uxCount, 0 );\r
1756                 }\r
1757                 #endif /* ipconfigFTP_TX_ZERO_COPY */\r
1758 \r
1759                 if( xRc < 0 )\r
1760                 {\r
1761                         break;\r
1762                 }\r
1763 \r
1764                 pxClient->ulRecvBytes += xRc;\r
1765                 if( pxClient->uxBytesLeft == 0u )\r
1766                 {\r
1767                         break;\r
1768                 }\r
1769         } while( uxCount > 0u );\r
1770 \r
1771         if( xRc < 0 )\r
1772         {\r
1773                 FreeRTOS_printf( ( "prvRetrieveFileWork: already disconnected\n" ) );\r
1774         }\r
1775         else if( pxClient->uxBytesLeft <= 0u )\r
1776         {\r
1777         BaseType_t x;\r
1778 \r
1779                 for( x = 0; x < 5; x++ )\r
1780                 {\r
1781                         xRc = FreeRTOS_recv( pxClient->xTransferSocket, pcFILE_BUFFER, sizeof( pcFILE_BUFFER ), 0 );\r
1782                         if( xRc < 0 )\r
1783                         {\r
1784                                 break;\r
1785                         }\r
1786                 }\r
1787 //              FreeRTOS_printf( ( "prvRetrieveFileWork: %s all sent: xRc %ld\n", pxClient->pcFileName, xRc ) );\r
1788         }\r
1789         else\r
1790         {\r
1791                 FreeRTOS_FD_SET( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );\r
1792                 xSetEvent = pdTRUE;\r
1793         }\r
1794         if( xSetEvent == pdFALSE )\r
1795         {\r
1796                 FreeRTOS_FD_CLR( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );\r
1797         }\r
1798         return xRc;\r
1799 }\r
1800 /*-----------------------------------------------------------*/\r
1801 \r
1802 /*\r
1803 ###     #####  ####  #####\r
1804  #        #   #    # # # #\r
1805  #        #   #    #   #\r
1806  #        #   #        #\r
1807  #        #    ##      #\r
1808  #    #   #      ##    #\r
1809  #    #   #   #    #   #\r
1810  #    #   #   #    #   #\r
1811 ####### #####  ####   ####\r
1812 */\r
1813 /* Prepare sending a directory LIST */\r
1814 static BaseType_t prvListSendPrep( FTPClient_t *pxClient )\r
1815 {\r
1816 BaseType_t xFindResult;\r
1817 int iErrorNo;\r
1818 \r
1819         if( pxClient->bits1.bIsListen != pdFALSE_UNSIGNED )\r
1820         {\r
1821                 /* True if PASV is used */\r
1822                 snprintf( pxClient->pcConnectionAck, sizeof( pxClient->pcConnectionAck ),\r
1823                         "150 Accepted data connection from %%xip:%%u\r\n" );\r
1824         }\r
1825         else\r
1826         {\r
1827         BaseType_t xLength;\r
1828 \r
1829                 /* Here the FTP server is supposed to connect() */\r
1830                 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
1831                         "150 Opening ASCII mode data connection to for /bin/ls \r\n" );\r
1832 \r
1833                 prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );\r
1834                 /* Clear the current connection acknowledge message */\r
1835                 pxClient->pcConnectionAck[ 0 ] = '\0';\r
1836                 prvTransferStart( pxClient );\r
1837         }\r
1838 \r
1839         pxClient->xDirCount = 0;\r
1840         xMakeAbsolute( pxClient, pcNEW_DIR, sizeof( pcNEW_DIR ), pxClient->pcCurrentDir );\r
1841 \r
1842         xFindResult = ff_findfirst( pcNEW_DIR, &pxClient->xFindData );\r
1843 \r
1844         pxClient->bits1.bDirHasEntry = ( xFindResult >= 0 );\r
1845 \r
1846         iErrorNo = stdioGET_ERRNO();\r
1847         if( ( xFindResult < 0 ) && ( iErrorNo == pdFREERTOS_ERRNO_ENMFILE ) )\r
1848         {\r
1849                 FreeRTOS_printf( ("prvListSendPrep: Empty directory? (%s)\n", pxClient->pcCurrentDir ) );\r
1850                 prvSendReply( pxClient->xTransferSocket, "total 0\r\n", 0 );\r
1851                 pxClient->xDirCount++;\r
1852         }\r
1853         else if( xFindResult < 0 )\r
1854         {\r
1855                 FreeRTOS_printf( ( "prvListSendPrep: rc = %ld iErrorNo = %d\n", xFindResult, iErrorNo ) );\r
1856                 prvSendReply( pxClient->xSocket, REPL_451, 0 );\r
1857         }\r
1858         pxClient->pcClientAck[ 0 ] = '\0';\r
1859 \r
1860         return pxClient->xDirCount;\r
1861 }\r
1862 /*-----------------------------------------------------------*/\r
1863 \r
1864 #define MAX_DIR_LIST_ENTRY_SIZE         256\r
1865 \r
1866 static BaseType_t prvListSendWork( FTPClient_t *pxClient )\r
1867 {\r
1868 BaseType_t xTxSpace;\r
1869 \r
1870         while( pxClient->bits1.bClientConnected != pdFALSE_UNSIGNED )\r
1871         {\r
1872         char *pcWritePtr = pcCOMMAND_BUFFER;\r
1873         BaseType_t xWriteLength;\r
1874 \r
1875                 xTxSpace = FreeRTOS_tx_space( pxClient->xTransferSocket );\r
1876 \r
1877                 if( xTxSpace > ( BaseType_t ) sizeof( pcCOMMAND_BUFFER ) )\r
1878                 {\r
1879                         xTxSpace = sizeof( pcCOMMAND_BUFFER );\r
1880                 }\r
1881 \r
1882                 while( ( xTxSpace >= MAX_DIR_LIST_ENTRY_SIZE ) && ( pxClient->bits1.bDirHasEntry != pdFALSE_UNSIGNED ) )\r
1883                 {\r
1884                 BaseType_t xLength, xEndOfDir;\r
1885                 int32_t iRc;\r
1886                 int iErrorNo;\r
1887 \r
1888                         xLength = prvGetFileInfoStat( &( pxClient->xFindData.xDirectoryEntry ), pcWritePtr, xTxSpace );\r
1889 \r
1890                         pxClient->xDirCount++;\r
1891                         pcWritePtr += xLength;\r
1892                         xTxSpace -= xLength;\r
1893 \r
1894                         iRc = ff_findnext( &pxClient->xFindData );\r
1895                         iErrorNo = stdioGET_ERRNO();\r
1896 \r
1897                         xEndOfDir = ( iRc < 0 ) && ( iErrorNo == pdFREERTOS_ERRNO_ENMFILE );\r
1898 \r
1899                         pxClient->bits1.bDirHasEntry = ( xEndOfDir == pdFALSE ) && ( iRc >= 0 );\r
1900 \r
1901                         if( ( iRc < 0 ) && ( xEndOfDir == pdFALSE ) )\r
1902                         {\r
1903                                 FreeRTOS_printf( ("prvListSendWork: %s (rc %08x)\n",\r
1904                                         ( const char * ) strerror( iErrorNo ),\r
1905                                         ( unsigned )iRc ) );\r
1906                         }\r
1907                 }\r
1908                 xWriteLength = ( BaseType_t ) ( pcWritePtr - pcCOMMAND_BUFFER );\r
1909 \r
1910                 if( xWriteLength == 0 )\r
1911                 {\r
1912                         break;\r
1913                 }\r
1914 \r
1915                 if( pxClient->bits1.bDirHasEntry == pdFALSE_UNSIGNED )\r
1916                 {\r
1917                 uint32_t ulTotalCount;\r
1918                 uint32_t ulFreeCount;\r
1919                 uint32_t ulPercentage;\r
1920 \r
1921                         ulTotalCount = 1;\r
1922                         ulFreeCount = ff_diskfree( pxClient->pcCurrentDir, &ulTotalCount );\r
1923                         ulPercentage = ( uint32_t ) ( ( 100ULL * ulFreeCount + ulTotalCount / 2 ) / ulTotalCount );\r
1924 \r
1925                         /* Prepare the ACK which will be sent when all data has been sent. */\r
1926                         snprintf( pxClient->pcClientAck, sizeof( pxClient->pcClientAck ),\r
1927                                 "226-Options: -l\r\n"\r
1928                                 "226-%ld matches total\r\n"\r
1929                                 "226 Total %lu KB (%lu %% free)\r\n",\r
1930                                 pxClient->xDirCount, ulTotalCount /1024, ulPercentage );\r
1931                 }\r
1932 \r
1933                 if( xWriteLength )\r
1934                 {\r
1935                         if( pxClient->bits1.bDirHasEntry == pdFALSE_UNSIGNED )\r
1936                         {\r
1937                         BaseType_t xTrueValue = 1;\r
1938 \r
1939                                 FreeRTOS_setsockopt( pxClient->xTransferSocket, 0, FREERTOS_SO_CLOSE_AFTER_SEND, ( void * ) &xTrueValue, sizeof( xTrueValue ) );\r
1940                         }\r
1941 \r
1942                         prvSendReply( pxClient->xTransferSocket, pcCOMMAND_BUFFER, xWriteLength );\r
1943                 }\r
1944 \r
1945                 if( pxClient->bits1.bDirHasEntry == pdFALSE_UNSIGNED )\r
1946                 {\r
1947                         prvSendReply( pxClient->xSocket, pxClient->pcClientAck, 0 );\r
1948                         break;\r
1949                 }\r
1950 \r
1951         }       /* while( pxClient->bits1.bClientConnected )  */\r
1952 \r
1953         return 0;\r
1954 }\r
1955 /*-----------------------------------------------------------*/\r
1956 \r
1957 static const char *pcMonthAbbrev( BaseType_t xMonth )\r
1958 {\r
1959 static const char pcMonthList[] = "JanFebMarAprMayJunJulAugSepOctNovDec";\r
1960 \r
1961         if( xMonth < 1 || xMonth > 12 )\r
1962                 xMonth = 12;\r
1963 \r
1964         return pcMonthList + 3 * ( xMonth - 1 );\r
1965 };\r
1966 /*-----------------------------------------------------------*/\r
1967 \r
1968 static BaseType_t prvGetFileInfoStat( FF_DirEnt_t *pxEntry, char *pcLine, BaseType_t xMaxLength )\r
1969 {\r
1970         char date[ 16 ];\r
1971         char mode[ 11 ] = "----------";\r
1972         BaseType_t st_nlink = 1;\r
1973         const char user[ 9 ] = "freertos";\r
1974         const char group[ 8 ] = "plusfat";\r
1975 \r
1976 /*\r
1977  *      Creates a unix-style listing, understood by most FTP clients:\r
1978  *\r
1979  * -rw-rw-r--   1 freertos FreeRTOS+FAT 10564588 Sep 01 00:17 03.  Metaharmoniks - Star (Instrumental).mp3\r
1980  * -rw-rw-r--   1 freertos FreeRTOS+FAT 19087839 Sep 01 00:17 04.  Simon Le Grec - Dimitri (Wherever U Are) (Cosmos Mix).mp3\r
1981  * -rw-rw-r--   1 freertos FreeRTOS+FAT 11100621 Sep 01 00:16 05.  D-Chill - Mistake (feat. Katy Blue).mp3\r
1982  */\r
1983 \r
1984         #if ( ffconfigTIME_SUPPORT == 1 )\r
1985                 const FF_SystemTime_t *pxCreateTime = &( pxEntry->xCreateTime );\r
1986         #else\r
1987         #warning Do not use this.\r
1988                 FF_SystemTime_t xCreateTime;\r
1989                 const FF_SystemTime_t *pxCreateTime = &xCreateTime;\r
1990         #endif\r
1991         size_t ulSize = ( size_t )pxEntry->ulFileSize;\r
1992         const char *pcFileName = pxEntry->pcFileName;\r
1993 \r
1994         mode[ 0 ] = ( ( pxEntry->ucAttrib & FF_FAT_ATTR_DIR ) != 0 ) ? 'd' : '-';\r
1995         #if( ffconfigDEV_SUPPORT != 0 )\r
1996         {\r
1997                 if( ( pxEntry->ucAttrib & FF_FAT_ATTR_DIR ) == 0 )\r
1998                 {\r
1999                         switch( pxEntry->ucIsDeviceDir )\r
2000                         {\r
2001                         case FF_DEV_CHAR_DEV:\r
2002                                 mode[ 0 ] = 'c';\r
2003                                 break;\r
2004                         case FF_DEV_BLOCK_DEV:\r
2005                                 mode[ 0 ] = 'b';\r
2006                                 break;\r
2007                         }\r
2008                 }\r
2009         }\r
2010         #endif /* ffconfigDEV_SUPPORT != 0 */\r
2011 \r
2012         mode[ 1 ] = 'r';        /* Owner. */\r
2013         mode[ 2 ] = ( ( pxEntry->ucAttrib & FF_FAT_ATTR_READONLY ) != 0 ) ? '-' : 'w';\r
2014         mode[ 3 ] = '-';        /* x for executable. */\r
2015 \r
2016         mode[ 4 ] = 'r';        /* group. */\r
2017         mode[ 5 ] = ( ( pxEntry->ucAttrib & FF_FAT_ATTR_READONLY ) != 0 ) ? '-' : 'w';\r
2018         mode[ 6 ] = '-';        /* x for executable. */\r
2019 \r
2020         mode[ 7 ] = 'r';        /* world. */\r
2021         mode[ 8 ] = '-';\r
2022         mode[ 9 ] = '-';        /* x for executable. */\r
2023 \r
2024         if( pxCreateTime->Month && pxCreateTime->Day )\r
2025         {\r
2026                 snprintf( date, sizeof( date ), "%-3.3s %02d %02d:%02d",\r
2027                         pcMonthAbbrev( pxCreateTime->Month ),\r
2028                         pxCreateTime->Day,\r
2029                         pxCreateTime->Hour,\r
2030                         pxCreateTime->Minute );\r
2031         }\r
2032         else\r
2033         {\r
2034                 snprintf (date, sizeof( date ), "Jan 01 1970");\r
2035         }\r
2036         return snprintf( pcLine, xMaxLength, "%s %3ld %-4s %-4s %8d %12s %s\r\n",\r
2037                 mode, st_nlink, user, group, ( int ) ulSize, date, pcFileName );\r
2038 }\r
2039 /*-----------------------------------------------------------*/\r
2040 \r
2041 /*\r
2042   ####  #     # #####\r
2043  #    # #     #  #   #\r
2044 #     # #     #  #    #\r
2045 #       #  #  #  #    #\r
2046 #       #  #  #  #    #\r
2047 #       #  #  #  #    #\r
2048 #     #  ## ##   #    #\r
2049  #    #  ## ##   #   #\r
2050   ####   ## ##  #####\r
2051 */\r
2052 static BaseType_t prvChangeDir( FTPClient_t *pxClient, char *pcDirectory )\r
2053 {\r
2054 BaseType_t xResult;\r
2055 BaseType_t xIsRootDir, xLength, xValid;\r
2056 BaseType_t xIsDotDir = 0;\r
2057 \r
2058         if( pcDirectory[ 0 ] == '.' )\r
2059         {\r
2060                 if( ( pcDirectory[ 1 ] == '.' ) &&\r
2061                         ( pcDirectory[ 2 ] == '\0' ) )\r
2062                 {\r
2063                         xIsDotDir = 2;\r
2064                 }\r
2065                 else if( pcDirectory[ 1 ] == '\0' )\r
2066                 {\r
2067                         xIsDotDir = 1;\r
2068                 }\r
2069         }\r
2070 \r
2071         if( xIsDotDir != 0 )\r
2072         {\r
2073                 strcpy( pcFILE_BUFFER, pxClient->pcCurrentDir );\r
2074 \r
2075                 if( pcDirectory[ 1 ] == '.' )\r
2076                 {\r
2077                         char *p = strrchr( pcFILE_BUFFER, '/' );\r
2078                         if( p != NULL )\r
2079                         {\r
2080                                 if( p == pcFILE_BUFFER )\r
2081                                 {\r
2082                                         p[ 1 ] = '\0';\r
2083                                 }\r
2084                                 else\r
2085                                 {\r
2086                                         p[ 0 ] = '\0';\r
2087                                 }\r
2088                         }\r
2089                 }\r
2090         }\r
2091         else\r
2092         {\r
2093                 if(pcDirectory[ 0 ] != '/' )\r
2094                 {\r
2095                 BaseType_t xCurLength;\r
2096 \r
2097                         xCurLength = strlen( pxClient->pcCurrentDir );\r
2098                         snprintf( pcFILE_BUFFER, sizeof( pcFILE_BUFFER ), "%s%s%s",\r
2099                                 pxClient->pcCurrentDir,\r
2100                                 pxClient->pcCurrentDir[ xCurLength - 1 ] == '/' ? "" : "/",\r
2101                                 pcDirectory );\r
2102                 }\r
2103                 else\r
2104                 {\r
2105                         snprintf( pcFILE_BUFFER, sizeof( pcFILE_BUFFER ), "%s", pcDirectory );\r
2106                 }\r
2107         }\r
2108 \r
2109         xIsRootDir = ( pcFILE_BUFFER[ 0 ] == '/' ) && ( pcFILE_BUFFER[ 1 ] == '\0' );\r
2110         xMakeAbsolute( pxClient, pcNEW_DIR, sizeof( pcNEW_DIR ), pcFILE_BUFFER );\r
2111 \r
2112         if( ( ( xIsRootDir == pdFALSE ) || ( FF_FS_Count() == 0 ) ) &&  ( ff_finddir( pcNEW_DIR ) == pdFALSE ) )\r
2113         {\r
2114                 xValid = pdFALSE;\r
2115         }\r
2116         else\r
2117         {\r
2118                 xValid = pdTRUE;\r
2119         }\r
2120 \r
2121         if( xValid == pdFALSE )\r
2122         {\r
2123                 /* Get the directory cluster, if it exists. */\r
2124                 FreeRTOS_printf( ("FTP: chdir \"%s\": No such dir\n", pcNEW_DIR ) );\r
2125                 //#define REPL_550 "550 Requested action not taken.\r\n"\r
2126                 //550 /home/hein/arch/h8300: No such file or directory\r
2127                 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
2128                         "550 %s: No such file or directory\r\n",\r
2129                         pcNEW_DIR );\r
2130                 prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );\r
2131                 xResult = pdFALSE;\r
2132         }\r
2133         else\r
2134         {\r
2135                 memcpy( pxClient->pcCurrentDir, pcNEW_DIR, sizeof( pxClient->pcCurrentDir ) );\r
2136 \r
2137                 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "250 Changed to %s\r\n", pcNEW_DIR );\r
2138                 prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );\r
2139                 xResult = pdTRUE;\r
2140         }\r
2141 \r
2142         return xResult;\r
2143 }\r
2144 /*-----------------------------------------------------------*/\r
2145 \r
2146 /*\r
2147 ######  ##    # ####### ######\r
2148  #    # ##    #  #   ##  #    #\r
2149  #    # ##    #  #    #  #    #\r
2150  #    # ###   #  #   #   #    #\r
2151  ###### # ##  #  #####   ######\r
2152  #  ##  #  ## #  #   #   #  ##\r
2153  #   #  #   ###  #       #   #\r
2154  #    # #    ##  #       #    #\r
2155 ###  ## #    ## ####    ###  ##\r
2156 */\r
2157 static BaseType_t prvRenameFrom( FTPClient_t *pxClient, const char *pcFileName )\r
2158 {\r
2159 const char *myReply;\r
2160 FF_FILE *fh;\r
2161 \r
2162         xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcFileName );\r
2163 \r
2164         myReply = NULL;\r
2165 \r
2166         fh = ff_fopen( pxClient->pcFileName, "rb" );\r
2167 \r
2168         if( fh != NULL )\r
2169         {\r
2170                 ff_fclose( fh );\r
2171                 /* REPL_350; "350 Requested file action pending further information." */\r
2172                 snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
2173                         "350 Rename '%s' ...\r\n", pxClient->pcFileName );\r
2174                 myReply = pcCOMMAND_BUFFER;\r
2175                 pxClient->bits.bInRename = pdTRUE_UNSIGNED;\r
2176         }\r
2177         else if( stdioGET_ERRNO() == pdFREERTOS_ERRNO_EISDIR )\r
2178         {\r
2179                 snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
2180                         "350 Rename directory '%s' ...\r\n", pxClient->pcFileName );\r
2181                 myReply = pcCOMMAND_BUFFER;\r
2182                 pxClient->bits.bInRename = pdTRUE_UNSIGNED;\r
2183         }\r
2184         else\r
2185         {\r
2186                 FreeRTOS_printf( ("ftp::renameFrom[%s]\n%s\n", pxClient->pcFileName, strerror( stdioGET_ERRNO() ) ) );\r
2187                 myReply = REPL_451;             /* "451 Requested action aborted. Local error in processing." */\r
2188         }\r
2189         if( myReply )\r
2190         {\r
2191                 prvSendReply( pxClient->xSocket, myReply, 0 );\r
2192         }\r
2193 \r
2194         return pdTRUE;\r
2195 }\r
2196 /*-----------------------------------------------------------*/\r
2197 \r
2198 /*\r
2199 ######  ##    # #####   ###\r
2200  #    # ##    # # # #  ## ##\r
2201  #    # ##    #   #   ##   ##\r
2202  #    # ###   #   #   #     #\r
2203  ###### # ##  #   #   #     #\r
2204  #  ##  #  ## #   #   #     #\r
2205  #   #  #   ###   #   ##   ##\r
2206  #    # #    ##   #    ## ##\r
2207 ###  ## #    ##  ####   ###\r
2208 */\r
2209 static BaseType_t prvRenameTo( FTPClient_t *pxClient, const char *pcFileName )\r
2210 {\r
2211 const char *myReply = NULL;\r
2212 int iResult;\r
2213 \r
2214         xMakeAbsolute( pxClient, pcNEW_DIR, sizeof( pcNEW_DIR ), pcFileName );\r
2215 \r
2216         /* FreeRTOS+FAT rename has an extra parameter: "remove target if already\r
2217         exists". */\r
2218         iResult = ff_rename( pxClient->pcFileName, pcNEW_DIR, pdFALSE );\r
2219 \r
2220         if( iResult < 0 )\r
2221         {\r
2222                 iResult = stdioGET_ERRNO();\r
2223         }\r
2224         else\r
2225         {\r
2226                 iResult = 0;\r
2227         }\r
2228 \r
2229         switch( iResult )\r
2230         {\r
2231         case 0:\r
2232                 FreeRTOS_printf( ( "ftp::renameTo[%s,%s]: Ok\n", pxClient->pcFileName, pcNEW_DIR ) );\r
2233                 snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
2234                         "250 Rename successful to '%s'\r\n", pcNEW_DIR );\r
2235                 myReply = pcCOMMAND_BUFFER;\r
2236                 break;\r
2237         case pdFREERTOS_ERRNO_EEXIST:\r
2238                 /* the destination file already exists.\r
2239                 "450 Requested file action not taken.\r\n"*/\r
2240                 snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
2241                         "450 Already exists '%s'\r\n", pcNEW_DIR );\r
2242                 myReply = pcCOMMAND_BUFFER;\r
2243                 break;\r
2244         case pdFREERTOS_ERRNO_EIO:      /* FF_ERR_FILE_COULD_NOT_CREATE_DIRENT */\r
2245                 /* if dirent creation failed (fatal error!).\r
2246                 "553 Requested action not taken.\r\n" */\r
2247                 FreeRTOS_printf( ("ftp::renameTo[%s,%s]: Error creating DirEnt\n",\r
2248                         pxClient->pcFileName, pcNEW_DIR ) );\r
2249                 myReply = REPL_553;\r
2250                 break;\r
2251         case pdFREERTOS_ERRNO_ENXIO:\r
2252         case pdFREERTOS_ERRNO_ENOENT:\r
2253                 /* if the source file was not found.\r
2254                 "450 Requested file action not taken.\r\n" */\r
2255                 snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
2256                         "450 No such file '%s'\r\n", pxClient->pcFileName );\r
2257                 myReply = pcCOMMAND_BUFFER;\r
2258                 break;\r
2259         default:\r
2260                 FreeRTOS_printf( ("ftp::renameTo[%s,%s]: %s\n", pxClient->pcFileName, pcNEW_DIR,\r
2261                         (const char*)strerror( stdioGET_ERRNO() ) ) );\r
2262                 myReply = REPL_451;     /* "451 Requested action aborted. Local error in processing." */\r
2263                 break;\r
2264         }\r
2265         prvSendReply( pxClient->xSocket, myReply, 0 );\r
2266 \r
2267         return pdTRUE;\r
2268 }\r
2269 /*-----------------------------------------------------------*/\r
2270 \r
2271 /*\r
2272  ####    #\r
2273 #    #   #     #\r
2274 #    #         #\r
2275 #      ###   ######  ####\r
2276  ##      #     #    #    #\r
2277    ##    #     #    ######\r
2278 #    #   #     #    #\r
2279 #    #   #     # ## #   ##\r
2280  ####  #####    ##   ####\r
2281 */\r
2282 static BaseType_t prvSiteCmd( FTPClient_t *pxClient, char *pcRestCommand )\r
2283 {\r
2284         ( void ) pxClient;\r
2285         ( void ) pcRestCommand;\r
2286 \r
2287         return 0;\r
2288 }\r
2289 /*-----------------------------------------------------------*/\r
2290 \r
2291 /*\r
2292 #####          ###\r
2293  #   #           #            #\r
2294  #    #          #            #\r
2295  #    #  ####    #    ####  ######  ####\r
2296  #    # #    #   #   #    #   #    #    #\r
2297  #    # ######   #   ######   #    ######\r
2298  #    # #        #   #        #    #\r
2299  #   #  #   ##   #   #   ##   # ## #   ##\r
2300 #####    ####  #####  ####     ##   ####\r
2301 */\r
2302 static BaseType_t prvDeleteFile( FTPClient_t *pxClient, char *pcFileName )\r
2303 {\r
2304 BaseType_t xResult, xLength;\r
2305 int32_t iRc;\r
2306 int iErrorNo;\r
2307 \r
2308         /* DELE: Delete a file. */\r
2309         xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcFileName );\r
2310 \r
2311         iRc = ff_remove( pxClient->pcFileName );\r
2312 \r
2313         if (iRc >= 0 )\r
2314         {\r
2315                 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
2316                         "250 File \"%s\" removed\r\n", pxClient->pcFileName );\r
2317                 xResult = pdTRUE;\r
2318         }\r
2319         else\r
2320         {\r
2321                 const char *errMsg = "other error";\r
2322 \r
2323                 iErrorNo = stdioGET_ERRNO();\r
2324                 switch( iErrorNo )\r
2325                 {                                                                                                                                               /*_RB_ What do these negative numbers relate to? */\r
2326                         case pdFREERTOS_ERRNO_ENOENT:   errMsg = "No such file"; break;         /* -31  File was not found. */\r
2327                         case pdFREERTOS_ERRNO_EALREADY: errMsg = "File still open"; break;      /* -30  File is in use. */\r
2328                         case pdFREERTOS_ERRNO_EISDIR:   errMsg = "Is a dir"; break;                     /* -32  Tried to FF_Open() a Directory. */\r
2329                         case pdFREERTOS_ERRNO_EROFS:    errMsg = "Read-only"; break;            /* -33  Tried to FF_Open() a file marked read only. */\r
2330                         case pdFREERTOS_ERRNO_ENOTDIR:  errMsg = "Invalid path"; break;         /* -34  The path of the file was not found. */\r
2331                 }\r
2332                 FreeRTOS_printf( ( "ftp::delFile: '%s' because %s\n",\r
2333                         pxClient->pcFileName, strerror( iErrorNo ) ) );\r
2334 \r
2335                 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
2336                         "521-\"%s\" %s;\r\n"\r
2337                         "521 taking no action\r\n",\r
2338                         pxClient->pcFileName, errMsg );\r
2339 \r
2340                 xResult = pdFALSE;\r
2341         }\r
2342 \r
2343         prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );\r
2344 \r
2345         return xResult;\r
2346 }\r
2347 /*-----------------------------------------------------------*/\r
2348 \r
2349 /*\r
2350  ####    #                       #####\r
2351 #    #   #                        #   #            #\r
2352 #    #                            #    #           #\r
2353 #      ###   ######  ####         #    #  ####   ######  ####\r
2354  ##      #   #    # #    #        #    #      #    #    #    #\r
2355    ##    #       #  ######        #    #  #####    #    ######\r
2356 #    #   #     #    #             #    # #    #    #    #\r
2357 #    #   #    #     #   ##        #   #  #    #    # ## #   ##\r
2358  ####  ##### ######  ####        #####    ### ##    ##   ####\r
2359 */\r
2360 static BaseType_t prvSizeDateFile( FTPClient_t *pxClient, char *pcFileName, BaseType_t xSendDate )\r
2361 {\r
2362 BaseType_t xResult = pdFALSE;\r
2363 char *pcPtr;\r
2364 \r
2365         /* SIZE: get the size of a file (xSendDate = 0)\r
2366         MDTM: get data and time properties (xSendDate = 1) */\r
2367         xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcFileName );\r
2368 \r
2369         pcPtr = strrchr( pxClient->pcFileName, '/' );\r
2370 \r
2371         if( ( pcPtr != NULL ) && ( pcPtr[ 1 ] != '\0' ) )\r
2372         {\r
2373                 FF_Stat_t xStatBuf;\r
2374                 int32_t iRc = ff_stat( pxClient->pcFileName, &xStatBuf );\r
2375                 if (iRc < 0 )\r
2376                         FreeRTOS_printf( ("In %s: %s\n", pxClient->pcFileName,\r
2377                                 ( const char* )strerror( stdioGET_ERRNO() ) ) );\r
2378 \r
2379                 if( iRc == 0 )\r
2380                 {\r
2381                 BaseType_t xLength;\r
2382                         /* "YYYYMMDDhhmmss" */\r
2383                         if( xSendDate != pdFALSE )\r
2384                         {\r
2385                                 #if( ffconfigTIME_SUPPORT != 0 )\r
2386                                 {\r
2387                                         FF_TimeStruct_t tmStruct;\r
2388                                         time_t secs = xStatBuf.st_mtime;\r
2389                                         FreeRTOS_gmtime_r( &secs, &tmStruct );\r
2390 \r
2391                                         xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "213 %04u%02u%02u%02u%02u%02u\r\n",\r
2392                                                 tmStruct.tm_year + 1900,\r
2393                                                 tmStruct.tm_mon+1,\r
2394                                                 tmStruct.tm_mday,\r
2395                                                 tmStruct.tm_hour,\r
2396                                                 tmStruct.tm_min,\r
2397                                                 tmStruct.tm_sec );\r
2398                                 }\r
2399                                 #else\r
2400                                 {\r
2401                                         xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "213 19700101000000\r\n",\r
2402                                 }\r
2403                                 #endif\r
2404                         }\r
2405                         else\r
2406                         {\r
2407                                 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "213 %lu\r\n", xStatBuf.st_size );\r
2408                         }\r
2409                         prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );\r
2410                         xResult = pdTRUE;\r
2411                 }\r
2412                 else\r
2413                 {\r
2414                         FreeRTOS_printf( ("ftp::sizeDateFile: No such file %s\n", pxClient->pcFileName ) );\r
2415                 }\r
2416         } else {\r
2417                 FreeRTOS_printf( ("ftp::sizeDateFile: Invalid file name: %s ?\n", pxClient->pcFileName ) );\r
2418         }\r
2419         if( xResult == pdFALSE )\r
2420         {\r
2421                 prvSendReply( pxClient->xSocket, REPL_450, 0 ); /* "Requested file action not taken". */\r
2422         }\r
2423 \r
2424         return xResult;\r
2425 }\r
2426 /*-----------------------------------------------------------*/\r
2427 \r
2428 /*\r
2429 ##   ## ##   ## #####      ######  ##   ## #####\r
2430 ### ###  #    #  #   #      #    # ### ###  #   #\r
2431 # ### #  #   #   #    #     #    # # ### #  #    #\r
2432 #  #  #  #   #   #    #     #    # #  #  #  #    #\r
2433 #  #  #  ####    #    #     ###### #  #  #  #    #\r
2434 #     #  #   #   #    #     #  ##  #     #  #    #\r
2435 #     #  #   #   #    #     #   #  #     #  #    #\r
2436 #     #  #    #  #   #      #    # #     #  #   #\r
2437 #     # ###  ## #####      ###  ## #     # #####\r
2438 */\r
2439 static BaseType_t prvMakeRemoveDir( FTPClient_t *pxClient, const char *pcDirectory, BaseType_t xDoRemove )\r
2440 {\r
2441 BaseType_t xResult;\r
2442 BaseType_t xLength;\r
2443 int32_t iRc;\r
2444 int iErrorNo;\r
2445 \r
2446         /* MKD: Make / create a directory (xDoRemove = 0)\r
2447         RMD: Remove a directory (xDoRemove = 1) */\r
2448         xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcDirectory );\r
2449 \r
2450         if( xDoRemove )\r
2451         {\r
2452                 iRc = ff_rmdir( pxClient->pcFileName );\r
2453         }\r
2454         else\r
2455         {\r
2456                 #if( ffconfigMKDIR_RECURSIVE != 0 )\r
2457                 {\r
2458                         iRc = ff_mkdir( pxClient->pcFileName, pdFALSE );\r
2459                 }\r
2460                 #else\r
2461                 {\r
2462                         iRc = ff_mkdir( pxClient->pcFileName );\r
2463                 }\r
2464                 #endif /* ffconfigMKDIR_RECURSIVE */\r
2465         }\r
2466         xResult = pdTRUE;\r
2467 \r
2468         if( iRc >= 0 )\r
2469         {\r
2470                 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "257 \"%s\" directory %s\r\n",\r
2471                         pxClient->pcFileName, xDoRemove ? "removed" : "created" );\r
2472         }\r
2473         else\r
2474         {\r
2475         const char *errMsg = "other error";\r
2476         BaseType_t xFTPCode = 521;\r
2477 \r
2478                 xResult = pdFALSE;\r
2479                 iErrorNo = stdioGET_ERRNO();\r
2480                 switch( iErrorNo )\r
2481                 {\r
2482                         case pdFREERTOS_ERRNO_EEXIST:   errMsg = "Directory already exists"; break;\r
2483                         case pdFREERTOS_ERRNO_ENOTDIR:  errMsg = "Invalid path"; break;                 /* -34 The path of the file was not found. *//*_RB_ As before, what do these negative numbers relate to? */\r
2484                         case pdFREERTOS_ERRNO_ENOTEMPTY:errMsg = "Dir not empty"; break;\r
2485                         case pdFREERTOS_ERRNO_EROFS:    errMsg = "Read-only"; break;                    /* -33  Tried to FF_Open() a file marked read only. */\r
2486                         default:                                                errMsg = strerror( iErrorNo ); break;\r
2487                 }\r
2488                 if( iErrorNo == pdFREERTOS_ERRNO_ENOSPC )\r
2489                 {\r
2490                         xFTPCode = 552;\r
2491                 }\r
2492                 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
2493                         "%ld-\"%s\" %s;\r\n"\r
2494                         "%ld taking no action\r\n",\r
2495                         xFTPCode, pxClient->pcFileName, errMsg, xFTPCode );\r
2496                 FreeRTOS_printf( ( "%sdir '%s': %s\n", xDoRemove ? "rm" : "mk", pxClient->pcFileName, errMsg ) );\r
2497         }\r
2498         prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );\r
2499 \r
2500         return xResult;\r
2501 }\r
2502 /*-----------------------------------------------------------*/\r
2503 \r
2504 static portINLINE BaseType_t IsDigit( char cChar )\r
2505 {\r
2506 BaseType_t xResult;\r
2507 \r
2508         if( cChar >= '0' && cChar <= '9' )\r
2509         {\r
2510                 xResult = pdTRUE;\r
2511         }\r
2512         else\r
2513         {\r
2514                 xResult = pdFALSE;\r
2515         }\r
2516         return xResult;\r
2517 }\r
2518 \r
2519 static BaseType_t prvSendReply( Socket_t xSocket, const char *pcBuffer, BaseType_t xLength )\r
2520 {\r
2521 BaseType_t xResult;\r
2522 \r
2523         if( xLength == 0 )\r
2524         {\r
2525                 xLength = strlen( pcBuffer );\r
2526         }\r
2527         xResult = FreeRTOS_send( xSocket, ( const void * )pcBuffer, ( size_t ) xLength, 0 );\r
2528         if( IsDigit( ( int ) pcBuffer[ 0 ] ) &&\r
2529                 IsDigit( ( int ) pcBuffer[ 1 ] ) &&\r
2530                 IsDigit( ( int ) pcBuffer[ 2 ] ) &&\r
2531                 IsDigit( ( int ) pcBuffer[ 3 ] ) )\r
2532         {\r
2533                 const char *last = pcBuffer + strlen( pcBuffer );\r
2534                 int iLength;\r
2535                 while( ( last > pcBuffer ) && ( ( last[ -1 ] == ftpASCII_CR ) || ( last[ -1 ] == ftpASCII_LF ) ) )\r
2536                 {\r
2537                         last--;\r
2538                 }\r
2539                 iLength = ( int )( last - pcBuffer );\r
2540                 FF_PRINTF( "   %-*.*s", iLength, iLength, pcBuffer );\r
2541         }\r
2542         return xResult;\r
2543 }\r
2544 /*-----------------------------------------------------------*/\r
2545 \r
2546 #if( ipconfigFTP_HAS_RECEIVED_HOOK != 0 )\r
2547 \r
2548         /*\r
2549          * The following function is called for every file received:\r
2550          *     void vApplicationFTPReceivedHook( pcFileName, ulSize, pxFTPClient );\r
2551          * This callback function may do a callback to vFTPReplyMessage() to send messages\r
2552          * to the FTP client like:\r
2553          *      200-Please wait: Received new firmware\r
2554          *      200-Please wait: Please wait a few seconds for reboot\r
2555          */\r
2556         void vFTPReplyMessage( struct xFTP_CLIENT *pxFTPClient, const char *pcMessage )\r
2557         {\r
2558                 if( ( pxFTPClient != NULL ) && ( pxFTPClient->xSocket != NULL ) )\r
2559                 {\r
2560                         prvSendReply( pxFTPClient->xSocket, pcMessage, 0 );\r
2561                 }\r
2562         }\r
2563         /*-----------------------------------------------------------*/\r
2564 \r
2565 #endif /* ipconfigFTP_HAS_RECEIVED_HOOK != 0 */\r
2566 \r
2567 /*\r
2568  * Some explanation:\r
2569  * The FTP client may send: "DELE readme.txt"\r
2570  * Here the complete path is constructed consisting of 3 parts:\r
2571  *\r
2572  * pxClient->pcRootDir  +  pxClient->pcCurrentDir  +  pcFileName\r
2573  *\r
2574  * 'pcCurrentDir' will not be applied for an absolute path like in "DELE /.htaccess"\r
2575  */\r
2576 BaseType_t xMakeAbsolute( FTPClient_t *pxClient, char *pcBuffer, BaseType_t xBufferLength, const char *pcFileName )\r
2577 {\r
2578 BaseType_t xLength = strlen( pxClient->pcRootDir );\r
2579 \r
2580         if( pcFileName[ 0 ] != '/' )\r
2581         {\r
2582         char *pcNewDirBuffer = pcNEW_DIR;\r
2583         BaseType_t xCurLength;\r
2584 \r
2585                 xCurLength = strlen( pxClient->pcCurrentDir );\r
2586                 if( pcBuffer == pcNEW_DIR )\r
2587                 {\r
2588                         /* In one call, the result already goes into pcNEW_DIR.\r
2589                         Use pcFILE_BUFFER in that case */\r
2590                         pcNewDirBuffer = pcFILE_BUFFER;\r
2591                 }\r
2592                 snprintf( pcNewDirBuffer, sizeof( pcNEW_DIR ), "%s%s%s",\r
2593                         pxClient->pcCurrentDir,\r
2594                         pxClient->pcCurrentDir[ xCurLength - 1 ] == '/' ? "" : "/",\r
2595                         pcFileName );\r
2596                 pcFileName = pcNewDirBuffer;\r
2597         }\r
2598         if( strncasecmp( pxClient->pcRootDir, pcFileName, xLength ) == 0 )\r
2599         {\r
2600                 xLength = snprintf( pcBuffer, xBufferLength, "%s", pcFileName );\r
2601         }\r
2602         else\r
2603         {\r
2604                 xLength = snprintf( pcBuffer, xBufferLength, "%s/%s",\r
2605                         pxClient->pcRootDir,\r
2606                         pcFileName[ 0 ] == '/' ? ( pcFileName + 1 ) : pcFileName );\r
2607         }\r
2608 \r
2609         #if( ipconfigFTP_FS_USES_BACKSLAH == 1 )\r
2610                 for( pcPtr = pcBuffer; *pcPtr; pcPtr++ )\r
2611                 {\r
2612                         if( pcPtr[ 0 ] == '/' )\r
2613                         {\r
2614                                 pcPtr[ 0 ] = '\\';\r
2615                         }\r
2616                 }\r
2617         #endif\r
2618 \r
2619         return xLength;\r
2620 }\r
2621 /*-----------------------------------------------------------*/\r
2622 \r
2623 BaseType_t xMakeRelative( FTPClient_t *pxClient, char *pcBuffer, BaseType_t xBufferLength, const char *pcFileName )\r
2624 {\r
2625 BaseType_t xLength = strlen( pxClient->pcRootDir );\r
2626 \r
2627         if( strncasecmp ( pxClient->pcRootDir, pcFileName, xLength ) == 0 )\r
2628         {\r
2629                 xLength = snprintf( pcBuffer, xBufferLength, "%s", pcFileName + xLength );\r
2630         }\r
2631         else\r
2632         {\r
2633                 xLength = snprintf( pcBuffer, xBufferLength, "%s", pcFileName );\r
2634         }\r
2635 \r
2636         return xLength;\r
2637 }\r
2638 /*-----------------------------------------------------------*/\r
2639 \r
2640 #endif /* ipconfigUSE_FTP */\r
2641 \r
2642 \r
2643 \r