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