]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/FTP/FreeRTOS_FTP_server.c
Fix some build issues in older kernel demo projects.
[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 ) == pdFALSE )\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 ) == pdFALSE )\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                 FreeRTOS_bind( xSocket, &xAddress, sizeof( xAddress ) );\r
854 \r
855                 #if( ipconfigFTP_TX_BUFSIZE > 0 )\r
856                 {\r
857                         /* Fill in the buffer and window sizes that will be used by the\r
858                         socket. */\r
859                         xWinProps.lTxBufSize = ipconfigFTP_TX_BUFSIZE;\r
860                         xWinProps.lTxWinSize = ipconfigFTP_TX_WINSIZE;\r
861                         xWinProps.lRxBufSize = ipconfigFTP_RX_BUFSIZE;\r
862                         xWinProps.lRxWinSize = ipconfigFTP_RX_WINSIZE;\r
863 \r
864                         /* Set the window and buffer sizes. */\r
865                         FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_WIN_PROPERTIES, ( void * ) &xWinProps,     sizeof( xWinProps ) );\r
866                 }\r
867                 #endif\r
868 \r
869                 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, ( void * ) &xSmallTimeout, sizeof( BaseType_t ) );\r
870                 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_SNDTIMEO, ( void * ) &xSmallTimeout, sizeof( BaseType_t ) );\r
871 \r
872                 /* The same instance of the socket will be used for the connection and\r
873                 data transport. */\r
874                 if( xDoListen != pdFALSE )\r
875                 {\r
876                 BaseType_t xTrueValue = pdTRUE;\r
877                         FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_REUSE_LISTEN_SOCKET, ( void * ) &xTrueValue, sizeof( xTrueValue ) );\r
878                 }\r
879                 pxClient->bits1.bIsListen = xDoListen;\r
880                 pxClient->xTransferSocket = xSocket;\r
881 \r
882                 if( xDoListen != pdFALSE )\r
883                 {\r
884                         FreeRTOS_FD_SET( xSocket, pxClient->pxParent->xSocketSet, eSELECT_EXCEPT | eSELECT_READ );\r
885                         /* Calling FreeRTOS_listen( ) */\r
886                         xResult = prvTransferStart( pxClient );\r
887                         if( xResult >= 0 )\r
888                         {\r
889                                 xResult = pdTRUE;\r
890                         }\r
891                 }\r
892                 else\r
893                 {\r
894                         FreeRTOS_FD_SET( xSocket, pxClient->pxParent->xSocketSet, eSELECT_EXCEPT | eSELECT_READ | eSELECT_WRITE );\r
895                         xResult = pdTRUE;\r
896                 }\r
897         }\r
898         else\r
899         {\r
900                 FreeRTOS_printf( ( "FreeRTOS_socket() failed\n" ) );\r
901                 xResult = -pdFREERTOS_ERRNO_ENOMEM;\r
902         }\r
903 \r
904         /* An active socket (PORT) should connect() later. */\r
905         return xResult;\r
906 }\r
907 /*-----------------------------------------------------------*/\r
908 \r
909 static BaseType_t prvTransferStart( FTPClient_t *pxClient )\r
910 {\r
911 BaseType_t xResult;\r
912 \r
913         /* A transfer socket has been opened, now either call listen() for 'PASV'\r
914         or connect() for the 'PORT' command. */\r
915         if( pxClient->bits1.bIsListen != pdFALSE_UNSIGNED )\r
916         {\r
917                 xResult = FreeRTOS_listen( pxClient->xTransferSocket, 1 );\r
918         }\r
919         else\r
920         {\r
921         struct freertos_sockaddr xAddress;\r
922 \r
923                 xAddress.sin_addr = FreeRTOS_htonl( pxClient->ulClientIP );\r
924                 xAddress.sin_port = FreeRTOS_htons( pxClient->usClientPort );\r
925                 /* Start an active connection for this data socket */\r
926                 xResult = FreeRTOS_connect( pxClient->xTransferSocket, &xAddress, sizeof( xAddress ) );\r
927         }\r
928 \r
929         return xResult;\r
930 }\r
931 /*-----------------------------------------------------------*/\r
932 \r
933 static void prvTransferCheck( FTPClient_t *pxClient )\r
934 {\r
935 BaseType_t xRxSize;\r
936 \r
937         /* A data transfer is busy. Check if there are changes in connectedness. */\r
938         xRxSize = FreeRTOS_rx_size( pxClient->xTransferSocket );\r
939 \r
940         if( pxClient->bits1.bClientConnected == pdFALSE_UNSIGNED )\r
941         {\r
942                 /* The time to receive a small file can be so short, that we don't even\r
943                 see that the socket gets connected and disconnected. Therefore, check\r
944                 the sizeof of the RX buffer. */\r
945                 {\r
946                 struct freertos_sockaddr xAddress;\r
947                 Socket_t xNexSocket;\r
948                 socklen_t xSocketLength = sizeof( xAddress );\r
949 \r
950                         if( pxClient->bits1.bIsListen != pdFALSE_UNSIGNED )\r
951                         {\r
952                                 xNexSocket = FreeRTOS_accept( pxClient->xTransferSocket, &xAddress, &xSocketLength);\r
953                                 if( ( ( xNexSocket != FREERTOS_NO_SOCKET ) && ( xNexSocket != FREERTOS_INVALID_SOCKET ) ) ||\r
954                                         xRxSize > 0 )\r
955                                 {\r
956                                         pxClient->bits1.bClientConnected = pdTRUE_UNSIGNED;\r
957                                 }\r
958                         }\r
959                         else\r
960                         {\r
961                                 if( FreeRTOS_issocketconnected( pxClient->xTransferSocket ) > 0 ||\r
962                                         xRxSize > 0 )\r
963                                 {\r
964                                         pxClient->bits1.bClientConnected = pdTRUE_UNSIGNED;\r
965                                 }\r
966                         }\r
967                         if(     pxClient->bits1.bClientConnected != pdFALSE_UNSIGNED )\r
968                         {\r
969                                 pxClient->bits1.bEmptyFile = pdFALSE_UNSIGNED;\r
970                                 #if( ipconfigHAS_PRINTF != 0 )\r
971                                 {\r
972                                         struct freertos_sockaddr xRemoteAddress, xLocalAddress;\r
973                                         FreeRTOS_GetRemoteAddress( pxClient->xTransferSocket, &xRemoteAddress );\r
974                                         FreeRTOS_GetLocalAddress( pxClient->xTransferSocket, &xLocalAddress );\r
975                                         FreeRTOS_printf( ( "%s Connected from %u to %u\n",\r
976                                                 pxClient->bits1.bIsListen != pdFALSE_UNSIGNED ? "PASV" : "PORT",\r
977                                                 ( unsigned ) FreeRTOS_ntohs( xLocalAddress.sin_port ),\r
978                                                 ( unsigned ) FreeRTOS_ntohs( xRemoteAddress.sin_port ) ) );\r
979                                 }\r
980                                 #endif /* ipconfigHAS_PRINTF */\r
981                                 FreeRTOS_FD_CLR( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );\r
982                                 FreeRTOS_FD_SET( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_READ|eSELECT_EXCEPT );\r
983                         }\r
984                 }\r
985         }\r
986 \r
987         if ( pxClient->bits1.bClientConnected != pdFALSE_UNSIGNED )\r
988         {\r
989                 if( pxClient->pcConnectionAck[ 0 ] != '\0' )\r
990                 {\r
991                 BaseType_t xLength;\r
992                 BaseType_t xRemotePort;\r
993                 struct freertos_sockaddr xRemoteAddress;\r
994 \r
995                         FreeRTOS_GetRemoteAddress( pxClient->xTransferSocket, &xRemoteAddress );\r
996                         xRemotePort = FreeRTOS_ntohs( xRemoteAddress.sin_port );\r
997 \r
998                         /* Tell on the command port 21 we have a data connection */\r
999                         xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
1000                                 pxClient->pcConnectionAck, pxClient->ulClientIP, xRemotePort );\r
1001 \r
1002                         prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );\r
1003                         pxClient->pcConnectionAck[ 0 ] = '\0';\r
1004                 }\r
1005 \r
1006                 if( ( FreeRTOS_issocketconnected( pxClient->xTransferSocket ) == pdFALSE ) && FreeRTOS_rx_size( pxClient->xTransferSocket ) == 0 )\r
1007                 {\r
1008                         prvTransferCloseSocket( pxClient );\r
1009                         prvTransferCloseFile( pxClient );\r
1010                 }\r
1011         }\r
1012 }\r
1013 /*-----------------------------------------------------------*/\r
1014 \r
1015 static void prvTransferCloseSocket( FTPClient_t *pxClient )\r
1016 {\r
1017         if( pxClient->xTransferSocket != FREERTOS_NO_SOCKET )\r
1018         {\r
1019                 /* DEBUGGING ONLY */\r
1020                 BaseType_t xRxSize = FreeRTOS_rx_size( pxClient->xTransferSocket );\r
1021                 if( xRxSize > 0 )\r
1022                 {\r
1023                 BaseType_t xRxSize2;\r
1024                 BaseType_t xStatus;\r
1025                         prvStoreFileWork( pxClient );\r
1026                         xStatus = FreeRTOS_connstatus( pxClient->xTransferSocket );\r
1027                         xRxSize2 = FreeRTOS_rx_size( pxClient->xTransferSocket );\r
1028                         FreeRTOS_printf( ( "FTP: WARNING: %s: RX size = %ld -> %ld (%s)\n",\r
1029                                 FreeRTOS_GetTCPStateName( xStatus ),\r
1030                                 xRxSize, xRxSize2, pxClient->pcFileName ) );\r
1031                         if( xRxSize2 > 1 )\r
1032                         {\r
1033                                 return;\r
1034                         }\r
1035 \r
1036                         /* Remove compiler warnings in case FreeRTOS_printf() is not\r
1037                         defined. */\r
1038                         ( void ) xStatus;\r
1039                 }\r
1040         }\r
1041 \r
1042         if( ( pxClient->pxWriteHandle != NULL ) || ( pxClient->pxReadHandle != NULL ) )\r
1043         {\r
1044         BaseType_t xLength;\r
1045         char pcStrBuf[ 32 ];\r
1046 \r
1047                 if( pxClient->bits1.bHadError == pdFALSE_UNSIGNED )\r
1048                 {\r
1049                         xLength = snprintf( pxClient->pcClientAck, sizeof( pxClient->pcClientAck ),\r
1050                                         "226 Closing connection %d bytes transmitted\r\n", ( int ) pxClient->ulRecvBytes );\r
1051                 }\r
1052                 else\r
1053                 {\r
1054                         xLength = snprintf( pxClient->pcClientAck, sizeof( pxClient->pcClientAck ),\r
1055                                         "451 Requested action aborted after %d bytes\r\n", ( int ) pxClient->ulRecvBytes );\r
1056                 }\r
1057 \r
1058                 /* Tell on the command socket the data connection is now closed. */\r
1059                 prvSendReply( pxClient->xSocket, pxClient->pcClientAck, xLength );\r
1060 \r
1061                 #if( ipconfigHAS_PRINTF != 0 )\r
1062                 {\r
1063                 TickType_t xDelta;\r
1064                 uint32_t ulAverage;\r
1065                         xDelta = xTaskGetTickCount( ) - pxClient->xStartTime;\r
1066                         ulAverage = ulGetAverage( pxClient->ulRecvBytes, xDelta );\r
1067 \r
1068                         FreeRTOS_printf( ("FTP: %s: '%s' %lu Bytes (%s/sec)\n",\r
1069                                 pxClient->pxReadHandle ? "sent" : "recv",\r
1070                                 pxClient->pcFileName,\r
1071                                 pxClient->ulRecvBytes,\r
1072                                 pcMkSize( ulAverage, pcStrBuf, sizeof( pcStrBuf ) ) ) );\r
1073                 }\r
1074                 #endif\r
1075         }\r
1076 \r
1077         if( pxClient->xTransferSocket != FREERTOS_NO_SOCKET )\r
1078         {\r
1079                 FreeRTOS_FD_CLR( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_ALL );\r
1080                 FreeRTOS_closesocket( pxClient->xTransferSocket );\r
1081                 pxClient->xTransferSocket = FREERTOS_NO_SOCKET;\r
1082                 if( pxClient->ulRecvBytes == 0ul )\r
1083                 {\r
1084                         /* Received zero bytes: an empty file */\r
1085                         pxClient->bits1.bEmptyFile = pdTRUE_UNSIGNED;\r
1086                 }\r
1087                 else\r
1088                 {\r
1089                         pxClient->bits1.bEmptyFile = pdFALSE_UNSIGNED;\r
1090                 }\r
1091         }\r
1092         pxClient->bits1.bIsListen = pdFALSE_UNSIGNED;\r
1093         pxClient->bits1.bDirHasEntry = pdFALSE_UNSIGNED;\r
1094         pxClient->bits1.bClientConnected = pdFALSE_UNSIGNED;\r
1095         pxClient->bits1.bHadError = pdFALSE_UNSIGNED;\r
1096 }\r
1097 /*-----------------------------------------------------------*/\r
1098 \r
1099 static void prvTransferCloseFile( FTPClient_t *pxClient )\r
1100 {\r
1101         if( pxClient->pxWriteHandle != NULL )\r
1102         {\r
1103                 ff_fclose( pxClient->pxWriteHandle );\r
1104                 pxClient->pxWriteHandle = NULL;\r
1105                 #if( ipconfigFTP_HAS_RECEIVED_HOOK != 0 )\r
1106                 {\r
1107                         vApplicationFTPReceivedHook( pxClient->pcFileName, pxClient->ulRecvBytes, pxClient );\r
1108                 }\r
1109                 #endif\r
1110 \r
1111         }\r
1112         if( pxClient->pxReadHandle != NULL )\r
1113         {\r
1114                 ff_fclose( pxClient->pxReadHandle );\r
1115                 pxClient->pxReadHandle = NULL;\r
1116         }\r
1117         /* These two field are only used for logging / file-statistics */\r
1118         pxClient->ulRecvBytes = 0ul;\r
1119         pxClient->xStartTime = 0ul;\r
1120 }\r
1121 /*-----------------------------------------------------------*/\r
1122 \r
1123 /**\r
1124  * Guess the transfer type, given the client requested type.\r
1125  * Actually in unix there is no difference between binary and\r
1126  * ascii mode when we work with file descriptors.\r
1127  * If #type is not recognized as a valid client request, -1 is returned.\r
1128  */\r
1129 static BaseType_t prvGetTransferType( const char *pcType )\r
1130 {\r
1131 BaseType_t xResult = -1;\r
1132 \r
1133         if( pcType != NULL )\r
1134         {\r
1135                 BaseType_t xLength = strlen( pcType );\r
1136                 if( xLength == 0 )\r
1137                 {\r
1138                         return -1;\r
1139                 }\r
1140                 switch( pcType[ 0 ] ) {\r
1141                 case 'I':\r
1142                         xResult = TMODE_BINARY;\r
1143                         break;\r
1144                 case 'A':\r
1145                         xResult = TMODE_ASCII;\r
1146                         break;\r
1147                 case 'L':\r
1148                         if( xLength >= 3 )\r
1149                         {\r
1150                                 if( pcType[ 2 ] == '7' )\r
1151                                 {\r
1152                                         xResult = TMODE_7BITS;\r
1153                                 }\r
1154                                 else if( pcType[ 2 ] == '8' )\r
1155                                 {\r
1156                                         xResult = TMODE_7BITS;\r
1157                                 }\r
1158                         }\r
1159                         break;\r
1160                 }\r
1161         }\r
1162         return xResult;\r
1163 }\r
1164 /*-----------------------------------------------------------*/\r
1165 \r
1166 #if( ipconfigHAS_PRINTF != 0 )\r
1167         #define SIZE_1_GB       ( 1024ul * 1024ul * 1024ul )\r
1168         #define SIZE_1_MB       ( 1024ul * 1024ul )\r
1169         #define SIZE_1_KB       ( 1024ul )\r
1170 \r
1171         static const char *pcMkSize( uint32_t ulAmount, char *pcBuffer, BaseType_t xBufferSize )\r
1172         {\r
1173         uint32_t ulGB, ulMB, ulKB, ulByte;\r
1174 \r
1175                 ulGB = ( ulAmount / SIZE_1_GB );\r
1176                 ulAmount -= ( ulGB * SIZE_1_GB );\r
1177                 ulMB = ( ulAmount / SIZE_1_MB );\r
1178                 ulAmount -= ( ulMB * SIZE_1_MB );\r
1179                 ulKB = ( ulAmount / SIZE_1_KB );\r
1180                 ulAmount -= ( ulKB * SIZE_1_KB );\r
1181                 ulByte = ( ulAmount );\r
1182 \r
1183                 if (ulGB != 0ul )\r
1184                 {\r
1185                         snprintf( pcBuffer, xBufferSize, "%lu.%02lu GB", ulGB, (100 * ulMB) / SIZE_1_KB );\r
1186                 }\r
1187                 else if( ulMB != 0ul )\r
1188                 {\r
1189                         snprintf( pcBuffer, xBufferSize, "%lu.%02lu MB", ulMB, (100 * ulKB) / SIZE_1_KB );\r
1190                 }\r
1191                 else if( ulKB != 0ul )\r
1192                 {\r
1193                         snprintf(pcBuffer, xBufferSize, "%lu.%02lu KB", ulKB, (100 * ulByte) / SIZE_1_KB );\r
1194                 }\r
1195                 else\r
1196                 {\r
1197                         snprintf( pcBuffer, xBufferSize, "%lu bytes", ulByte );\r
1198                 }\r
1199 \r
1200                 return pcBuffer;\r
1201         }\r
1202         /*-----------------------------------------------------------*/\r
1203 #endif /* ipconfigHAS_PRINTF != 0 */\r
1204 \r
1205 #if( ipconfigHAS_PRINTF != 0 )\r
1206         static uint32_t ulGetAverage( uint32_t ulAmount, TickType_t xDeltaMs )\r
1207         {\r
1208         uint32_t ulAverage;\r
1209 \r
1210                 /* Get the average amount of bytes per seconds. Ideally this is\r
1211                 calculated by Multiplying with 1000 and dividing by milliseconds:\r
1212                         ulAverage = ( 1000ul * ulAmount ) / xDeltaMs;\r
1213                 Now get a maximum precision, while avoiding an arithmetic overflow:\r
1214                 */\r
1215                 if( xDeltaMs == 0ul )\r
1216                 {\r
1217                         /* Time is zero, there is no average  */\r
1218                         ulAverage = 0ul;\r
1219                 }\r
1220                 else if( ulAmount >= ( ~0ul / 10ul ) )\r
1221                 {\r
1222                         /* More than 409 MB has been transferred, do not multiply. */\r
1223                         ulAverage = ( ulAmount / ( xDeltaMs / 1000ul ) );\r
1224                 }\r
1225                 else if( ulAmount >= ( ~0ul / 100ul ) )\r
1226                 {\r
1227                         /* Between 409 and 41 MB has been transferred, can multiply by 10. */\r
1228                         ulAverage = ( ( ulAmount * 10ul ) / ( xDeltaMs / 100ul ) );\r
1229                 }\r
1230                 else if( ulAmount >= ( ~0ul / 1000ul ) )\r
1231                 {\r
1232                         /* Between 4.1 MB and 41 has been transferred, can multiply by 100. */\r
1233                         ulAverage = ( ( ulAmount * 100ul ) / ( xDeltaMs / 10ul ) );\r
1234                 }\r
1235                 else\r
1236                 {\r
1237                         /* Less than 4.1 MB: can multiply by 1000. */\r
1238                         ulAverage = ( ( ulAmount * 1000ul ) / xDeltaMs );\r
1239                 }\r
1240 \r
1241                 return ulAverage;\r
1242         }\r
1243         /*-----------------------------------------------------------*/\r
1244 #endif /* ipconfigHAS_PRINTF != 0 */\r
1245 \r
1246 static UBaseType_t prvParsePortData( const char *pcCommand, uint32_t *pulIPAddress )\r
1247 {\r
1248 /*_HT_ Using 'unsigned' here because when sscanf() sees '%u', it expects a pointer to 'unsigned'.\r
1249 Not sure about the sscanf() format for UBaseType_t ? */\r
1250 unsigned h1, h2, h3, h4, p1, p2;\r
1251 char sep;\r
1252 UBaseType_t uxResult;\r
1253 \r
1254         /* Expect PORT h1,h2,h3,h4,p1,p2 */\r
1255         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
1256         {\r
1257                 uxResult= 0u;\r
1258         }\r
1259         else\r
1260         {\r
1261                 /* Put in network byte order. */\r
1262                 *pulIPAddress =\r
1263                         ( ( uint32_t ) h1 << 24 ) |\r
1264                         ( ( uint32_t ) h2 << 16 ) |\r
1265                         ( ( uint32_t ) h3 << 8 ) |\r
1266                         ( ( uint32_t ) h4 );\r
1267                 uxResult = ( p1 << 8 ) | p2;\r
1268         }\r
1269         return uxResult;\r
1270 }\r
1271 /*-----------------------------------------------------------*/\r
1272 \r
1273 /*\r
1274 \r
1275  ####                                  #######   #   ###\r
1276 #    #   #                              #   ##   #     #\r
1277 #    #   #                              #    #         #\r
1278 #      ######  ####  ### ##   ####      #   #  ###     #    ####\r
1279  ##      #    #    #  # #  # #    #     #####    #     #   #    #\r
1280    ##    #    #    #  ##   # ######     #   #    #     #   ######\r
1281 #    #   #    #    #  #      #          #        #     #   #\r
1282 #    #   # ## #    #  #      #   ##     #        #     #   #   ##\r
1283  ####     ##   ####  ####     ####     ####    ##### #####  ####\r
1284 \r
1285 */\r
1286 \r
1287 static BaseType_t prvStoreFilePrep( FTPClient_t *pxClient, char *pcFileName )\r
1288 {\r
1289 BaseType_t xResult;\r
1290 FF_FILE *pxNewHandle;\r
1291 size_t uxFileSize = 0ul;\r
1292 int iErrorNo;\r
1293 \r
1294         /* Close previous handle (if any) and reset file transfer parameters. */\r
1295         prvTransferCloseFile( pxClient );\r
1296 \r
1297         xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcFileName );\r
1298 \r
1299         pxNewHandle = NULL;\r
1300 \r
1301         if( pxClient->ulRestartOffset != 0 )\r
1302         {\r
1303         size_t uxOffset = pxClient->ulRestartOffset;\r
1304         int32_t lRc;\r
1305 \r
1306                 pxClient->ulRestartOffset = 0ul; /* Only use 1 time. */\r
1307                 pxNewHandle = ff_fopen( pxClient->pcFileName, "ab" );\r
1308 \r
1309                 if( pxNewHandle != NULL )\r
1310                 {\r
1311                         uxFileSize = pxNewHandle->ulFileSize;\r
1312 \r
1313                         if( uxOffset <= uxFileSize )\r
1314                         {\r
1315                                 lRc = ff_fseek( pxNewHandle, uxOffset, FF_SEEK_SET );\r
1316                         }\r
1317                         else\r
1318                         {\r
1319                                 /* Won't even try to seek after EOF */\r
1320                                 lRc = -pdFREERTOS_ERRNO_EINVAL;\r
1321                         }\r
1322                         if( lRc != 0 )\r
1323                         {\r
1324                         BaseType_t xLength;\r
1325 \r
1326                                 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
1327                                         "450 Seek invalid %u length %u\r\n",\r
1328                                         ( unsigned ) uxOffset, ( unsigned ) uxFileSize );\r
1329 \r
1330                                 /* "Requested file action not taken". */\r
1331                                 prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );\r
1332 \r
1333                                 FreeRTOS_printf( ( "ftp::storeFile: create %s: Seek %u length %u\n",\r
1334                                         pxClient->pcFileName, ( unsigned ) uxOffset, ( unsigned ) uxFileSize ) );\r
1335 \r
1336                                 ff_fclose( pxNewHandle );\r
1337                                 pxNewHandle = NULL;\r
1338                         }\r
1339                 }\r
1340         }\r
1341         else\r
1342         {\r
1343                 pxNewHandle = ff_fopen( pxClient->pcFileName, "wb" );\r
1344         }\r
1345 \r
1346         if( pxNewHandle == NULL )\r
1347         {\r
1348                 iErrorNo = stdioGET_ERRNO();\r
1349                 if( iErrorNo == pdFREERTOS_ERRNO_ENOSPC )\r
1350                 {\r
1351                         prvSendReply( pxClient->xSocket, REPL_552, 0 );\r
1352                 }\r
1353                 else\r
1354                 {\r
1355                         /* "Requested file action not taken". */\r
1356                         prvSendReply( pxClient->xSocket, REPL_450, 0 );\r
1357                 }\r
1358                 FreeRTOS_printf( ( "ftp::storeFile: create %s: %s (errno %d)\n",\r
1359                         pxClient->pcFileName,\r
1360                         ( const char* ) strerror( iErrorNo ), iErrorNo ) );\r
1361 \r
1362                 xResult = pdFALSE;\r
1363         }\r
1364         else\r
1365         {\r
1366                 if( pxClient->bits1.bIsListen )\r
1367                 {\r
1368                         /* True if PASV is used. */\r
1369                         snprintf( pxClient->pcConnectionAck, sizeof( pxClient->pcConnectionAck ),\r
1370                                 "150 Accepted data connection from %%xip:%%u\r\n" );\r
1371                         prvTransferCheck( pxClient );\r
1372                 }\r
1373                 else\r
1374                 {\r
1375                 BaseType_t xLength;\r
1376 \r
1377                         xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "150 Opening BIN connection to store file\r\n" );\r
1378                         prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );\r
1379                         pxClient->pcConnectionAck[ 0 ] = '\0';\r
1380                         prvTransferStart( pxClient ); /* Now active connect. */\r
1381                 }\r
1382 \r
1383                 pxClient->pxWriteHandle = pxNewHandle;\r
1384 \r
1385                 /* To get some statistics about the performance. */\r
1386                 pxClient->xStartTime = xTaskGetTickCount( );\r
1387 \r
1388                 xResult = pdTRUE;\r
1389         }\r
1390 \r
1391         return xResult;\r
1392 }\r
1393 /*-----------------------------------------------------------*/\r
1394 \r
1395 #if( ipconfigFTP_ZERO_COPY_ALIGNED_WRITES == 0 )\r
1396 \r
1397         static BaseType_t prvStoreFileWork( FTPClient_t *pxClient )\r
1398         {\r
1399         BaseType_t xRc, xWritten;\r
1400 \r
1401                 /* Read from the data socket until all has been read or until a negative value\r
1402                 is returned. */\r
1403                 for( ; ; )\r
1404                 {\r
1405                 char *pcBuffer;\r
1406 \r
1407                         /* The "zero-copy" method: */\r
1408                         xRc = FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) &pcBuffer,\r
1409                                 0x20000u, FREERTOS_ZERO_COPY | FREERTOS_MSG_DONTWAIT );\r
1410                         if( xRc <= 0 )\r
1411                         {\r
1412                                 break;\r
1413                         }\r
1414                         pxClient->ulRecvBytes += xRc;\r
1415                         xWritten = ff_fwrite( pcBuffer, 1, xRc, pxClient->pxWriteHandle );\r
1416                         FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) NULL, xRc, 0 );\r
1417                         if( xWritten != xRc )\r
1418                         {\r
1419                                 xRc = -1;\r
1420                                 /* bHadError: a transfer got aborted because of an error. */\r
1421                                 pxClient->bits1.bHadError = pdTRUE_UNSIGNED;\r
1422                                 break;\r
1423                         }\r
1424                 }\r
1425                 return xRc;\r
1426         }\r
1427 \r
1428 #else   /* ipconfigFTP_ZERO_COPY_ALIGNED_WRITES != 0 */\r
1429 \r
1430         #if !defined( ipconfigFTP_PREFERRED_WRITE_SIZE )\r
1431                 /* If you store data on flash, it may be profitable to give 'ipconfigFTP_PREFERRED_WRITE_SIZE'\r
1432                 the same size as the size of the flash' erase blocks, e.g. 4KB */\r
1433                 #define ipconfigFTP_PREFERRED_WRITE_SIZE        512ul\r
1434         #endif\r
1435 \r
1436         static BaseType_t prvStoreFileWork( FTPClient_t *pxClient )\r
1437         {\r
1438         BaseType_t xRc, xWritten;\r
1439 \r
1440                 /* Read from the data socket until all has been read or until a negative\r
1441                 value is returned. */\r
1442                 for( ; ; )\r
1443                 {\r
1444                 char *pcBuffer;\r
1445                 UBaseType_t xStatus;\r
1446 \r
1447                         /* The "zero-copy" method: */\r
1448                         xRc = FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) &pcBuffer,\r
1449                                 0x20000u, FREERTOS_ZERO_COPY | FREERTOS_MSG_DONTWAIT );\r
1450 \r
1451                         if( xRc <= 0 )\r
1452                         {\r
1453                                 /* There are no data or the connection is closed. */\r
1454                                 break;\r
1455                         }\r
1456                         xStatus = FreeRTOS_connstatus( pxClient->xTransferSocket );\r
1457                         if( xStatus != eESTABLISHED )\r
1458                         {\r
1459                                 /* The connection is not established (any more), therefore\r
1460                                 accept any amount of bytes, probably the last few bytes. */\r
1461                         }\r
1462                         else\r
1463                         {\r
1464                                 if( xRc >= ipconfigFTP_PREFERRED_WRITE_SIZE )\r
1465                                 {\r
1466                                         /* More than a sector to write, round down to a multiple of\r
1467                                         PREFERRED_WRITE_SIZE bytes. */\r
1468                                         xRc = ( xRc / ipconfigFTP_PREFERRED_WRITE_SIZE ) * ipconfigFTP_PREFERRED_WRITE_SIZE;\r
1469                                 }\r
1470                                 else\r
1471                                 {\r
1472                                 const StreamBuffer_t *pxBuffer = FreeRTOS_get_rx_buf( pxClient->xTransferSocket );\r
1473                                 size_t uxSpace = pxBuffer->LENGTH - pxBuffer->uxTail;\r
1474 \r
1475                                         if( uxSpace >= ipconfigFTP_PREFERRED_WRITE_SIZE )\r
1476                                         {\r
1477                                                 /* At this moment there are les than PREFERRED_WRITE_SIZE bytes in the RX\r
1478                                                 buffer, but there is space for more. Just return and\r
1479                                                 wait for more. */\r
1480                                                 xRc = 0;\r
1481                                         }\r
1482                                         else\r
1483                                         {\r
1484                                                 /* Now reading beyond the end of the circular buffer,\r
1485                                                 use a normal read. */\r
1486                                                 pcBuffer = pcFILE_BUFFER;\r
1487                                                 xRc = FreeRTOS_recvcount( pxClient->xTransferSocket );\r
1488                                                 xRc = ( xRc / ipconfigFTP_PREFERRED_WRITE_SIZE ) * ipconfigFTP_PREFERRED_WRITE_SIZE;\r
1489                                                 if( xRc > 0 )\r
1490                                                 {\r
1491                                                         xRc = FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) pcBuffer,\r
1492                                                                 sizeof( pcFILE_BUFFER ), FREERTOS_MSG_DONTWAIT );\r
1493                                                 }\r
1494                                         }\r
1495                                 }\r
1496                         }\r
1497                         if( xRc == 0 )\r
1498                         {\r
1499                                 break;\r
1500                         }\r
1501                         pxClient->ulRecvBytes += xRc;\r
1502 \r
1503                         xWritten = ff_fwrite( pcBuffer, 1, xRc, pxClient->pxWriteHandle );\r
1504                         if( pcBuffer != pcFILE_BUFFER )\r
1505                         {\r
1506                                 FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) NULL, xRc, 0 );\r
1507                         }\r
1508                         if( xWritten != xRc )\r
1509                         {\r
1510                                 xRc = -1;\r
1511                                 /* bHadError: a transfer got aborted because of an error. */\r
1512                                 pxClient->bits1.bHadError = pdTRUE_UNSIGNED;\r
1513                                 break;\r
1514                         }\r
1515                 }\r
1516                 return xRc;\r
1517         }\r
1518 \r
1519 #endif /* ipconfigFTP_ZERO_COPY_ALIGNED_WRITES */\r
1520 /*-----------------------------------------------------------*/\r
1521 \r
1522 /*\r
1523 ######                          #                           #######   #   ###\r
1524  #    #          #              #                            #   ##   #     #\r
1525  #    #          #                                           #    #         #\r
1526  #    #  ####  ###### ### ##  ###    ####  #    #  ####      #   #  ###     #    ####\r
1527  ###### #    #   #     # #  #   #   #    # #    # #    #     #####    #     #   #    #\r
1528  #  ##  ######   #     ##   #   #   ###### #    # ######     #   #    #     #   ######\r
1529  #   #  #        #     #        #   #      #    # #          #        #     #   #\r
1530  #    # #   ##   # ##  #        #   #   ##  #  #  #   ##     #        #     #   #   ##\r
1531 ###  ##  ####     ##  ####    #####  ####    ##    ####     ####    ##### #####  ####\r
1532 */\r
1533 static BaseType_t prvRetrieveFilePrep( FTPClient_t *pxClient, char *pcFileName )\r
1534 {\r
1535 BaseType_t xResult = pdTRUE;\r
1536 size_t uxFileSize;\r
1537 \r
1538         /* Close previous handle (if any) and reset file transfer parameters */\r
1539         prvTransferCloseFile( pxClient );\r
1540 \r
1541         xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcFileName );\r
1542 \r
1543         pxClient->pxReadHandle = ff_fopen( pxClient->pcFileName, "rb" );\r
1544         if( pxClient->pxReadHandle == NULL )\r
1545         {\r
1546         int iErrno = stdioGET_ERRNO();\r
1547                 /* "Requested file action not taken". */\r
1548                 prvSendReply( pxClient->xSocket, REPL_450, 0 );\r
1549                 FreeRTOS_printf( ("prvRetrieveFilePrep: open '%s': errno %d: %s\n",\r
1550                         pxClient->pcFileName, iErrno, ( const char * ) strerror( iErrno ) ) );\r
1551                 uxFileSize = 0ul;\r
1552                 xResult = pdFALSE;\r
1553         }\r
1554         else\r
1555         {\r
1556                 uxFileSize = pxClient->pxReadHandle->ulFileSize;\r
1557                 pxClient->uxBytesLeft = uxFileSize;\r
1558                 if( pxClient->ulRestartOffset != 0ul )\r
1559                 {\r
1560                 size_t uxOffset = pxClient->ulRestartOffset;\r
1561                 int32_t iRc;\r
1562 \r
1563                         /* Only use 1 time. */\r
1564                         pxClient->ulRestartOffset = 0;\r
1565 \r
1566                         if( uxOffset < uxFileSize )\r
1567                         {\r
1568                                 iRc = ff_fseek( pxClient->pxReadHandle, uxOffset, FF_SEEK_SET );\r
1569                         }\r
1570                         else\r
1571                         {\r
1572                                 iRc = -pdFREERTOS_ERRNO_EINVAL;\r
1573                         }\r
1574                         if( iRc != 0 )\r
1575                         {\r
1576                         BaseType_t xLength;\r
1577 \r
1578                                 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
1579                                         "450 Seek invalid %u length %u\r\n", ( unsigned ) uxOffset, ( unsigned ) uxFileSize );\r
1580 \r
1581                                 /* "Requested file action not taken". */\r
1582                                 prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );\r
1583 \r
1584                                 FreeRTOS_printf( ( "prvRetrieveFilePrep: create %s: Seek %u length %u\n",\r
1585                                         pxClient->pcFileName, ( unsigned ) uxOffset, ( unsigned ) uxFileSize ) );\r
1586 \r
1587                                 ff_fclose( pxClient->pxReadHandle );\r
1588                                 pxClient->pxReadHandle = NULL;\r
1589                                 xResult = pdFALSE;\r
1590                         }\r
1591                         else\r
1592                         {\r
1593                                 pxClient->uxBytesLeft = uxFileSize - pxClient->ulRestartOffset;\r
1594                         }\r
1595                 }\r
1596         }\r
1597         if( xResult != pdFALSE )\r
1598         {\r
1599                 if( pxClient->bits1.bIsListen != pdFALSE_UNSIGNED )\r
1600                 {\r
1601                         /* True if PASV is used. */\r
1602                         snprintf( pxClient->pcConnectionAck, sizeof( pxClient->pcConnectionAck ),\r
1603                                 "150%cAccepted data connection from %%xip:%%u\r\n%s",\r
1604                                 pxClient->xTransType == TMODE_ASCII ? '-' : ' ',\r
1605                                 pxClient->xTransType == TMODE_ASCII ? "150 NOTE: ASCII mode requested, but binary mode used\r\n" : "" );\r
1606                 } else {\r
1607                 BaseType_t xLength;\r
1608 \r
1609                         xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "150%cOpening data connection to %lxip:%u\r\n%s",\r
1610                                 pxClient->xTransType == TMODE_ASCII ? '-' : ' ',\r
1611                                 pxClient->ulClientIP,\r
1612                                 pxClient->usClientPort,\r
1613                                 pxClient->xTransType == TMODE_ASCII ? "150 NOTE: ASCII mode requested, but binary mode used\r\n" : "" );\r
1614                         prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );\r
1615                         pxClient->pcConnectionAck[ 0 ] = '\0';\r
1616                         prvTransferStart( pxClient );\r
1617                 }\r
1618 \r
1619                 /* Prepare the ACK which will be sent when all data has been sent. */\r
1620                 snprintf( pxClient->pcClientAck, sizeof( pxClient->pcClientAck ), "%s", REPL_226 );\r
1621 \r
1622                 /* To get some statistics about the performance. */\r
1623                 pxClient->xStartTime = xTaskGetTickCount( );\r
1624                 if( uxFileSize == 0ul )\r
1625                 {\r
1626                         FreeRTOS_shutdown( pxClient->xTransferSocket, FREERTOS_SHUT_RDWR );\r
1627                 }\r
1628         }\r
1629 \r
1630         return xResult;\r
1631 }\r
1632 /*-----------------------------------------------------------*/\r
1633 \r
1634 static BaseType_t prvRetrieveFileWork( FTPClient_t *pxClient )\r
1635 {\r
1636 size_t uxSpace;\r
1637 size_t uxCount, uxItemsRead;\r
1638 BaseType_t xRc = 0;\r
1639 BaseType_t xSetEvent = pdFALSE;\r
1640 \r
1641         do\r
1642         {\r
1643         #if( ipconfigFTP_TX_ZERO_COPY != 0 )\r
1644                 char *pcBuffer;\r
1645                 BaseType_t xBufferLength;\r
1646         #endif /* ipconfigFTP_TX_ZERO_COPY */\r
1647 \r
1648                 /* Take the lesser of the two: tx_space (number of bytes that can be\r
1649                 queued for transmission) and uxBytesLeft (the number of bytes left to\r
1650                 read from the file) */\r
1651                 uxSpace = FreeRTOS_tx_space( pxClient->xTransferSocket );\r
1652 \r
1653                 if( uxSpace == 0 )\r
1654                 {\r
1655                         FreeRTOS_FD_SET( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE | eSELECT_EXCEPT );\r
1656                         xRc = FreeRTOS_select( pxClient->pxParent->xSocketSet, 200 );\r
1657                         uxSpace = FreeRTOS_tx_space( pxClient->xTransferSocket );\r
1658                 }\r
1659 \r
1660                 uxCount = FreeRTOS_min_uint32( pxClient->uxBytesLeft, uxSpace );\r
1661 \r
1662                 if( uxCount == 0 )\r
1663                 {\r
1664                         break;\r
1665                 }\r
1666 \r
1667                 #if( ipconfigFTP_TX_ZERO_COPY == 0 )\r
1668                 {\r
1669                         if( uxCount > sizeof( pcFILE_BUFFER ) )\r
1670                         {\r
1671                                 uxCount = sizeof( pcFILE_BUFFER );\r
1672                         }\r
1673                         uxItemsRead = ff_fread( pcFILE_BUFFER, 1, uxCount, pxClient->pxReadHandle );\r
1674                         if( uxItemsRead != uxCount )\r
1675                         {\r
1676                                 FreeRTOS_printf( ( "prvRetrieveFileWork: Got %u Expected %u\n", ( unsigned )uxItemsRead, ( unsigned ) uxCount ) );\r
1677                                 xRc = FreeRTOS_shutdown( pxClient->xTransferSocket, FREERTOS_SHUT_RDWR );\r
1678                                 pxClient->uxBytesLeft = 0u;\r
1679                                 break;\r
1680                         }\r
1681                         pxClient->uxBytesLeft -= uxCount;\r
1682 \r
1683                         if( pxClient->uxBytesLeft == 0u )\r
1684                         {\r
1685                         BaseType_t xTrueValue = 1;\r
1686 \r
1687                                 FreeRTOS_setsockopt( pxClient->xTransferSocket, 0, FREERTOS_SO_CLOSE_AFTER_SEND, ( void * ) &xTrueValue, sizeof( xTrueValue ) );\r
1688                         }\r
1689 \r
1690                         xRc = FreeRTOS_send( pxClient->xTransferSocket, pcFILE_BUFFER, uxCount, 0 );\r
1691                 }\r
1692                 #else /* ipconfigFTP_TX_ZERO_COPY != 0 */\r
1693                 {\r
1694                         /* Use zero-copy transmission:\r
1695                         FreeRTOS_get_tx_head() returns a direct pointer to the TX stream and\r
1696                         set xBufferLength to know how much space there is left. */\r
1697                         pcBuffer = ( char * )FreeRTOS_get_tx_head( pxClient->xTransferSocket, &xBufferLength );\r
1698                         if( ( pcBuffer != NULL ) && ( xBufferLength >= 512 ) )\r
1699                         {\r
1700                                 /* Will read disk data directly to the TX stream of the socket. */\r
1701                                 uxCount = FreeRTOS_min_uint32( uxCount, ( uint32_t )xBufferLength );\r
1702                                 if( uxCount > ( size_t ) 0x40000u )\r
1703                                 {\r
1704                                         uxCount = ( size_t ) 0x40000u;\r
1705                                 }\r
1706                         }\r
1707                         else\r
1708                         {\r
1709                                 /* Use the normal file i/o buffer. */\r
1710                                 pcBuffer = pcFILE_BUFFER;\r
1711                                 if( uxCount > sizeof( pcFILE_BUFFER ) )\r
1712                                 {\r
1713                                         uxCount = sizeof( pcFILE_BUFFER );\r
1714                                 }\r
1715                         }\r
1716 \r
1717                         if ( pxClient->uxBytesLeft >= 1024u )\r
1718                         {\r
1719                                 uxCount &= ~( ( size_t ) 512u - 1u );\r
1720                         }\r
1721 \r
1722                         if( uxCount <= 0u )\r
1723                         {\r
1724                                 /* Nothing to send after rounding down to a multiple of a sector size. */\r
1725                                 break;\r
1726                         }\r
1727 \r
1728                         uxItemsRead = ff_fread( pcBuffer, 1, uxCount, pxClient->pxReadHandle );\r
1729 \r
1730                         if( uxCount != uxItemsRead )\r
1731                         {\r
1732                                 FreeRTOS_printf( ( "prvRetrieveFileWork: Got %u Expected %u\n", ( unsigned )uxItemsRead, ( unsigned )uxCount ) );\r
1733                                 xRc = FreeRTOS_shutdown( pxClient->xTransferSocket, FREERTOS_SHUT_RDWR );\r
1734                                 pxClient->uxBytesLeft = 0u;\r
1735                                 break;\r
1736                         }\r
1737                         pxClient->uxBytesLeft -= uxCount;\r
1738 \r
1739                         if( pxClient->uxBytesLeft == 0u )\r
1740                         {\r
1741                         BaseType_t xTrueValue = 1;\r
1742 \r
1743                                 FreeRTOS_setsockopt( pxClient->xTransferSocket, 0, FREERTOS_SO_CLOSE_AFTER_SEND, ( void * ) &xTrueValue, sizeof( xTrueValue ) );\r
1744                         }\r
1745                         if( pcBuffer != pcFILE_BUFFER )\r
1746                         {\r
1747                                 pcBuffer = NULL;\r
1748                         }\r
1749                         xRc = FreeRTOS_send( pxClient->xTransferSocket, pcBuffer, uxCount, 0 );\r
1750                 }\r
1751                 #endif /* ipconfigFTP_TX_ZERO_COPY */\r
1752 \r
1753                 if( xRc < 0 )\r
1754                 {\r
1755                         break;\r
1756                 }\r
1757 \r
1758                 pxClient->ulRecvBytes += xRc;\r
1759                 if( pxClient->uxBytesLeft == 0u )\r
1760                 {\r
1761                         break;\r
1762                 }\r
1763         } while( uxCount > 0u );\r
1764 \r
1765         if( xRc < 0 )\r
1766         {\r
1767                 FreeRTOS_printf( ( "prvRetrieveFileWork: already disconnected\n" ) );\r
1768         }\r
1769         else if( pxClient->uxBytesLeft <= 0u )\r
1770         {\r
1771         BaseType_t x;\r
1772 \r
1773                 for( x = 0; x < 5; x++ )\r
1774                 {\r
1775                         xRc = FreeRTOS_recv( pxClient->xTransferSocket, pcFILE_BUFFER, sizeof( pcFILE_BUFFER ), 0 );\r
1776                         if( xRc < 0 )\r
1777                         {\r
1778                                 break;\r
1779                         }\r
1780                 }\r
1781 //              FreeRTOS_printf( ( "prvRetrieveFileWork: %s all sent: xRc %ld\n", pxClient->pcFileName, xRc ) );\r
1782         }\r
1783         else\r
1784         {\r
1785                 FreeRTOS_FD_SET( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );\r
1786                 xSetEvent = pdTRUE;\r
1787         }\r
1788         if( xSetEvent == pdFALSE )\r
1789         {\r
1790                 FreeRTOS_FD_CLR( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );\r
1791         }\r
1792         return xRc;\r
1793 }\r
1794 /*-----------------------------------------------------------*/\r
1795 \r
1796 /*\r
1797 ###     #####  ####  #####\r
1798  #        #   #    # # # #\r
1799  #        #   #    #   #\r
1800  #        #   #        #\r
1801  #        #    ##      #\r
1802  #    #   #      ##    #\r
1803  #    #   #   #    #   #\r
1804  #    #   #   #    #   #\r
1805 ####### #####  ####   ####\r
1806 */\r
1807 /* Prepare sending a directory LIST */\r
1808 static BaseType_t prvListSendPrep( FTPClient_t *pxClient )\r
1809 {\r
1810 BaseType_t xFindResult;\r
1811 int iErrorNo;\r
1812 \r
1813         if( pxClient->bits1.bIsListen != pdFALSE_UNSIGNED )\r
1814         {\r
1815                 /* True if PASV is used */\r
1816                 snprintf( pxClient->pcConnectionAck, sizeof( pxClient->pcConnectionAck ),\r
1817                         "150 Accepted data connection from %%xip:%%u\r\n" );\r
1818         }\r
1819         else\r
1820         {\r
1821         BaseType_t xLength;\r
1822 \r
1823                 /* Here the FTP server is supposed to connect() */\r
1824                 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
1825                         "150 Opening ASCII mode data connection to for /bin/ls \r\n" );\r
1826 \r
1827                 prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );\r
1828                 /* Clear the current connection acknowledge message */\r
1829                 pxClient->pcConnectionAck[ 0 ] = '\0';\r
1830                 prvTransferStart( pxClient );\r
1831         }\r
1832 \r
1833         pxClient->xDirCount = 0;\r
1834         xMakeAbsolute( pxClient, pcNEW_DIR, sizeof( pcNEW_DIR ), pxClient->pcCurrentDir );\r
1835 \r
1836         xFindResult = ff_findfirst( pcNEW_DIR, &pxClient->xFindData );\r
1837 \r
1838         pxClient->bits1.bDirHasEntry = ( xFindResult >= 0 );\r
1839 \r
1840         iErrorNo = stdioGET_ERRNO();\r
1841         if( ( xFindResult < 0 ) && ( iErrorNo == pdFREERTOS_ERRNO_ENMFILE ) )\r
1842         {\r
1843                 FreeRTOS_printf( ("prvListSendPrep: Empty directory? (%s)\n", pxClient->pcCurrentDir ) );\r
1844                 prvSendReply( pxClient->xTransferSocket, "total 0\r\n", 0 );\r
1845                 pxClient->xDirCount++;\r
1846         }\r
1847         else if( xFindResult < 0 )\r
1848         {\r
1849                 FreeRTOS_printf( ( "prvListSendPrep: rc = %ld iErrorNo = %d\n", xFindResult, iErrorNo ) );\r
1850                 prvSendReply( pxClient->xSocket, REPL_451, 0 );\r
1851         }\r
1852         pxClient->pcClientAck[ 0 ] = '\0';\r
1853 \r
1854         return pxClient->xDirCount;\r
1855 }\r
1856 /*-----------------------------------------------------------*/\r
1857 \r
1858 #define MAX_DIR_LIST_ENTRY_SIZE         256\r
1859 \r
1860 static BaseType_t prvListSendWork( FTPClient_t *pxClient )\r
1861 {\r
1862 BaseType_t xTxSpace;\r
1863 \r
1864         while( pxClient->bits1.bClientConnected != pdFALSE_UNSIGNED )\r
1865         {\r
1866         char *pcWritePtr = pcCOMMAND_BUFFER;\r
1867         BaseType_t xWriteLength;\r
1868 \r
1869                 xTxSpace = FreeRTOS_tx_space( pxClient->xTransferSocket );\r
1870 \r
1871                 if( xTxSpace > ( BaseType_t ) sizeof( pcCOMMAND_BUFFER ) )\r
1872                 {\r
1873                         xTxSpace = sizeof( pcCOMMAND_BUFFER );\r
1874                 }\r
1875 \r
1876                 while( ( xTxSpace >= MAX_DIR_LIST_ENTRY_SIZE ) && ( pxClient->bits1.bDirHasEntry != pdFALSE_UNSIGNED ) )\r
1877                 {\r
1878                 BaseType_t xLength, xEndOfDir;\r
1879                 int32_t iRc;\r
1880                 int iErrorNo;\r
1881 \r
1882                         xLength = prvGetFileInfoStat( &( pxClient->xFindData.xDirectoryEntry ), pcWritePtr, xTxSpace );\r
1883 \r
1884                         pxClient->xDirCount++;\r
1885                         pcWritePtr += xLength;\r
1886                         xTxSpace -= xLength;\r
1887 \r
1888                         iRc = ff_findnext( &pxClient->xFindData );\r
1889                         iErrorNo = stdioGET_ERRNO();\r
1890 \r
1891                         xEndOfDir = ( iRc < 0 ) && ( iErrorNo == pdFREERTOS_ERRNO_ENMFILE );\r
1892 \r
1893                         pxClient->bits1.bDirHasEntry = ( xEndOfDir == pdFALSE ) && ( iRc >= 0 );\r
1894 \r
1895                         if( ( iRc < 0 ) && ( xEndOfDir == pdFALSE ) )\r
1896                         {\r
1897                                 FreeRTOS_printf( ("prvListSendWork: %s (rc %08x)\n",\r
1898                                         ( const char * ) strerror( iErrorNo ),\r
1899                                         ( unsigned )iRc ) );\r
1900                         }\r
1901                 }\r
1902                 xWriteLength = ( BaseType_t ) ( pcWritePtr - pcCOMMAND_BUFFER );\r
1903 \r
1904                 if( xWriteLength == 0 )\r
1905                 {\r
1906                         break;\r
1907                 }\r
1908 \r
1909                 if( pxClient->bits1.bDirHasEntry == pdFALSE_UNSIGNED )\r
1910                 {\r
1911                 uint32_t ulTotalCount;\r
1912                 uint32_t ulFreeCount;\r
1913                 uint32_t ulPercentage;\r
1914 \r
1915                         ulTotalCount = 1;\r
1916                         ulFreeCount = ff_diskfree( pxClient->pcCurrentDir, &ulTotalCount );\r
1917                         ulPercentage = ( uint32_t ) ( ( 100ULL * ulFreeCount + ulTotalCount / 2 ) / ulTotalCount );\r
1918 \r
1919                         /* Prepare the ACK which will be sent when all data has been sent. */\r
1920                         snprintf( pxClient->pcClientAck, sizeof( pxClient->pcClientAck ),\r
1921                                 "226-Options: -l\r\n"\r
1922                                 "226-%ld matches total\r\n"\r
1923                                 "226 Total %lu KB (%lu %% free)\r\n",\r
1924                                 pxClient->xDirCount, ulTotalCount /1024, ulPercentage );\r
1925                 }\r
1926 \r
1927                 if( xWriteLength )\r
1928                 {\r
1929                         if( pxClient->bits1.bDirHasEntry == pdFALSE_UNSIGNED )\r
1930                         {\r
1931                         BaseType_t xTrueValue = 1;\r
1932 \r
1933                                 FreeRTOS_setsockopt( pxClient->xTransferSocket, 0, FREERTOS_SO_CLOSE_AFTER_SEND, ( void * ) &xTrueValue, sizeof( xTrueValue ) );\r
1934                         }\r
1935 \r
1936                         prvSendReply( pxClient->xTransferSocket, pcCOMMAND_BUFFER, xWriteLength );\r
1937                 }\r
1938 \r
1939                 if( pxClient->bits1.bDirHasEntry == pdFALSE_UNSIGNED )\r
1940                 {\r
1941                         prvSendReply( pxClient->xSocket, pxClient->pcClientAck, 0 );\r
1942                         break;\r
1943                 }\r
1944 \r
1945         }       /* while( pxClient->bits1.bClientConnected )  */\r
1946 \r
1947         return 0;\r
1948 }\r
1949 /*-----------------------------------------------------------*/\r
1950 \r
1951 static const char *pcMonthAbbrev( BaseType_t xMonth )\r
1952 {\r
1953 static const char pcMonthList[] = "JanFebMarAprMayJunJulAugSepOctNovDec";\r
1954 \r
1955         if( xMonth < 1 || xMonth > 12 )\r
1956                 xMonth = 12;\r
1957 \r
1958         return pcMonthList + 3 * ( xMonth - 1 );\r
1959 };\r
1960 /*-----------------------------------------------------------*/\r
1961 \r
1962 static BaseType_t prvGetFileInfoStat( FF_DirEnt_t *pxEntry, char *pcLine, BaseType_t xMaxLength )\r
1963 {\r
1964         char date[ 16 ];\r
1965         char mode[ 11 ] = "----------";\r
1966         BaseType_t st_nlink = 1;\r
1967         const char user[ 9 ] = "freertos";\r
1968         const char group[ 8 ] = "plusfat";\r
1969 \r
1970 /*\r
1971  *      Creates a unix-style listing, understood by most FTP clients:\r
1972  *\r
1973  * -rw-rw-r--   1 freertos FreeRTOS+FAT 10564588 Sep 01 00:17 03.  Metaharmoniks - Star (Instrumental).mp3\r
1974  * -rw-rw-r--   1 freertos FreeRTOS+FAT 19087839 Sep 01 00:17 04.  Simon Le Grec - Dimitri (Wherever U Are) (Cosmos Mix).mp3\r
1975  * -rw-rw-r--   1 freertos FreeRTOS+FAT 11100621 Sep 01 00:16 05.  D-Chill - Mistake (feat. Katy Blue).mp3\r
1976  */\r
1977 \r
1978         #if ( ffconfigTIME_SUPPORT == 1 )\r
1979                 const FF_SystemTime_t *pxCreateTime = &( pxEntry->xCreateTime );\r
1980         #else\r
1981         #warning Do not use this.\r
1982                 FF_SystemTime_t xCreateTime;\r
1983                 const FF_SystemTime_t *pxCreateTime = &xCreateTime;\r
1984         #endif\r
1985         size_t ulSize = ( size_t )pxEntry->ulFileSize;\r
1986         const char *pcFileName = pxEntry->pcFileName;\r
1987 \r
1988         mode[ 0 ] = ( ( pxEntry->ucAttrib & FF_FAT_ATTR_DIR ) != 0 ) ? 'd' : '-';\r
1989         #if( ffconfigDEV_SUPPORT != 0 )\r
1990         {\r
1991                 if( ( pxEntry->ucAttrib & FF_FAT_ATTR_DIR ) == 0 )\r
1992                 {\r
1993                         switch( pxEntry->ucIsDeviceDir )\r
1994                         {\r
1995                         case FF_DEV_CHAR_DEV:\r
1996                                 mode[ 0 ] = 'c';\r
1997                                 break;\r
1998                         case FF_DEV_BLOCK_DEV:\r
1999                                 mode[ 0 ] = 'b';\r
2000                                 break;\r
2001                         }\r
2002                 }\r
2003         }\r
2004         #endif /* ffconfigDEV_SUPPORT != 0 */\r
2005 \r
2006         mode[ 1 ] = 'r';        /* Owner. */\r
2007         mode[ 2 ] = ( ( pxEntry->ucAttrib & FF_FAT_ATTR_READONLY ) != 0 ) ? '-' : 'w';\r
2008         mode[ 3 ] = '-';        /* x for executable. */\r
2009 \r
2010         mode[ 4 ] = 'r';        /* group. */\r
2011         mode[ 5 ] = ( ( pxEntry->ucAttrib & FF_FAT_ATTR_READONLY ) != 0 ) ? '-' : 'w';\r
2012         mode[ 6 ] = '-';        /* x for executable. */\r
2013 \r
2014         mode[ 7 ] = 'r';        /* world. */\r
2015         mode[ 8 ] = '-';\r
2016         mode[ 9 ] = '-';        /* x for executable. */\r
2017 \r
2018         if( pxCreateTime->Month && pxCreateTime->Day )\r
2019         {\r
2020                 snprintf( date, sizeof( date ), "%-3.3s %02d %02d:%02d",\r
2021                         pcMonthAbbrev( pxCreateTime->Month ),\r
2022                         pxCreateTime->Day,\r
2023                         pxCreateTime->Hour,\r
2024                         pxCreateTime->Minute );\r
2025         }\r
2026         else\r
2027         {\r
2028                 snprintf (date, sizeof( date ), "Jan 01 1970");\r
2029         }\r
2030         return snprintf( pcLine, xMaxLength, "%s %3ld %-4s %-4s %8d %12s %s\r\n",\r
2031                 mode, st_nlink, user, group, ( int ) ulSize, date, pcFileName );\r
2032 }\r
2033 /*-----------------------------------------------------------*/\r
2034 \r
2035 /*\r
2036   ####  #     # #####\r
2037  #    # #     #  #   #\r
2038 #     # #     #  #    #\r
2039 #       #  #  #  #    #\r
2040 #       #  #  #  #    #\r
2041 #       #  #  #  #    #\r
2042 #     #  ## ##   #    #\r
2043  #    #  ## ##   #   #\r
2044   ####   ## ##  #####\r
2045 */\r
2046 static BaseType_t prvChangeDir( FTPClient_t *pxClient, char *pcDirectory )\r
2047 {\r
2048 BaseType_t xResult;\r
2049 BaseType_t xIsRootDir, xLength, xValid;\r
2050 BaseType_t xIsDotDir = 0;\r
2051 \r
2052         if( pcDirectory[ 0 ] == '.' )\r
2053         {\r
2054                 if( ( pcDirectory[ 1 ] == '.' ) &&\r
2055                         ( pcDirectory[ 2 ] == '\0' ) )\r
2056                 {\r
2057                         xIsDotDir = 2;\r
2058                 }\r
2059                 else if( pcDirectory[ 1 ] == '\0' )\r
2060                 {\r
2061                         xIsDotDir = 1;\r
2062                 }\r
2063         }\r
2064 \r
2065         if( xIsDotDir != 0 )\r
2066         {\r
2067                 strcpy( pcFILE_BUFFER, pxClient->pcCurrentDir );\r
2068 \r
2069                 if( pcDirectory[ 1 ] == '.' )\r
2070                 {\r
2071                         char *p = strrchr( pcFILE_BUFFER, '/' );\r
2072                         if( p != NULL )\r
2073                         {\r
2074                                 if( p == pcFILE_BUFFER )\r
2075                                 {\r
2076                                         p[ 1 ] = '\0';\r
2077                                 }\r
2078                                 else\r
2079                                 {\r
2080                                         p[ 0 ] = '\0';\r
2081                                 }\r
2082                         }\r
2083                 }\r
2084         }\r
2085         else\r
2086         {\r
2087                 if(pcDirectory[ 0 ] != '/' )\r
2088                 {\r
2089                 BaseType_t xCurLength;\r
2090 \r
2091                         xCurLength = strlen( pxClient->pcCurrentDir );\r
2092                         snprintf( pcFILE_BUFFER, sizeof( pcFILE_BUFFER ), "%s%s%s",\r
2093                                 pxClient->pcCurrentDir,\r
2094                                 pxClient->pcCurrentDir[ xCurLength - 1 ] == '/' ? "" : "/",\r
2095                                 pcDirectory );\r
2096                 }\r
2097                 else\r
2098                 {\r
2099                         snprintf( pcFILE_BUFFER, sizeof( pcFILE_BUFFER ), "%s", pcDirectory );\r
2100                 }\r
2101         }\r
2102 \r
2103         xIsRootDir = ( pcFILE_BUFFER[ 0 ] == '/' ) && ( pcFILE_BUFFER[ 1 ] == '\0' );\r
2104         xMakeAbsolute( pxClient, pcNEW_DIR, sizeof( pcNEW_DIR ), pcFILE_BUFFER );\r
2105 \r
2106         if( ( ( xIsRootDir == pdFALSE ) || ( FF_FS_Count() == 0 ) ) &&  ( ff_finddir( pcNEW_DIR ) == pdFALSE ) )\r
2107         {\r
2108                 xValid = pdFALSE;\r
2109         }\r
2110         else\r
2111         {\r
2112                 xValid = pdTRUE;\r
2113         }\r
2114 \r
2115         if( xValid == pdFALSE )\r
2116         {\r
2117                 /* Get the directory cluster, if it exists. */\r
2118                 FreeRTOS_printf( ("FTP: chdir \"%s\": No such dir\n", pcNEW_DIR ) );\r
2119                 //#define REPL_550 "550 Requested action not taken.\r\n"\r
2120                 //550 /home/hein/arch/h8300: No such file or directory\r
2121                 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
2122                         "550 %s: No such file or directory\r\n",\r
2123                         pcNEW_DIR );\r
2124                 prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );\r
2125                 xResult = pdFALSE;\r
2126         }\r
2127         else\r
2128         {\r
2129                 memcpy( pxClient->pcCurrentDir, pcNEW_DIR, sizeof( pxClient->pcCurrentDir ) );\r
2130 \r
2131                 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "250 Changed to %s\r\n", pcNEW_DIR );\r
2132                 prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );\r
2133                 xResult = pdTRUE;\r
2134         }\r
2135 \r
2136         return xResult;\r
2137 }\r
2138 /*-----------------------------------------------------------*/\r
2139 \r
2140 /*\r
2141 ######  ##    # ####### ######\r
2142  #    # ##    #  #   ##  #    #\r
2143  #    # ##    #  #    #  #    #\r
2144  #    # ###   #  #   #   #    #\r
2145  ###### # ##  #  #####   ######\r
2146  #  ##  #  ## #  #   #   #  ##\r
2147  #   #  #   ###  #       #   #\r
2148  #    # #    ##  #       #    #\r
2149 ###  ## #    ## ####    ###  ##\r
2150 */\r
2151 static BaseType_t prvRenameFrom( FTPClient_t *pxClient, const char *pcFileName )\r
2152 {\r
2153 const char *myReply;\r
2154 FF_FILE *fh;\r
2155 \r
2156         xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcFileName );\r
2157 \r
2158         myReply = NULL;\r
2159 \r
2160         fh = ff_fopen( pxClient->pcFileName, "rb" );\r
2161 \r
2162         if( fh != NULL )\r
2163         {\r
2164                 ff_fclose( fh );\r
2165                 /* REPL_350; "350 Requested file action pending further information." */\r
2166                 snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
2167                         "350 Rename '%s' ...\r\n", pxClient->pcFileName );\r
2168                 myReply = pcCOMMAND_BUFFER;\r
2169                 pxClient->bits.bInRename = pdTRUE_UNSIGNED;\r
2170         }\r
2171         else if( stdioGET_ERRNO() == pdFREERTOS_ERRNO_EISDIR )\r
2172         {\r
2173                 snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
2174                         "350 Rename directory '%s' ...\r\n", pxClient->pcFileName );\r
2175                 myReply = pcCOMMAND_BUFFER;\r
2176                 pxClient->bits.bInRename = pdTRUE_UNSIGNED;\r
2177         }\r
2178         else\r
2179         {\r
2180                 FreeRTOS_printf( ("ftp::renameFrom[%s]\n%s\n", pxClient->pcFileName, strerror( stdioGET_ERRNO() ) ) );\r
2181                 myReply = REPL_451;             /* "451 Requested action aborted. Local error in processing." */\r
2182         }\r
2183         if( myReply )\r
2184         {\r
2185                 prvSendReply( pxClient->xSocket, myReply, 0 );\r
2186         }\r
2187 \r
2188         return pdTRUE;\r
2189 }\r
2190 /*-----------------------------------------------------------*/\r
2191 \r
2192 /*\r
2193 ######  ##    # #####   ###\r
2194  #    # ##    # # # #  ## ##\r
2195  #    # ##    #   #   ##   ##\r
2196  #    # ###   #   #   #     #\r
2197  ###### # ##  #   #   #     #\r
2198  #  ##  #  ## #   #   #     #\r
2199  #   #  #   ###   #   ##   ##\r
2200  #    # #    ##   #    ## ##\r
2201 ###  ## #    ##  ####   ###\r
2202 */\r
2203 static BaseType_t prvRenameTo( FTPClient_t *pxClient, const char *pcFileName )\r
2204 {\r
2205 const char *myReply = NULL;\r
2206 int iResult;\r
2207 \r
2208         xMakeAbsolute( pxClient, pcNEW_DIR, sizeof( pcNEW_DIR ), pcFileName );\r
2209 \r
2210         /* FreeRTOS+FAT rename has an extra parameter: "remove target if already\r
2211         exists". */\r
2212         iResult = ff_rename( pxClient->pcFileName, pcNEW_DIR, pdFALSE );\r
2213 \r
2214         if( iResult < 0 )\r
2215         {\r
2216                 iResult = stdioGET_ERRNO();\r
2217         }\r
2218         else\r
2219         {\r
2220                 iResult = 0;\r
2221         }\r
2222 \r
2223         switch( iResult )\r
2224         {\r
2225         case 0:\r
2226                 FreeRTOS_printf( ( "ftp::renameTo[%s,%s]: Ok\n", pxClient->pcFileName, pcNEW_DIR ) );\r
2227                 snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
2228                         "250 Rename successful to '%s'\r\n", pcNEW_DIR );\r
2229                 myReply = pcCOMMAND_BUFFER;\r
2230                 break;\r
2231         case pdFREERTOS_ERRNO_EEXIST:\r
2232                 /* the destination file already exists.\r
2233                 "450 Requested file action not taken.\r\n"*/\r
2234                 snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
2235                         "450 Already exists '%s'\r\n", pcNEW_DIR );\r
2236                 myReply = pcCOMMAND_BUFFER;\r
2237                 break;\r
2238         case pdFREERTOS_ERRNO_EIO:      /* FF_ERR_FILE_COULD_NOT_CREATE_DIRENT */\r
2239                 /* if dirent creation failed (fatal error!).\r
2240                 "553 Requested action not taken.\r\n" */\r
2241                 FreeRTOS_printf( ("ftp::renameTo[%s,%s]: Error creating DirEnt\n",\r
2242                         pxClient->pcFileName, pcNEW_DIR ) );\r
2243                 myReply = REPL_553;\r
2244                 break;\r
2245         case pdFREERTOS_ERRNO_ENXIO:\r
2246         case pdFREERTOS_ERRNO_ENOENT:\r
2247                 /* if the source file was not found.\r
2248                 "450 Requested file action not taken.\r\n" */\r
2249                 snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
2250                         "450 No such file '%s'\r\n", pxClient->pcFileName );\r
2251                 myReply = pcCOMMAND_BUFFER;\r
2252                 break;\r
2253         default:\r
2254                 FreeRTOS_printf( ("ftp::renameTo[%s,%s]: %s\n", pxClient->pcFileName, pcNEW_DIR,\r
2255                         (const char*)strerror( stdioGET_ERRNO() ) ) );\r
2256                 myReply = REPL_451;     /* "451 Requested action aborted. Local error in processing." */\r
2257                 break;\r
2258         }\r
2259         prvSendReply( pxClient->xSocket, myReply, 0 );\r
2260 \r
2261         return pdTRUE;\r
2262 }\r
2263 /*-----------------------------------------------------------*/\r
2264 \r
2265 /*\r
2266  ####    #\r
2267 #    #   #     #\r
2268 #    #         #\r
2269 #      ###   ######  ####\r
2270  ##      #     #    #    #\r
2271    ##    #     #    ######\r
2272 #    #   #     #    #\r
2273 #    #   #     # ## #   ##\r
2274  ####  #####    ##   ####\r
2275 */\r
2276 static BaseType_t prvSiteCmd( FTPClient_t *pxClient, char *pcRestCommand )\r
2277 {\r
2278         ( void ) pxClient;\r
2279         ( void ) pcRestCommand;\r
2280 \r
2281         return 0;\r
2282 }\r
2283 /*-----------------------------------------------------------*/\r
2284 \r
2285 /*\r
2286 #####          ###\r
2287  #   #           #            #\r
2288  #    #          #            #\r
2289  #    #  ####    #    ####  ######  ####\r
2290  #    # #    #   #   #    #   #    #    #\r
2291  #    # ######   #   ######   #    ######\r
2292  #    # #        #   #        #    #\r
2293  #   #  #   ##   #   #   ##   # ## #   ##\r
2294 #####    ####  #####  ####     ##   ####\r
2295 */\r
2296 static BaseType_t prvDeleteFile( FTPClient_t *pxClient, char *pcFileName )\r
2297 {\r
2298 BaseType_t xResult, xLength;\r
2299 int32_t iRc;\r
2300 int iErrorNo;\r
2301 \r
2302         /* DELE: Delete a file. */\r
2303         xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcFileName );\r
2304 \r
2305         iRc = ff_remove( pxClient->pcFileName );\r
2306 \r
2307         if (iRc >= 0 )\r
2308         {\r
2309                 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
2310                         "250 File \"%s\" removed\r\n", pxClient->pcFileName );\r
2311                 xResult = pdTRUE;\r
2312         }\r
2313         else\r
2314         {\r
2315                 const char *errMsg = "other error";\r
2316 \r
2317                 iErrorNo = stdioGET_ERRNO();\r
2318                 switch( iErrorNo )\r
2319                 {                                                                                                                                               /*_RB_ What do these negative numbers relate to? */\r
2320                         case pdFREERTOS_ERRNO_ENOENT:   errMsg = "No such file"; break;         /* -31  File was not found. */\r
2321                         case pdFREERTOS_ERRNO_EALREADY: errMsg = "File still open"; break;      /* -30  File is in use. */\r
2322                         case pdFREERTOS_ERRNO_EISDIR:   errMsg = "Is a dir"; break;                     /* -32  Tried to FF_Open() a Directory. */\r
2323                         case pdFREERTOS_ERRNO_EROFS:    errMsg = "Read-only"; break;            /* -33  Tried to FF_Open() a file marked read only. */\r
2324                         case pdFREERTOS_ERRNO_ENOTDIR:  errMsg = "Invalid path"; break;         /* -34  The path of the file was not found. */\r
2325                 }\r
2326                 FreeRTOS_printf( ( "ftp::delFile: '%s' because %s\n",\r
2327                         pxClient->pcFileName, strerror( iErrorNo ) ) );\r
2328 \r
2329                 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
2330                         "521-\"%s\" %s;\r\n"\r
2331                         "521 taking no action\r\n",\r
2332                         pxClient->pcFileName, errMsg );\r
2333 \r
2334                 xResult = pdFALSE;\r
2335         }\r
2336 \r
2337         prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );\r
2338 \r
2339         return xResult;\r
2340 }\r
2341 /*-----------------------------------------------------------*/\r
2342 \r
2343 /*\r
2344  ####    #                       #####\r
2345 #    #   #                        #   #            #\r
2346 #    #                            #    #           #\r
2347 #      ###   ######  ####         #    #  ####   ######  ####\r
2348  ##      #   #    # #    #        #    #      #    #    #    #\r
2349    ##    #       #  ######        #    #  #####    #    ######\r
2350 #    #   #     #    #             #    # #    #    #    #\r
2351 #    #   #    #     #   ##        #   #  #    #    # ## #   ##\r
2352  ####  ##### ######  ####        #####    ### ##    ##   ####\r
2353 */\r
2354 static BaseType_t prvSizeDateFile( FTPClient_t *pxClient, char *pcFileName, BaseType_t xSendDate )\r
2355 {\r
2356 BaseType_t xResult = pdFALSE;\r
2357 char *pcPtr;\r
2358 \r
2359         /* SIZE: get the size of a file (xSendDate = 0)\r
2360         MDTM: get data and time properties (xSendDate = 1) */\r
2361         xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcFileName );\r
2362 \r
2363         pcPtr = strrchr( pxClient->pcFileName, '/' );\r
2364 \r
2365         if( ( pcPtr != NULL ) && ( pcPtr[ 1 ] != '\0' ) )\r
2366         {\r
2367                 FF_Stat_t xStatBuf;\r
2368                 int32_t iRc = ff_stat( pxClient->pcFileName, &xStatBuf );\r
2369                 if (iRc < 0 )\r
2370                         FreeRTOS_printf( ("In %s: %s\n", pxClient->pcFileName,\r
2371                                 ( const char* )strerror( stdioGET_ERRNO() ) ) );\r
2372 \r
2373                 if( iRc == 0 )\r
2374                 {\r
2375                 BaseType_t xLength;\r
2376                         /* "YYYYMMDDhhmmss" */\r
2377                         if( xSendDate != pdFALSE )\r
2378                         {\r
2379                                 #if( ffconfigTIME_SUPPORT != 0 )\r
2380                                 {\r
2381                                         FF_TimeStruct_t tmStruct;\r
2382                                         time_t secs = xStatBuf.st_mtime;\r
2383                                         FreeRTOS_gmtime_r( &secs, &tmStruct );\r
2384 \r
2385                                         xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "213 %04u%02u%02u%02u%02u%02u\r\n",\r
2386                                                 tmStruct.tm_year + 1900,\r
2387                                                 tmStruct.tm_mon+1,\r
2388                                                 tmStruct.tm_mday,\r
2389                                                 tmStruct.tm_hour,\r
2390                                                 tmStruct.tm_min,\r
2391                                                 tmStruct.tm_sec );\r
2392                                 }\r
2393                                 #else\r
2394                                 {\r
2395                                         xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "213 19700101000000\r\n",\r
2396                                 }\r
2397                                 #endif\r
2398                         }\r
2399                         else\r
2400                         {\r
2401                                 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "213 %lu\r\n", xStatBuf.st_size );\r
2402                         }\r
2403                         prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );\r
2404                         xResult = pdTRUE;\r
2405                 }\r
2406                 else\r
2407                 {\r
2408                         FreeRTOS_printf( ("ftp::sizeDateFile: No such file %s\n", pxClient->pcFileName ) );\r
2409                 }\r
2410         } else {\r
2411                 FreeRTOS_printf( ("ftp::sizeDateFile: Invalid file name: %s ?\n", pxClient->pcFileName ) );\r
2412         }\r
2413         if( xResult == pdFALSE )\r
2414         {\r
2415                 prvSendReply( pxClient->xSocket, REPL_450, 0 ); /* "Requested file action not taken". */\r
2416         }\r
2417 \r
2418         return xResult;\r
2419 }\r
2420 /*-----------------------------------------------------------*/\r
2421 \r
2422 /*\r
2423 ##   ## ##   ## #####      ######  ##   ## #####\r
2424 ### ###  #    #  #   #      #    # ### ###  #   #\r
2425 # ### #  #   #   #    #     #    # # ### #  #    #\r
2426 #  #  #  #   #   #    #     #    # #  #  #  #    #\r
2427 #  #  #  ####    #    #     ###### #  #  #  #    #\r
2428 #     #  #   #   #    #     #  ##  #     #  #    #\r
2429 #     #  #   #   #    #     #   #  #     #  #    #\r
2430 #     #  #    #  #   #      #    # #     #  #   #\r
2431 #     # ###  ## #####      ###  ## #     # #####\r
2432 */\r
2433 static BaseType_t prvMakeRemoveDir( FTPClient_t *pxClient, const char *pcDirectory, BaseType_t xDoRemove )\r
2434 {\r
2435 BaseType_t xResult;\r
2436 BaseType_t xLength;\r
2437 int32_t iRc;\r
2438 int iErrorNo;\r
2439 \r
2440         /* MKD: Make / create a directory (xDoRemove = 0)\r
2441         RMD: Remove a directory (xDoRemove = 1) */\r
2442         xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcDirectory );\r
2443 \r
2444         if( xDoRemove )\r
2445         {\r
2446                 iRc = ff_rmdir( pxClient->pcFileName );\r
2447         }\r
2448         else\r
2449         {\r
2450                 #if( ffconfigMKDIR_RECURSIVE != 0 )\r
2451                 {\r
2452                         iRc = ff_mkdir( pxClient->pcFileName, pdFALSE );\r
2453                 }\r
2454                 #else\r
2455                 {\r
2456                         iRc = ff_mkdir( pxClient->pcFileName );\r
2457                 }\r
2458                 #endif /* ffconfigMKDIR_RECURSIVE */\r
2459         }\r
2460         xResult = pdTRUE;\r
2461 \r
2462         if( iRc >= 0 )\r
2463         {\r
2464                 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "257 \"%s\" directory %s\r\n",\r
2465                         pxClient->pcFileName, xDoRemove ? "removed" : "created" );\r
2466         }\r
2467         else\r
2468         {\r
2469         const char *errMsg = "other error";\r
2470         BaseType_t xFTPCode = 521;\r
2471 \r
2472                 xResult = pdFALSE;\r
2473                 iErrorNo = stdioGET_ERRNO();\r
2474                 switch( iErrorNo )\r
2475                 {\r
2476                         case pdFREERTOS_ERRNO_EEXIST:   errMsg = "Directory already exists"; break;\r
2477                         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
2478                         case pdFREERTOS_ERRNO_ENOTEMPTY:errMsg = "Dir not empty"; break;\r
2479                         case pdFREERTOS_ERRNO_EROFS:    errMsg = "Read-only"; break;                    /* -33  Tried to FF_Open() a file marked read only. */\r
2480                         default:                                                errMsg = strerror( iErrorNo ); break;\r
2481                 }\r
2482                 if( iErrorNo == pdFREERTOS_ERRNO_ENOSPC )\r
2483                 {\r
2484                         xFTPCode = 552;\r
2485                 }\r
2486                 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),\r
2487                         "%ld-\"%s\" %s;\r\n"\r
2488                         "%ld taking no action\r\n",\r
2489                         xFTPCode, pxClient->pcFileName, errMsg, xFTPCode );\r
2490                 FreeRTOS_printf( ( "%sdir '%s': %s\n", xDoRemove ? "rm" : "mk", pxClient->pcFileName, errMsg ) );\r
2491         }\r
2492         prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );\r
2493 \r
2494         return xResult;\r
2495 }\r
2496 /*-----------------------------------------------------------*/\r
2497 \r
2498 static portINLINE BaseType_t IsDigit( char cChar )\r
2499 {\r
2500 BaseType_t xResult;\r
2501 \r
2502         if( cChar >= '0' && cChar <= '9' )\r
2503         {\r
2504                 xResult = pdTRUE;\r
2505         }\r
2506         else\r
2507         {\r
2508                 xResult = pdFALSE;\r
2509         }\r
2510         return xResult;\r
2511 }\r
2512 \r
2513 static BaseType_t prvSendReply( Socket_t xSocket, const char *pcBuffer, BaseType_t xLength )\r
2514 {\r
2515 BaseType_t xResult;\r
2516 \r
2517         if( xLength == 0 )\r
2518         {\r
2519                 xLength = strlen( pcBuffer );\r
2520         }\r
2521         xResult = FreeRTOS_send( xSocket, ( const void * )pcBuffer, ( size_t ) xLength, 0 );\r
2522         if( IsDigit( ( int ) pcBuffer[ 0 ] ) &&\r
2523                 IsDigit( ( int ) pcBuffer[ 1 ] ) &&\r
2524                 IsDigit( ( int ) pcBuffer[ 2 ] ) &&\r
2525                 IsDigit( ( int ) pcBuffer[ 3 ] ) )\r
2526         {\r
2527                 const char *last = pcBuffer + strlen( pcBuffer );\r
2528                 int iLength;\r
2529                 while( ( last > pcBuffer ) && ( ( last[ -1 ] == ftpASCII_CR ) || ( last[ -1 ] == ftpASCII_LF ) ) )\r
2530                 {\r
2531                         last--;\r
2532                 }\r
2533                 iLength = ( int )( last - pcBuffer );\r
2534                 FF_PRINTF( "   %-*.*s", iLength, iLength, pcBuffer );\r
2535         }\r
2536         return xResult;\r
2537 }\r
2538 /*-----------------------------------------------------------*/\r
2539 \r
2540 #if( ipconfigFTP_HAS_RECEIVED_HOOK != 0 )\r
2541 \r
2542         /*\r
2543          * The following function is called for every file received:\r
2544          *     void vApplicationFTPReceivedHook( pcFileName, ulSize, pxFTPClient );\r
2545          * This callback function may do a callback to vFTPReplyMessage() to send messages\r
2546          * to the FTP client like:\r
2547          *      200-Please wait: Received new firmware\r
2548          *      200-Please wait: Please wait a few seconds for reboot\r
2549          */\r
2550         void vFTPReplyMessage( struct xFTP_CLIENT *pxFTPClient, const char *pcMessage )\r
2551         {\r
2552                 if( ( pxFTPClient != NULL ) && ( pxFTPClient->xSocket != NULL ) )\r
2553                 {\r
2554                         prvSendReply( pxFTPClient->xSocket, pcMessage, 0 );\r
2555                 }\r
2556         }\r
2557         /*-----------------------------------------------------------*/\r
2558 \r
2559 #endif /* ipconfigFTP_HAS_RECEIVED_HOOK != 0 */\r
2560 \r
2561 /*\r
2562  * Some explanation:\r
2563  * The FTP client may send: "DELE readme.txt"\r
2564  * Here the complete path is constructed consisting of 3 parts:\r
2565  *\r
2566  * pxClient->pcRootDir  +  pxClient->pcCurrentDir  +  pcFileName\r
2567  *\r
2568  * 'pcCurrentDir' will not be applied for an absolute path like in "DELE /.htaccess"\r
2569  */\r
2570 BaseType_t xMakeAbsolute( FTPClient_t *pxClient, char *pcBuffer, BaseType_t xBufferLength, const char *pcFileName )\r
2571 {\r
2572 BaseType_t xLength = strlen( pxClient->pcRootDir );\r
2573 \r
2574         if( pcFileName[ 0 ] != '/' )\r
2575         {\r
2576         char *pcNewDirBuffer = pcNEW_DIR;\r
2577         BaseType_t xCurLength;\r
2578 \r
2579                 xCurLength = strlen( pxClient->pcCurrentDir );\r
2580                 if( pcBuffer == pcNEW_DIR )\r
2581                 {\r
2582                         /* In one call, the result already goes into pcNEW_DIR.\r
2583                         Use pcFILE_BUFFER in that case */\r
2584                         pcNewDirBuffer = pcFILE_BUFFER;\r
2585                 }\r
2586                 snprintf( pcNewDirBuffer, sizeof( pcNEW_DIR ), "%s%s%s",\r
2587                         pxClient->pcCurrentDir,\r
2588                         pxClient->pcCurrentDir[ xCurLength - 1 ] == '/' ? "" : "/",\r
2589                         pcFileName );\r
2590                 pcFileName = pcNewDirBuffer;\r
2591         }\r
2592         if( strncasecmp( pxClient->pcRootDir, pcFileName, xLength ) == 0 )\r
2593         {\r
2594                 xLength = snprintf( pcBuffer, xBufferLength, "%s", pcFileName );\r
2595         }\r
2596         else\r
2597         {\r
2598                 xLength = snprintf( pcBuffer, xBufferLength, "%s/%s",\r
2599                         pxClient->pcRootDir,\r
2600                         pcFileName[ 0 ] == '/' ? ( pcFileName + 1 ) : pcFileName );\r
2601         }\r
2602 \r
2603         #if( ipconfigFTP_FS_USES_BACKSLAH == 1 )\r
2604                 for( pcPtr = pcBuffer; *pcPtr; pcPtr++ )\r
2605                 {\r
2606                         if( pcPtr[ 0 ] == '/' )\r
2607                         {\r
2608                                 pcPtr[ 0 ] = '\\';\r
2609                         }\r
2610                 }\r
2611         #endif\r
2612 \r
2613         return xLength;\r
2614 }\r
2615 /*-----------------------------------------------------------*/\r
2616 \r
2617 BaseType_t xMakeRelative( FTPClient_t *pxClient, char *pcBuffer, BaseType_t xBufferLength, const char *pcFileName )\r
2618 {\r
2619 BaseType_t xLength = strlen( pxClient->pcRootDir );\r
2620 \r
2621         if( strncasecmp ( pxClient->pcRootDir, pcFileName, xLength ) == 0 )\r
2622         {\r
2623                 xLength = snprintf( pcBuffer, xBufferLength, "%s", pcFileName + xLength );\r
2624         }\r
2625         else\r
2626         {\r
2627                 xLength = snprintf( pcBuffer, xBufferLength, "%s", pcFileName );\r
2628         }\r
2629 \r
2630         return xLength;\r
2631 }\r
2632 /*-----------------------------------------------------------*/\r
2633 \r
2634 #endif /* ipconfigUSE_FTP */\r
2635 \r
2636 \r
2637 \r