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