Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
ftp_server_commands.c
00001 /** 00002 * @file ftp_server_commands.c 00003 * @brief FTP server (command processing) 00004 * 00005 * @section License 00006 * 00007 * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. 00008 * 00009 * This file is part of CycloneTCP Open. 00010 * 00011 * This program is free software; you can redistribute it and/or 00012 * modify it under the terms of the GNU General Public License 00013 * as published by the Free Software Foundation; either version 2 00014 * of the License, or (at your option) any later version. 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU General Public License 00022 * along with this program; if not, write to the Free Software Foundation, 00023 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00024 * 00025 * @author Oryx Embedded SARL (www.oryx-embedded.com) 00026 * @version 1.7.6 00027 **/ 00028 00029 //Switch to the appropriate trace level 00030 #define TRACE_LEVEL FTP_TRACE_LEVEL 00031 00032 //Dependencies 00033 #include <stdlib.h> 00034 #include "ftp/ftp_server.h" 00035 #include "ftp/ftp_server_events.h" 00036 #include "ftp/ftp_server_commands.h" 00037 #include "ftp/ftp_server_misc.h" 00038 #include "str.h" 00039 #include "path.h" 00040 #include "error.h" 00041 #include "debug.h" 00042 00043 //Check TCP/IP stack configuration 00044 #if (FTP_SERVER_SUPPORT == ENABLED) 00045 00046 00047 /** 00048 * @brief FTP command processing 00049 * @param[in] context Pointer to the FTP server context 00050 * @param[in] connection Pointer to the client connection 00051 **/ 00052 00053 void ftpServerProcessCmd(FtpServerContext *context, 00054 FtpClientConnection *connection) 00055 { 00056 size_t n; 00057 char_t *p; 00058 00059 //The <CRLF> sequence should be used to terminate the command line 00060 for(n = 0; n < connection->commandLength; n++) 00061 { 00062 if(connection->command[n] == '\n') 00063 break; 00064 } 00065 00066 //Any command to process? 00067 if(n < connection->commandLength) 00068 { 00069 //Properly terminate the string with a NULL character 00070 connection->command[n] = '\0'; 00071 //Remove trailing whitespace from the command line 00072 strRemoveTrailingSpace(connection->command); 00073 00074 //Debug message 00075 TRACE_DEBUG("FTP client: %s\r\n", connection->command); 00076 00077 //Command line too long? 00078 if(connection->controlState == FTP_CONTROL_STATE_DISCARD) 00079 { 00080 //Switch to idle state 00081 connection->controlState = FTP_CONTROL_STATE_IDLE; 00082 //Format response message 00083 strcpy(connection->response, "500 Command line too long\r\n"); 00084 } 00085 else 00086 { 00087 //The command name and the arguments are separated by one or more spaces 00088 for(p = connection->command; *p != '\0' && *p != ' '; p++); 00089 00090 //Space character found? 00091 if(*p == ' ') 00092 { 00093 //Split the string at the first occurrence of the space character 00094 *(p++) = '\0'; 00095 //Skip extra whitespace 00096 while(*p == ' ') p++; 00097 } 00098 00099 //NOOP command received 00100 if(!strcasecmp(connection->command, "NOOP")) 00101 ftpServerProcessNoop(context, connection, p); 00102 //SYST command received 00103 else if(!strcasecmp(connection->command, "SYST")) 00104 ftpServerProcessSyst(context, connection, p); 00105 //FEAT command received? 00106 else if(!strcasecmp(connection->command, "FEAT")) 00107 ftpServerProcessFeat(context, connection, p); 00108 //TYPE command received? 00109 else if(!strcasecmp(connection->command, "TYPE")) 00110 ftpServerProcessType(context, connection, p); 00111 //STRU command received? 00112 else if(!strcasecmp(connection->command, "STRU")) 00113 ftpServerProcessStru(context, connection, p); 00114 //MODE command received? 00115 else if(!strcasecmp(connection->command, "MODE")) 00116 ftpServerProcessMode(context, connection, p); 00117 //USER command received? 00118 else if(!strcasecmp(connection->command, "USER")) 00119 ftpServerProcessUser(context, connection, p); 00120 //PASS command received? 00121 else if(!strcasecmp(connection->command, "PASS")) 00122 ftpServerProcessPass(context, connection, p); 00123 //REIN command received? 00124 else if(!strcasecmp(connection->command, "REIN")) 00125 ftpServerProcessRein(context, connection, p); 00126 //QUIT command received? 00127 else if(!strcasecmp(connection->command, "QUIT")) 00128 ftpServerProcessQuit(context, connection, p); 00129 //PORT command received? 00130 else if(!strcasecmp(connection->command, "PORT")) 00131 ftpServerProcessPort(context, connection, p); 00132 //EPRT command received? 00133 else if(!strcasecmp(connection->command, "EPRT")) 00134 ftpServerProcessEprt(context, connection, p); 00135 //PASV command received? 00136 else if(!strcasecmp(connection->command, "PASV")) 00137 ftpServerProcessPasv(context, connection, p); 00138 //EPSV command received? 00139 else if(!strcasecmp(connection->command, "EPSV")) 00140 ftpServerProcessEpsv(context, connection, p); 00141 //ABOR command received? 00142 else if(!strcasecmp(connection->command, "ABOR")) 00143 ftpServerProcessAbor(context, connection, p); 00144 //PWD command received? 00145 else if(!strcasecmp(connection->command, "PWD")) 00146 ftpServerProcessPwd(context, connection, p); 00147 //LIST command received? 00148 else if(!strcasecmp(connection->command, "LIST")) 00149 ftpServerProcessList(context, connection, p); 00150 //CWD command received? 00151 else if(!strcasecmp(connection->command, "CWD")) 00152 ftpServerProcessCwd(context, connection, p); 00153 //CDUP command received? 00154 else if(!strcasecmp(connection->command, "CDUP")) 00155 ftpServerProcessCdup(context, connection, p); 00156 //MKD command received? 00157 else if(!strcasecmp(connection->command, "MKD")) 00158 ftpServerProcessMkd(context, connection, p); 00159 //RMD command received? 00160 else if(!strcasecmp(connection->command, "RMD")) 00161 ftpServerProcessRmd(context, connection, p); 00162 //SIZE command received? 00163 else if(!strcasecmp(connection->command, "SIZE")) 00164 ftpServerProcessSize(context, connection, p); 00165 //RETR command received? 00166 else if(!strcasecmp(connection->command, "RETR")) 00167 ftpServerProcessRetr(context, connection, p); 00168 //STOR command received? 00169 else if(!strcasecmp(connection->command, "STOR")) 00170 ftpServerProcessStor(context, connection, p); 00171 //APPE command received? 00172 else if(!strcasecmp(connection->command, "APPE")) 00173 ftpServerProcessAppe(context, connection, p); 00174 //RNFR command received? 00175 else if(!strcasecmp(connection->command, "RNFR")) 00176 ftpServerProcessRnfr(context, connection, p); 00177 //RNTO command received? 00178 else if(!strcasecmp(connection->command, "RNTO")) 00179 ftpServerProcessRnto(context, connection, p); 00180 //DELE command received? 00181 else if(!strcasecmp(connection->command, "DELE")) 00182 ftpServerProcessDele(context, connection, p); 00183 //Unknown command received? 00184 else 00185 ftpServerProcessUnknownCmd(context, connection, p); 00186 } 00187 00188 //Debug message 00189 TRACE_DEBUG("FTP server: %s", connection->response); 00190 00191 //Number of bytes in the response buffer 00192 connection->responseLength = strlen(connection->response); 00193 connection->responsePos = 0; 00194 00195 //Clear command line 00196 connection->commandLength = 0; 00197 } 00198 else if(connection->commandLength >= FTP_SERVER_MAX_LINE_LEN) 00199 { 00200 //The command line is too long... 00201 connection->controlState = FTP_CONTROL_STATE_DISCARD; 00202 //Drop incoming data 00203 connection->commandLength = 0; 00204 } 00205 } 00206 00207 00208 /** 00209 * @brief Unknown command processing 00210 * @param[in] context Pointer to the FTP server context 00211 * @param[in] connection Pointer to the client connection 00212 * @param[in] param Command line parameters 00213 **/ 00214 00215 void ftpServerProcessUnknownCmd(FtpServerContext *context, 00216 FtpClientConnection *connection, char_t *param) 00217 { 00218 error_t error; 00219 00220 //Invoke user-defined callback, if any 00221 if(context->settings.unknownCommandCallback != NULL) 00222 { 00223 //Custom command processing 00224 error = context->settings.unknownCommandCallback(connection, 00225 connection->command, param); 00226 } 00227 else 00228 { 00229 //Report an error 00230 error = ERROR_INVALID_COMMAND; 00231 } 00232 00233 //Invalid command received? 00234 if(error == ERROR_INVALID_COMMAND) 00235 { 00236 //Format response message 00237 strcpy(connection->response, "500 Command unrecognized\r\n"); 00238 } 00239 } 00240 00241 00242 /** 00243 * @brief NOOP command processing 00244 * 00245 * The NOOP command does not affect any parameters or previously entered 00246 * commands. It specifies no action other than that the server send an OK reply 00247 * 00248 * @param[in] context Pointer to the FTP server context 00249 * @param[in] connection Pointer to the client connection 00250 * @param[in] param Command line parameters 00251 **/ 00252 00253 void ftpServerProcessNoop(FtpServerContext *context, 00254 FtpClientConnection *connection, char_t *param) 00255 { 00256 //Send an OK reply 00257 strcpy(connection->response, "200 Command okay\r\n"); 00258 } 00259 00260 00261 /** 00262 * @brief SYST command processing 00263 * 00264 * The SYST command is used to find out the type of operating system 00265 * at the server side 00266 * 00267 * @param[in] context Pointer to the FTP server context 00268 * @param[in] connection Pointer to the client connection 00269 * @param[in] param Command line parameters 00270 **/ 00271 00272 void ftpServerProcessSyst(FtpServerContext *context, 00273 FtpClientConnection *connection, char_t *param) 00274 { 00275 //Format the response to the SYST command 00276 strcpy(connection->response, "215 UNIX Type: L8\r\n"); 00277 } 00278 00279 00280 /** 00281 * @brief FEAT command processing 00282 * 00283 * The FEAT command allows a client to discover which optional 00284 * commands a server supports 00285 * 00286 * @param[in] context Pointer to the FTP server context 00287 * @param[in] connection Pointer to the client connection 00288 * @param[in] param Command line parameters 00289 **/ 00290 00291 void ftpServerProcessFeat(FtpServerContext *context, 00292 FtpClientConnection *connection, char_t *param) 00293 { 00294 //Format the response to the FEAT command 00295 strcpy(connection->response, "211-Features supported:\r\n"); 00296 strcat(connection->response, " SIZE\r\n"); 00297 strcat(connection->response, " EPRT\r\n"); 00298 strcat(connection->response, " EPSV\r\n"); 00299 strcat(connection->response, "211 End\r\n"); 00300 } 00301 00302 00303 /** 00304 * @brief TYPE command processing 00305 * 00306 * The TYPE command specifies the representation type 00307 * 00308 * @param[in] context Pointer to the FTP server context 00309 * @param[in] connection Pointer to the client connection 00310 * @param[in] param Command line parameters 00311 **/ 00312 00313 void ftpServerProcessType(FtpServerContext *context, 00314 FtpClientConnection *connection, char_t *param) 00315 { 00316 //The argument specifies the representation type 00317 if(*param != '\0') 00318 { 00319 //ASCII type? 00320 if(!strcasecmp(param, "A")) 00321 { 00322 //Format the response to the TYPE command 00323 strcpy(connection->response, "200 Type set to A\r\n"); 00324 } 00325 //Image type? 00326 else if(!strcasecmp(param, "I")) 00327 { 00328 //Format the response to the TYPE command 00329 strcpy(connection->response, "200 Type set to I\r\n"); 00330 } 00331 //Unknown type? 00332 else 00333 { 00334 //Report an error 00335 strcpy(connection->response, "504 Unknown type\r\n"); 00336 } 00337 } 00338 else 00339 { 00340 //The argument is missing... 00341 strcpy(connection->response, "501 Missing parameter\r\n"); 00342 } 00343 } 00344 00345 00346 /** 00347 * @brief STRU command processing 00348 * 00349 * The STRU command specifies the file structure 00350 * 00351 * @param[in] context Pointer to the FTP server context 00352 * @param[in] connection Pointer to the client connection 00353 * @param[in] param Command line parameters 00354 **/ 00355 00356 void ftpServerProcessStru(FtpServerContext *context, 00357 FtpClientConnection *connection, char_t *param) 00358 { 00359 //The argument specifies the file structure 00360 if(*param != '\0') 00361 { 00362 //No record structure? 00363 if(!strcasecmp(param, "F")) 00364 { 00365 //Format the response to the STRU command 00366 strcpy(connection->response, "200 Structure set to F\r\n"); 00367 } 00368 //Unknown file structure? 00369 else 00370 { 00371 //Report an error 00372 strcpy(connection->response, "504 Unknown structure\r\n"); 00373 } 00374 } 00375 else 00376 { 00377 //The argument is missing... 00378 strcpy(connection->response, "501 Missing parameter\r\n"); 00379 } 00380 } 00381 00382 00383 /** 00384 * @brief MODE command processing 00385 * 00386 * The MODE command specifies the data transfer mode 00387 * 00388 * @param[in] context Pointer to the FTP server context 00389 * @param[in] connection Pointer to the client connection 00390 * @param[in] param Command line parameters 00391 **/ 00392 00393 void ftpServerProcessMode(FtpServerContext *context, 00394 FtpClientConnection *connection, char_t *param) 00395 { 00396 //The argument specifies the data transfer mode 00397 if(*param != '\0') 00398 { 00399 //Stream mode? 00400 if(!strcasecmp(param, "S")) 00401 { 00402 //Format the response to the MODE command 00403 strcpy(connection->response, "200 Mode set to S\r\n"); 00404 } 00405 //Unknown data transfer mode? 00406 else 00407 { 00408 //Report an error 00409 strcpy(connection->response, "504 Unknown mode\r\n"); 00410 } 00411 } 00412 else 00413 { 00414 //The argument is missing... 00415 strcpy(connection->response, "501 Missing parameter\r\n"); 00416 } 00417 } 00418 00419 00420 /** 00421 * @brief USER command processing 00422 * 00423 * The USER command is used to identify the user 00424 * 00425 * @param[in] context Pointer to the FTP server context 00426 * @param[in] connection Pointer to the client connection 00427 * @param[in] param Command line parameters 00428 **/ 00429 00430 void ftpServerProcessUser(FtpServerContext *context, 00431 FtpClientConnection *connection, char_t *param) 00432 { 00433 uint_t status; 00434 00435 //The argument specifies the user name 00436 if(*param == '\0') 00437 { 00438 //The argument is missing... 00439 strcpy(connection->response, "501 Missing parameter\r\n"); 00440 //Exit immediately 00441 return; 00442 } 00443 00444 //Check the length of the user name 00445 if(strlen(param) > FTP_SERVER_MAX_USERNAME_LEN) 00446 { 00447 //The specified user name is not valid... 00448 strcpy(connection->response, "501 Invalid parameter\r\n"); 00449 //Exit immediately 00450 return; 00451 } 00452 00453 //Save user name 00454 strcpy(connection->user, param); 00455 //Log out the user 00456 connection->userLoggedIn = FALSE; 00457 //Set home directory 00458 strcpy(connection->homeDir, context->settings.rootDir); 00459 //Set current directory 00460 strcpy(connection->currentDir, context->settings.rootDir); 00461 00462 //Invoke user-defined callback, if any 00463 if(context->settings.checkUserCallback != NULL) 00464 status = context->settings.checkUserCallback(connection, param); 00465 else 00466 status = FTP_ACCESS_ALLOWED; 00467 00468 //Access allowed? 00469 if(status == FTP_ACCESS_ALLOWED) 00470 { 00471 //The user is now logged in 00472 connection->userLoggedIn = TRUE; 00473 //Format response message 00474 strcpy(connection->response, "230 User logged in, proceed\r\n"); 00475 } 00476 //Password required? 00477 else if(status == FTP_PASSWORD_REQUIRED) 00478 { 00479 //This command must be immediately followed by a PASS command 00480 connection->controlState = FTP_CONTROL_STATE_USER; 00481 //Format response message 00482 strcpy(connection->response, "331 User name okay, need password\r\n"); 00483 } 00484 //Access denied? 00485 else 00486 { 00487 //Format response message 00488 strcpy(connection->response, "530 Login authentication failed\r\n"); 00489 } 00490 } 00491 00492 00493 /** 00494 * @brief PASS command processing 00495 * 00496 * The USER command specifies the user's password 00497 * 00498 * @param[in] context Pointer to the FTP server context 00499 * @param[in] connection Pointer to the client connection 00500 * @param[in] param Command line parameters 00501 **/ 00502 00503 void ftpServerProcessPass(FtpServerContext *context, 00504 FtpClientConnection *connection, char_t *param) 00505 { 00506 uint_t status; 00507 00508 //This command must immediately follow a USER command 00509 if(connection->controlState != FTP_CONTROL_STATE_USER) 00510 { 00511 //Switch to idle state 00512 connection->controlState = FTP_CONTROL_STATE_IDLE; 00513 //Report an error 00514 strcpy(connection->response, "503 Bad sequence of commands\r\n"); 00515 //Exit immediately 00516 return; 00517 } 00518 00519 //Switch to idle state 00520 connection->controlState = FTP_CONTROL_STATE_IDLE; 00521 00522 //The argument specifies the password 00523 if(*param == '\0') 00524 { 00525 //The argument is missing... 00526 strcpy(connection->response, "501 Missing parameter\r\n"); 00527 //Exit immediately 00528 return; 00529 } 00530 00531 //Invoke user-defined callback, if any 00532 if(context->settings.checkPasswordCallback != NULL) 00533 status = context->settings.checkPasswordCallback(connection, connection->user, param); 00534 else 00535 status = FTP_ACCESS_ALLOWED; 00536 00537 //Access allowed? 00538 if(status == FTP_ACCESS_ALLOWED) 00539 { 00540 //The user is now logged in 00541 connection->userLoggedIn = TRUE; 00542 //Format response message 00543 strcpy(connection->response, "230 User logged in, proceed\r\n"); 00544 } 00545 //Access denied? 00546 else 00547 { 00548 //Format response message 00549 strcpy(connection->response, "530 Login authentication failed\r\n"); 00550 } 00551 } 00552 00553 00554 /** 00555 * @brief REIN command processing 00556 * 00557 * The REIN command is used to reinitialize a user session 00558 * 00559 * @param[in] context Pointer to the FTP server context 00560 * @param[in] connection Pointer to the client connection 00561 * @param[in] param Command line parameters 00562 **/ 00563 00564 void ftpServerProcessRein(FtpServerContext *context, 00565 FtpClientConnection *connection, char_t *param) 00566 { 00567 //Close data connection 00568 ftpServerCloseDataConnection(connection); 00569 00570 //Release previously allocated resources 00571 if(connection->file != NULL) 00572 { 00573 fsCloseFile(connection->file); 00574 connection->file = NULL; 00575 } 00576 if(connection->dir != NULL) 00577 { 00578 fsCloseDir(connection->dir); 00579 connection->dir = NULL; 00580 } 00581 00582 //Clear account information 00583 connection->userLoggedIn = FALSE; 00584 00585 //Format response message 00586 strcpy(connection->response, "220 Service ready for new user\r\n"); 00587 } 00588 00589 00590 /** 00591 * @brief QUIT command processing 00592 * 00593 * The QUIT command is used to terminate a user session 00594 * 00595 * @param[in] context Pointer to the FTP server context 00596 * @param[in] connection Pointer to the client connection 00597 * @param[in] param Command line parameters 00598 **/ 00599 00600 void ftpServerProcessQuit(FtpServerContext *context, 00601 FtpClientConnection *connection, char_t *param) 00602 { 00603 //There are two cases to consider upon receipt of this command 00604 if(connection->dataState == FTP_DATA_STATE_CLOSED) 00605 { 00606 //If the FTP service command was already completed, the server closes 00607 //the data connection (if it is open)... 00608 ftpServerCloseDataConnection(connection); 00609 00610 //...and responds with a 221 reply 00611 strcpy(connection->response, "221 Service closing control connection\r\n"); 00612 } 00613 else 00614 { 00615 //If the FTP service command is still in progress, the server aborts 00616 //the FTP service in progress and closes the data connection... 00617 ftpServerCloseDataConnection(connection); 00618 00619 //...returning a 426 reply to indicate that the service request 00620 //terminated abnormally 00621 strcpy(connection->response, "426 Connection closed; transfer aborted\r\n"); 00622 00623 //The server then sends a 221 reply 00624 strcat(connection->response, "221 Service closing control connection\r\n"); 00625 } 00626 00627 //Release previously allocated resources 00628 if(connection->file != NULL) 00629 { 00630 fsCloseFile(connection->file); 00631 connection->file = NULL; 00632 } 00633 if(connection->dir != NULL) 00634 { 00635 fsCloseDir(connection->dir); 00636 connection->dir = NULL; 00637 } 00638 00639 //Clear account information 00640 connection->userLoggedIn = FALSE; 00641 //Gracefully disconnect from the remote host 00642 connection->controlState = FTP_CONTROL_STATE_WAIT_ACK; 00643 } 00644 00645 00646 /** 00647 * @brief PORT command processing 00648 * 00649 * The PORT command specifies the data port to be used for the data connection 00650 * 00651 * @param[in] context Pointer to the FTP server context 00652 * @param[in] connection Pointer to the client connection 00653 * @param[in] param Command line parameters 00654 **/ 00655 00656 void ftpServerProcessPort(FtpServerContext *context, 00657 FtpClientConnection *connection, char_t *param) 00658 { 00659 error_t error; 00660 size_t i; 00661 size_t j; 00662 char_t *p; 00663 char_t *token; 00664 char_t *end; 00665 00666 //Ensure the user is logged in 00667 if(!connection->userLoggedIn) 00668 { 00669 //Format response message 00670 strcpy(connection->response, "530 Not logged in\r\n"); 00671 //Exit immediately 00672 return; 00673 } 00674 00675 //The argument is the concatenation of the IP address and the 16-bit port number 00676 if(*param == '\0') 00677 { 00678 //The argument is missing... 00679 strcpy(connection->response, "501 Missing parameter\r\n"); 00680 //Exit immediately 00681 return; 00682 } 00683 00684 //Close the data connection, if any 00685 ftpServerCloseDataConnection(connection); 00686 00687 //Start of exception handling block 00688 do 00689 { 00690 //Assume an error condition... 00691 error = ERROR_INVALID_SYNTAX; 00692 00693 //Parse the string 00694 for(i = 0, j = 1; param[i] != '\0'; i++) 00695 { 00696 //Change commas to dots 00697 if(param[i] == ',' && j < sizeof(Ipv4Addr)) 00698 { 00699 param[i] = '.'; 00700 j++; 00701 } 00702 } 00703 00704 //Get the IP address to be used 00705 token = strtok_r(param, ",", &p); 00706 //Syntax error? 00707 if(token == NULL) 00708 break; 00709 00710 //Convert the dot-decimal string to a binary IP address 00711 error = ipStringToAddr(token, &connection->remoteIpAddr); 00712 //Invalid IP address? 00713 if(error) 00714 break; 00715 00716 //Assume an error condition... 00717 error = ERROR_INVALID_SYNTAX; 00718 00719 //Get the most significant byte of the port number 00720 token = strtok_r(NULL, ",", &p); 00721 //Syntax error? 00722 if(token == NULL) 00723 break; 00724 00725 //Convert the string representation to integer 00726 connection->remotePort = strtoul(token, &end, 10) << 8; 00727 //Syntax error? 00728 if(*end != '\0') 00729 break; 00730 00731 //Get the least significant byte of the port number 00732 token = strtok_r(NULL, ",", &p); 00733 //Syntax error? 00734 if(token == NULL) 00735 break; 00736 00737 //Convert the string representation to integer 00738 connection->remotePort |= strtoul(token, &end, 10) & 0xFF; 00739 //Syntax error? 00740 if(*end != '\0') 00741 break; 00742 00743 //Successful processing 00744 error = NO_ERROR; 00745 00746 //End of exception handling block 00747 } while(0); 00748 00749 //Any error to report? 00750 if(error) 00751 { 00752 //Re initialize data connection 00753 connection->passiveMode = FALSE; 00754 connection->remotePort = 0; 00755 00756 //Format response message 00757 strcpy(connection->response, "501 Syntax error in parameters or arguments\r\n"); 00758 //Exit immediately 00759 return; 00760 } 00761 00762 //Successful processing 00763 strcpy(connection->response, "200 Command okay\r\n"); 00764 } 00765 00766 00767 /** 00768 * @brief EPRT command processing 00769 * 00770 * The EPRT command allows for the specification of an extended address 00771 * for the data connection 00772 * 00773 * @param[in] context Pointer to the FTP server context 00774 * @param[in] connection Pointer to the client connection 00775 * @param[in] param Command line parameters 00776 **/ 00777 00778 void ftpServerProcessEprt(FtpServerContext *context, 00779 FtpClientConnection *connection, char_t *param) 00780 { 00781 error_t error; 00782 uint_t protocol; 00783 char_t *p; 00784 char_t *token; 00785 char_t *end; 00786 char_t delimiter[2]; 00787 00788 //Ensure the user is logged in 00789 if(!connection->userLoggedIn) 00790 { 00791 //Format response message 00792 strcpy(connection->response, "530 Not logged in\r\n"); 00793 //Exit immediately 00794 return; 00795 } 00796 00797 //The extended address must consist of the network protocol 00798 //as well as the IP address and the 16-bit port number 00799 if(*param == '\0') 00800 { 00801 //The argument is missing... 00802 strcpy(connection->response, "501 Missing parameter\r\n"); 00803 //Exit immediately 00804 return; 00805 } 00806 00807 //Close the data connection, if any 00808 ftpServerCloseDataConnection(connection); 00809 00810 //Start of exception handling block 00811 do 00812 { 00813 //A delimiter character must be specified 00814 delimiter[0] = param[0]; 00815 delimiter[1] = '\0'; 00816 //Skip delimiter character 00817 param++; 00818 00819 //Assume an error condition... 00820 error = ERROR_INVALID_SYNTAX; 00821 00822 //Retrieve the network protocol to be used 00823 token = strtok_r(param, delimiter, &p); 00824 //Syntax error? 00825 if(token == NULL) 00826 break; 00827 00828 //Convert the string representation to integer 00829 protocol = strtoul(token, &end, 10); 00830 //Syntax error? 00831 if(*end != '\0') 00832 break; 00833 00834 //Get the IP address to be used 00835 token = strtok_r(NULL, delimiter, &p); 00836 //Syntax error? 00837 if(token == NULL) 00838 break; 00839 00840 #if (IPV4_SUPPORT == ENABLED) 00841 //IPv4 address family? 00842 if(protocol == 1) 00843 { 00844 //IPv4 addresses are 4-byte long 00845 connection->remoteIpAddr.length = sizeof(Ipv4Addr); 00846 //Convert the string to IPv4 address 00847 error = ipv4StringToAddr(token, &connection->remoteIpAddr.ipv4Addr); 00848 //Invalid IP address? 00849 if(error) 00850 break; 00851 } 00852 else 00853 #endif 00854 #if (IPV6_SUPPORT == ENABLED) 00855 //IPv6 address family? 00856 if(protocol == 2) 00857 { 00858 //IPv6 addresses are 16-byte long 00859 connection->remoteIpAddr.length = sizeof(Ipv6Addr); 00860 //Convert the string to IPv6 address 00861 error = ipv6StringToAddr(token, &connection->remoteIpAddr.ipv6Addr); 00862 //Invalid IP address? 00863 if(error) 00864 break; 00865 } 00866 else 00867 #endif 00868 //Unknown address family? 00869 { 00870 //Report an error 00871 error = ERROR_INVALID_ADDRESS; 00872 //Exit immediately 00873 break; 00874 } 00875 00876 //Assume an error condition... 00877 error = ERROR_INVALID_SYNTAX; 00878 00879 //Get the port number to be used 00880 token = strtok_r(NULL, delimiter, &p); 00881 //Syntax error? 00882 if(token == NULL) 00883 break; 00884 00885 //Convert the string representation to integer 00886 connection->remotePort = strtoul(token, &end, 10); 00887 //Syntax error? 00888 if(*end != '\0') 00889 break; 00890 00891 //Successful processing 00892 error = NO_ERROR; 00893 00894 //End of exception handling block 00895 } while(0); 00896 00897 //Any error to report? 00898 if(error) 00899 { 00900 //Re initialize data connection 00901 connection->passiveMode = FALSE; 00902 connection->remotePort = 0; 00903 00904 //Format response message 00905 strcpy(connection->response, "501 Syntax error in parameters or arguments\r\n"); 00906 //Exit immediately 00907 return; 00908 } 00909 00910 //Successful processing 00911 strcpy(connection->response, "200 Command okay\r\n"); 00912 } 00913 00914 00915 /** 00916 * @brief PASV command processing 00917 * 00918 * The PASV command requests the server to listen on a data port and 00919 * to wait for a connection rather than initiate one upon receipt of 00920 * a transfer command 00921 * 00922 * @param[in] context Pointer to the FTP server context 00923 * @param[in] connection Pointer to the client connection 00924 * @param[in] param Command line parameters 00925 **/ 00926 00927 void ftpServerProcessPasv(FtpServerContext *context, 00928 FtpClientConnection *connection, char_t *param) 00929 { 00930 error_t error; 00931 size_t n; 00932 IpAddr ipAddr; 00933 uint16_t port; 00934 00935 //Ensure the user is logged in 00936 if(!connection->userLoggedIn) 00937 { 00938 //Format response message 00939 strcpy(connection->response, "530 Not logged in\r\n"); 00940 //Exit immediately 00941 return; 00942 } 00943 00944 //Close the data connection, if any 00945 ftpServerCloseDataConnection(connection); 00946 00947 //Get the next passive port number to be used 00948 port = ftpServerGetPassivePort(context); 00949 00950 //Start of exception handling block 00951 do 00952 { 00953 //Open data socket 00954 connection->dataSocket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP); 00955 //Failed to open socket? 00956 if(!connection->dataSocket) 00957 { 00958 //Report an error 00959 error = ERROR_OPEN_FAILED; 00960 //Exit immediately 00961 break; 00962 } 00963 00964 //Force the socket to operate in non-blocking mode 00965 error = socketSetTimeout(connection->dataSocket, 0); 00966 //Any error to report? 00967 if(error) 00968 break; 00969 00970 //Change the size of the TX buffer 00971 error = socketSetTxBufferSize(connection->dataSocket, 00972 FTP_SERVER_DATA_SOCKET_BUFFER_SIZE); 00973 //Any error to report? 00974 if(error) 00975 break; 00976 00977 //Change the size of the RX buffer 00978 error = socketSetRxBufferSize(connection->dataSocket, 00979 FTP_SERVER_DATA_SOCKET_BUFFER_SIZE); 00980 //Any error to report? 00981 if(error) 00982 break; 00983 00984 //Associate the socket with the relevant interface 00985 error = socketBindToInterface(connection->dataSocket, connection->interface); 00986 //Unable to bind the socket to the desired interface? 00987 if(error) 00988 break; 00989 00990 //Bind the socket to the passive port number 00991 error = socketBind(connection->dataSocket, &IP_ADDR_ANY, port); 00992 //Failed to bind the socket to the desired port? 00993 if(error) 00994 break; 00995 00996 //Place the data socket in the listening state 00997 error = socketListen(connection->dataSocket, 1); 00998 //Any error to report? 00999 if(error) 01000 break; 01001 01002 //Retrieve local IP address 01003 error = socketGetLocalAddr(connection->controlSocket, &ipAddr, NULL); 01004 //Any error to report? 01005 if(error) 01006 break; 01007 01008 //The local IP address must be a valid IPv4 address 01009 if(ipAddr.length != sizeof(Ipv4Addr)) 01010 { 01011 //PASV command cannot be used on IPv6 connections 01012 error = ERROR_INVALID_ADDRESS; 01013 //Exit immediately 01014 break; 01015 } 01016 01017 //End of exception handling block 01018 } while(0); 01019 01020 //Any error to report? 01021 if(error) 01022 { 01023 //Clean up side effects 01024 ftpServerCloseDataConnection(connection); 01025 //Format response message 01026 strcpy(connection->response, "425 Can't enter passive mode\r\n"); 01027 //Exit immediately 01028 return; 01029 } 01030 01031 //Use passive data transfer 01032 connection->passiveMode = TRUE; 01033 //Update data connection state 01034 connection->dataState = FTP_DATA_STATE_LISTEN; 01035 01036 #if defined(FTP_SERVER_PASV_HOOK) 01037 FTP_SERVER_PASV_HOOK(connection, ipAddr); 01038 #endif 01039 01040 //Format response message 01041 n = sprintf(connection->response, "227 Entering passive mode ("); 01042 //Append host address 01043 ipAddrToString(&ipAddr, connection->response + n); 01044 01045 //Parse the resulting string 01046 for(n = 0; connection->response[n] != '\0'; n++) 01047 { 01048 //Change dots to commas 01049 if(connection->response[n] == '.') 01050 connection->response[n] = ','; 01051 } 01052 01053 //Append port number 01054 sprintf(connection->response + n, ",%" PRIu8 ",%" PRIu8 ")\r\n", MSB(port), LSB(port)); 01055 } 01056 01057 01058 /** 01059 * @brief EPSV command processing 01060 * 01061 * The EPSV command requests that a server listen on a data port and 01062 * wait for a connection 01063 * 01064 * @param[in] context Pointer to the FTP server context 01065 * @param[in] connection Pointer to the client connection 01066 * @param[in] param Command line parameters 01067 **/ 01068 01069 void ftpServerProcessEpsv(FtpServerContext *context, 01070 FtpClientConnection *connection, char_t *param) 01071 { 01072 error_t error; 01073 uint16_t port; 01074 01075 //Ensure the user is logged in 01076 if(!connection->userLoggedIn) 01077 { 01078 //Format response message 01079 strcpy(connection->response, "530 Not logged in\r\n"); 01080 //Exit immediately 01081 return; 01082 } 01083 01084 //Close the data connection, if any 01085 ftpServerCloseDataConnection(connection); 01086 01087 //Get the next passive port number to be used 01088 port = ftpServerGetPassivePort(context); 01089 01090 //Start of exception handling block 01091 do 01092 { 01093 //Open data socket 01094 connection->dataSocket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP); 01095 //Failed to open socket? 01096 if(!connection->dataSocket) 01097 { 01098 //Report an error 01099 error = ERROR_OPEN_FAILED; 01100 //Exit immediately 01101 break; 01102 } 01103 01104 //Force the socket to operate in non-blocking mode 01105 error = socketSetTimeout(connection->dataSocket, 0); 01106 //Any error to report? 01107 if(error) 01108 break; 01109 01110 //Change the size of the TX buffer 01111 error = socketSetTxBufferSize(connection->dataSocket, 01112 FTP_SERVER_DATA_SOCKET_BUFFER_SIZE); 01113 //Any error to report? 01114 if(error) 01115 break; 01116 01117 //Change the size of the RX buffer 01118 error = socketSetRxBufferSize(connection->dataSocket, 01119 FTP_SERVER_DATA_SOCKET_BUFFER_SIZE); 01120 //Any error to report? 01121 if(error) 01122 break; 01123 01124 //Associate the socket with the relevant interface 01125 error = socketBindToInterface(connection->dataSocket, connection->interface); 01126 //Unable to bind the socket to the desired interface? 01127 if(error) 01128 break; 01129 01130 //Bind the socket to the passive port number 01131 error = socketBind(connection->dataSocket, &IP_ADDR_ANY, port); 01132 //Failed to bind the socket to the desired port? 01133 if(error) 01134 break; 01135 01136 //Place the data socket in the listening state 01137 error = socketListen(connection->dataSocket, 1); 01138 //Any error to report? 01139 if(error) 01140 break; 01141 01142 //End of exception handling block 01143 } while(0); 01144 01145 //Any error to report? 01146 if(error) 01147 { 01148 //Clean up side effects 01149 ftpServerCloseDataConnection(connection); 01150 //Format response message 01151 strcpy(connection->response, "425 Can't enter passive mode\r\n"); 01152 //Exit immediately 01153 return; 01154 } 01155 01156 //Use passive data transfer 01157 connection->passiveMode = TRUE; 01158 //Update data connection state 01159 connection->dataState = FTP_DATA_STATE_LISTEN; 01160 01161 //The response code for entering passive mode using an extended address must be 229 01162 sprintf(connection->response, "229 Entering extended passive mode (|||%" PRIu16 "|)\r\n", 01163 port); 01164 } 01165 01166 01167 /** 01168 * @brief ABOR command processing 01169 * 01170 * The ABOR command tells the server to abort the previous FTP 01171 * service command and any associated transfer of data 01172 * 01173 * @param[in] context Pointer to the FTP server context 01174 * @param[in] connection Pointer to the client connection 01175 * @param[in] param Command line parameters 01176 **/ 01177 01178 void ftpServerProcessAbor(FtpServerContext *context, 01179 FtpClientConnection *connection, char_t *param) 01180 { 01181 //There are two cases to consider upon receipt of this command 01182 if(connection->dataState == FTP_DATA_STATE_CLOSED) 01183 { 01184 //If the FTP service command was already completed, the server closes 01185 //the data connection (if it is open)... 01186 ftpServerCloseDataConnection(connection); 01187 01188 //...and responds with a 226 reply, indicating that the abort command 01189 //was successfully processed 01190 strcpy(connection->response, "226 Abort command successful\r\n"); 01191 } 01192 else 01193 { 01194 //If the FTP service command is still in progress, the server aborts 01195 //the FTP service in progress and closes the data connection... 01196 ftpServerCloseDataConnection(connection); 01197 01198 //...returning a 426 reply to indicate that the service request 01199 //terminated abnormally 01200 strcpy(connection->response, "426 Connection closed; transfer aborted\r\n"); 01201 01202 //The server then sends a 226 reply, indicating that the abort command 01203 //was successfully processed 01204 strcat(connection->response, "226 Abort command successful\r\n"); 01205 } 01206 01207 //Release previously allocated resources 01208 if(connection->file != NULL) 01209 { 01210 fsCloseFile(connection->file); 01211 connection->file = NULL; 01212 } 01213 if(connection->dir != NULL) 01214 { 01215 fsCloseDir(connection->dir); 01216 connection->dir = NULL; 01217 } 01218 } 01219 01220 01221 /** 01222 * @brief PWD command processing 01223 * 01224 * The PWD command causes the name of the current working 01225 * directory to be returned in the reply 01226 * 01227 * @param[in] context Pointer to the FTP server context 01228 * @param[in] connection Pointer to the client connection 01229 * @param[in] param Command line parameters 01230 **/ 01231 01232 void ftpServerProcessPwd(FtpServerContext *context, 01233 FtpClientConnection *connection, char_t *param) 01234 { 01235 //Ensure the user is logged in 01236 if(!connection->userLoggedIn) 01237 { 01238 //Format response message 01239 strcpy(connection->response, "530 Not logged in\r\n"); 01240 //Exit immediately 01241 return; 01242 } 01243 01244 //A successful PWD command uses the 257 reply code 01245 sprintf(connection->response, "257 \"%s\" is current directory\r\n", 01246 ftpServerStripHomeDir(connection, connection->currentDir)); 01247 } 01248 01249 01250 /** 01251 * @brief CWD command processing 01252 * 01253 * The CWD command allows the user to work with a different 01254 * directory 01255 * 01256 * @param[in] context Pointer to the FTP server context 01257 * @param[in] connection Pointer to the client connection 01258 * @param[in] param Command line parameters 01259 **/ 01260 01261 void ftpServerProcessCwd(FtpServerContext *context, 01262 FtpClientConnection *connection, char_t *param) 01263 { 01264 error_t error; 01265 uint_t perm; 01266 01267 //Ensure the user is logged in 01268 if(!connection->userLoggedIn) 01269 { 01270 //Format response message 01271 strcpy(connection->response, "530 Not logged in\r\n"); 01272 //Exit immediately 01273 return; 01274 } 01275 01276 //The argument specifies the pathname 01277 if(*param == '\0') 01278 { 01279 //The argument is missing... 01280 strcpy(connection->response, "501 Missing parameter\r\n"); 01281 //Exit immediately 01282 return; 01283 } 01284 01285 //Retrieve the full pathname 01286 error = ftpServerGetPath(connection, param, 01287 connection->path, FTP_SERVER_MAX_PATH_LEN); 01288 01289 //Make sure the pathname is valid 01290 if(error) 01291 { 01292 //Report an error 01293 strcpy(connection->response, "501 Invalid parameter\r\n"); 01294 //Exit immediately 01295 return; 01296 } 01297 01298 //Retrieve permissions for the specified directory 01299 perm = ftpServerGetFilePermissions(context, connection, connection->path); 01300 01301 //Insufficient access rights? 01302 if(!(perm & FTP_FILE_PERM_READ)) 01303 { 01304 //Report an error 01305 strcpy(connection->response, "550 Access denied\r\n"); 01306 //Exit immediately 01307 return; 01308 } 01309 01310 //Make sure the specified directory exists 01311 if(!fsDirExists(connection->path)) 01312 { 01313 //Report an error 01314 strcpy(connection->response, "550 Directory not found\r\n"); 01315 //Exit immediately 01316 return; 01317 } 01318 01319 //Change current working directory 01320 strcpy(connection->currentDir, connection->path); 01321 01322 //A successful PWD command uses the 250 reply code 01323 sprintf(connection->response, "250 Directory changed to %s\r\n", 01324 ftpServerStripHomeDir(connection, connection->currentDir)); 01325 } 01326 01327 01328 /** 01329 * @brief CDUP command processing 01330 * 01331 * The CDUP command allows the user to change to the parent directory 01332 * 01333 * @param[in] context Pointer to the FTP server context 01334 * @param[in] connection Pointer to the client connection 01335 * @param[in] param Command line parameters 01336 **/ 01337 01338 void ftpServerProcessCdup(FtpServerContext *context, 01339 FtpClientConnection *connection, char_t *param) 01340 { 01341 uint_t perm; 01342 01343 //Ensure the user is logged in 01344 if(!connection->userLoggedIn) 01345 { 01346 //Format response message 01347 strcpy(connection->response, "530 Not logged in\r\n"); 01348 //Exit immediately 01349 return; 01350 } 01351 01352 //Get current directory 01353 strcpy(connection->path, connection->currentDir); 01354 01355 //Change to the parent directory 01356 pathCombine(connection->path, "..", FTP_SERVER_MAX_PATH_LEN); 01357 pathCanonicalize(connection->path); 01358 01359 //Retrieve permissions for the directory 01360 perm = ftpServerGetFilePermissions(context, connection, connection->path); 01361 01362 //Check access rights 01363 if(perm & FTP_FILE_PERM_READ) 01364 { 01365 //Update current directory 01366 strcpy(connection->currentDir, connection->path); 01367 } 01368 01369 //A successful PWD command uses the 250 reply code 01370 sprintf(connection->response, "250 Directory changed to %s\r\n", 01371 ftpServerStripHomeDir(connection, connection->currentDir)); 01372 } 01373 01374 01375 /** 01376 * @brief LIST command processing 01377 * 01378 * The LIST command is used to list the content of a directory 01379 * 01380 * @param[in] context Pointer to the FTP server context 01381 * @param[in] connection Pointer to the client connection 01382 * @param[in] param Command line parameters 01383 **/ 01384 01385 void ftpServerProcessList(FtpServerContext *context, 01386 FtpClientConnection *connection, char_t *param) 01387 { 01388 error_t error; 01389 uint_t perm; 01390 01391 //Ensure the user is logged in 01392 if(!connection->userLoggedIn) 01393 { 01394 //Format response message 01395 strcpy(connection->response, "530 Not logged in\r\n"); 01396 //Exit immediately 01397 return; 01398 } 01399 01400 //Any option flags 01401 while(*param == '-') 01402 { 01403 //Skip option flags 01404 while(*param != ' ' && *param != '\0') 01405 param++; 01406 //Skip whitespace characters 01407 while(*param == ' ') 01408 param++; 01409 } 01410 01411 //The pathname is optional 01412 if(*param == '\0') 01413 { 01414 //Use current directory if no pathname is specified 01415 strcpy(connection->path, connection->currentDir); 01416 } 01417 else 01418 { 01419 //Retrieve the full pathname 01420 error = ftpServerGetPath(connection, param, 01421 connection->path, FTP_SERVER_MAX_PATH_LEN); 01422 01423 //Any error to report? 01424 if(error) 01425 { 01426 //The specified pathname is not valid... 01427 strcpy(connection->response, "501 Invalid parameter\r\n"); 01428 //Exit immediately 01429 return; 01430 } 01431 } 01432 01433 //Retrieve permissions for the specified directory 01434 perm = ftpServerGetFilePermissions(context, connection, connection->path); 01435 01436 //Insufficient access rights? 01437 if(!(perm & FTP_FILE_PERM_READ)) 01438 { 01439 //Report an error 01440 strcpy(connection->response, "550 Access denied\r\n"); 01441 //Exit immediately 01442 return; 01443 } 01444 01445 //Open specified directory for reading 01446 connection->dir = fsOpenDir(connection->path); 01447 01448 //Failed to open the directory? 01449 if(!connection->dir) 01450 { 01451 //Report an error 01452 strcpy(connection->response, "550 Directory not found\r\n"); 01453 //Exit immediately 01454 return; 01455 } 01456 01457 //Check current data transfer mode 01458 if(connection->passiveMode) 01459 { 01460 //Check whether the data connection is already opened 01461 if(connection->dataState == FTP_DATA_STATE_IDLE) 01462 connection->dataState = FTP_DATA_STATE_SEND; 01463 } 01464 else 01465 { 01466 //Open the data connection 01467 error = ftpServerOpenDataConnection(context, connection); 01468 01469 //Any error to report? 01470 if(error) 01471 { 01472 //Clean up side effects 01473 fsCloseDir(connection->dir); 01474 //Format response 01475 strcpy(connection->response, "450 Can't open data connection\r\n"); 01476 //Exit immediately 01477 return; 01478 } 01479 01480 //The data connection is ready to send data 01481 connection->dataState = FTP_DATA_STATE_SEND; 01482 } 01483 01484 //Flush transmission buffer 01485 connection->bufferLength = 0; 01486 connection->bufferPos = 0; 01487 01488 //LIST command is being processed 01489 connection->controlState = FTP_CONTROL_STATE_LIST; 01490 01491 //Format response message 01492 strcpy(connection->response, "150 Opening data connection\r\n"); 01493 } 01494 01495 01496 /** 01497 * @brief MKD command processing 01498 * 01499 * The MKD command causes the directory specified in the pathname 01500 * to be created as a directory 01501 * 01502 * @param[in] context Pointer to the FTP server context 01503 * @param[in] connection Pointer to the client connection 01504 * @param[in] param Command line parameters 01505 **/ 01506 01507 void ftpServerProcessMkd(FtpServerContext *context, 01508 FtpClientConnection *connection, char_t *param) 01509 { 01510 error_t error; 01511 uint_t perm; 01512 01513 //Ensure the user is logged in 01514 if(!connection->userLoggedIn) 01515 { 01516 //Format response message 01517 strcpy(connection->response, "530 Not logged in\r\n"); 01518 //Exit immediately 01519 return; 01520 } 01521 01522 //The argument specifies the pathname 01523 if(*param == '\0') 01524 { 01525 //The argument is missing... 01526 strcpy(connection->response, "501 Missing parameter\r\n"); 01527 //Exit immediately 01528 return; 01529 } 01530 01531 //Retrieve the full pathname 01532 error = ftpServerGetPath(connection, param, 01533 connection->path, FTP_SERVER_MAX_PATH_LEN); 01534 01535 //Any error to report? 01536 if(error) 01537 { 01538 //The specified pathname is not valid... 01539 strcpy(connection->response, "501 Invalid parameter\r\n"); 01540 //Exit immediately 01541 return; 01542 } 01543 01544 //Retrieve permissions for the specified directory 01545 perm = ftpServerGetFilePermissions(context, connection, connection->path); 01546 01547 //Insufficient access rights? 01548 if(!(perm & FTP_FILE_PERM_WRITE)) 01549 { 01550 //Report an error 01551 strcpy(connection->response, "550 Access denied\r\n"); 01552 //Exit immediately 01553 return; 01554 } 01555 01556 //Create the specified directory 01557 error = fsCreateDir(connection->path); 01558 01559 //Any error to report? 01560 if(error) 01561 { 01562 //The specified pathname is not valid... 01563 strcpy(connection->response, "550 Can't create directory\r\n"); 01564 //Exit immediately 01565 return; 01566 } 01567 01568 //The specified directory was successfully created 01569 sprintf(connection->response, "257 \"%s\" created\r\n", 01570 ftpServerStripHomeDir(connection, connection->path)); 01571 } 01572 01573 01574 /** 01575 * @brief RMD command processing 01576 * 01577 * The RMD command causes the directory specified in the pathname 01578 * to be removed 01579 * 01580 * @param[in] context Pointer to the FTP server context 01581 * @param[in] connection Pointer to the client connection 01582 * @param[in] param Command line parameters 01583 **/ 01584 01585 void ftpServerProcessRmd(FtpServerContext *context, 01586 FtpClientConnection *connection, char_t *param) 01587 { 01588 error_t error; 01589 uint_t perm; 01590 01591 //Ensure the user is logged in 01592 if(!connection->userLoggedIn) 01593 { 01594 //Format response message 01595 strcpy(connection->response, "530 Not logged in\r\n"); 01596 //Exit immediately 01597 return; 01598 } 01599 01600 //The argument specifies the directory to be removed 01601 if(*param == '\0') 01602 { 01603 //The argument is missing... 01604 strcpy(connection->response, "501 Missing parameter\r\n"); 01605 //Exit immediately 01606 return; 01607 } 01608 01609 //Retrieve the full pathname of the directory 01610 error = ftpServerGetPath(connection, param, 01611 connection->path, FTP_SERVER_MAX_PATH_LEN); 01612 01613 //Any error to report? 01614 if(error) 01615 { 01616 //The specified pathname is not valid... 01617 strcpy(connection->response, "501 Invalid parameter\r\n"); 01618 //Exit immediately 01619 return; 01620 } 01621 01622 //Retrieve permissions for the specified directory 01623 perm = ftpServerGetFilePermissions(context, connection, connection->path); 01624 01625 //Insufficient access rights? 01626 if(!(perm & FTP_FILE_PERM_WRITE)) 01627 { 01628 //Report an error 01629 strcpy(connection->response, "550 Access denied\r\n"); 01630 //Exit immediately 01631 return; 01632 } 01633 01634 //Remove the specified directory 01635 error = fsRemoveDir(connection->path); 01636 01637 //Any error to report? 01638 if(error) 01639 { 01640 //The specified directory cannot be deleted... 01641 strcpy(connection->response, "550 Can't remove directory\r\n"); 01642 //Exit immediately 01643 return; 01644 } 01645 01646 //The specified directory was successfully removed 01647 strcpy(connection->response, "250 Directory removed\r\n"); 01648 } 01649 01650 01651 /** 01652 * @brief SIZE command processing 01653 * 01654 * The SIZE command is used to obtain the transfer size of the specified file 01655 * 01656 * @param[in] context Pointer to the FTP server context 01657 * @param[in] connection Pointer to the client connection 01658 * @param[in] param Command line parameters 01659 **/ 01660 01661 void ftpServerProcessSize(FtpServerContext *context, 01662 FtpClientConnection *connection, char_t *param) 01663 { 01664 error_t error; 01665 uint_t perm; 01666 uint32_t size; 01667 01668 //Ensure the user is logged in 01669 if(!connection->userLoggedIn) 01670 { 01671 //Format response message 01672 strcpy(connection->response, "530 Not logged in\r\n"); 01673 //Exit immediately 01674 return; 01675 } 01676 01677 //The argument specifies the pathname of the file 01678 if(*param == '\0') 01679 { 01680 //The argument is missing... 01681 strcpy(connection->response, "501 Missing parameter\r\n"); 01682 //Exit immediately 01683 return; 01684 } 01685 01686 //Retrieve the full pathname 01687 error = ftpServerGetPath(connection, param, 01688 connection->path, FTP_SERVER_MAX_PATH_LEN); 01689 01690 //Any error to report? 01691 if(error) 01692 { 01693 //The specified pathname is not valid... 01694 strcpy(connection->response, "501 Invalid parameter\r\n"); 01695 //Exit immediately 01696 return; 01697 } 01698 01699 //Retrieve permissions for the specified directory 01700 perm = ftpServerGetFilePermissions(context, connection, connection->path); 01701 01702 //Insufficient access rights? 01703 if(!(perm & FTP_FILE_PERM_LIST) && !(perm & FTP_FILE_PERM_READ)) 01704 { 01705 //Report an error 01706 strcpy(connection->response, "550 Access denied\r\n"); 01707 //Exit immediately 01708 return; 01709 } 01710 01711 //Retrieve the size of the specified file 01712 error = fsGetFileSize(connection->path, &size); 01713 01714 //Any error to report? 01715 if(error) 01716 { 01717 //Report an error 01718 strcpy(connection->response, "550 File not found\r\n"); 01719 //Exit immediately 01720 return; 01721 } 01722 01723 //Format response message 01724 sprintf(connection->response, "213 %" PRIu32 "\r\n", size); 01725 } 01726 01727 01728 /** 01729 * @brief RETR command processing 01730 * 01731 * The RETR command is used to retrieve the content of the specified file 01732 * 01733 * @param[in] context Pointer to the FTP server context 01734 * @param[in] connection Pointer to the client connection 01735 * @param[in] param Command line parameters 01736 **/ 01737 01738 void ftpServerProcessRetr(FtpServerContext *context, 01739 FtpClientConnection *connection, char_t *param) 01740 { 01741 error_t error; 01742 uint_t perm; 01743 01744 //Ensure the user is logged in 01745 if(!connection->userLoggedIn) 01746 { 01747 //Format response message 01748 strcpy(connection->response, "530 Not logged in\r\n"); 01749 //Exit immediately 01750 return; 01751 } 01752 01753 //The argument specifies the pathname of the file to read 01754 if(*param == '\0') 01755 { 01756 //The argument is missing... 01757 strcpy(connection->response, "501 Missing parameter\r\n"); 01758 //Exit immediately 01759 return; 01760 } 01761 01762 //Retrieve the full pathname 01763 error = ftpServerGetPath(connection, param, 01764 connection->path, FTP_SERVER_MAX_PATH_LEN); 01765 01766 //Any error to report? 01767 if(error) 01768 { 01769 //The specified pathname is not valid... 01770 strcpy(connection->response, "501 Invalid parameter\r\n"); 01771 //Exit immediately 01772 return; 01773 } 01774 01775 //Retrieve permissions for the specified directory 01776 perm = ftpServerGetFilePermissions(context, connection, connection->path); 01777 01778 //Insufficient access rights? 01779 if(!(perm & FTP_FILE_PERM_READ)) 01780 { 01781 //Report an error 01782 strcpy(connection->response, "550 Access denied\r\n"); 01783 //Exit immediately 01784 return; 01785 } 01786 01787 //Open specified file for reading 01788 connection->file = fsOpenFile(connection->path, FS_FILE_MODE_READ); 01789 01790 //Failed to open the file? 01791 if(!connection->file) 01792 { 01793 //Report an error 01794 strcpy(connection->response, "550 File not found\r\n"); 01795 //Exit immediately 01796 return; 01797 } 01798 01799 //Check current data transfer mode 01800 if(connection->passiveMode) 01801 { 01802 //Check whether the data connection is already opened 01803 if(connection->dataState == FTP_DATA_STATE_IDLE) 01804 connection->dataState = FTP_DATA_STATE_SEND; 01805 } 01806 else 01807 { 01808 //Open the data connection 01809 error = ftpServerOpenDataConnection(context, connection); 01810 01811 //Any error to report? 01812 if(error) 01813 { 01814 //Clean up side effects 01815 fsCloseFile(connection->file); 01816 //Format response 01817 strcpy(connection->response, "450 Can't open data connection\r\n"); 01818 //Exit immediately 01819 return; 01820 } 01821 01822 //The data connection is ready to send data 01823 connection->dataState = FTP_DATA_STATE_SEND; 01824 } 01825 01826 //Flush transmission buffer 01827 connection->bufferLength = 0; 01828 connection->bufferPos = 0; 01829 01830 //RETR command is being processed 01831 connection->controlState = FTP_CONTROL_STATE_RETR; 01832 01833 //Format response message 01834 strcpy(connection->response, "150 Opening data connection\r\n"); 01835 } 01836 01837 01838 /** 01839 * @brief STOR command processing 01840 * 01841 * The STOR command is used to store data to the specified file 01842 * 01843 * @param[in] context Pointer to the FTP server context 01844 * @param[in] connection Pointer to the client connection 01845 * @param[in] param Command line parameters 01846 **/ 01847 01848 void ftpServerProcessStor(FtpServerContext *context, 01849 FtpClientConnection *connection, char_t *param) 01850 { 01851 error_t error; 01852 uint_t perm; 01853 01854 //Ensure the user is logged in 01855 if(!connection->userLoggedIn) 01856 { 01857 //Format response message 01858 strcpy(connection->response, "530 Not logged in\r\n"); 01859 //Exit immediately 01860 return; 01861 } 01862 01863 //The argument specifies the pathname of the file to written 01864 if(*param == '\0') 01865 { 01866 //The argument is missing... 01867 strcpy(connection->response, "501 Missing parameter\r\n"); 01868 //Exit immediately 01869 return; 01870 } 01871 01872 //Retrieve the full pathname 01873 error = ftpServerGetPath(connection, param, 01874 connection->path, FTP_SERVER_MAX_PATH_LEN); 01875 01876 //Any error to report? 01877 if(error) 01878 { 01879 //The specified pathname is not valid... 01880 strcpy(connection->response, "501 Invalid parameter\r\n"); 01881 //Exit immediately 01882 return; 01883 } 01884 01885 //Retrieve permissions for the specified directory 01886 perm = ftpServerGetFilePermissions(context, connection, connection->path); 01887 01888 //Insufficient access rights? 01889 if(!(perm & FTP_FILE_PERM_WRITE)) 01890 { 01891 //Report an error 01892 strcpy(connection->response, "550 Access denied\r\n"); 01893 //Exit immediately 01894 return; 01895 } 01896 01897 //Open specified file for writing 01898 connection->file = fsOpenFile(connection->path, 01899 FS_FILE_MODE_WRITE | FS_FILE_MODE_CREATE | FS_FILE_MODE_TRUNC); 01900 01901 //Failed to open the file? 01902 if(!connection->file) 01903 { 01904 //Report an error 01905 strcpy(connection->response, "550 File not found\r\n"); 01906 //Exit immediately 01907 return; 01908 } 01909 01910 //Check current data transfer mode 01911 if(connection->passiveMode) 01912 { 01913 //Check whether the data connection is already opened 01914 if(connection->dataState == FTP_DATA_STATE_IDLE) 01915 connection->dataState = FTP_DATA_STATE_RECEIVE; 01916 } 01917 else 01918 { 01919 //Open the data connection 01920 error = ftpServerOpenDataConnection(context, connection); 01921 01922 //Any error to report? 01923 if(error) 01924 { 01925 //Clean up side effects 01926 fsCloseFile(connection->file); 01927 //Format response 01928 strcpy(connection->response, "450 Can't open data connection\r\n"); 01929 //Exit immediately 01930 return; 01931 } 01932 01933 //The data connection is ready to receive data 01934 connection->dataState = FTP_DATA_STATE_RECEIVE; 01935 } 01936 01937 //Flush reception buffer 01938 connection->bufferLength = 0; 01939 connection->bufferPos = 0; 01940 01941 //STOR command is being processed 01942 connection->controlState = FTP_CONTROL_STATE_STOR; 01943 01944 //Format response message 01945 strcpy(connection->response, "150 Opening data connection\r\n"); 01946 } 01947 01948 01949 /** 01950 * @brief APPE command processing 01951 * 01952 * The APPE command is used to append data to the specified file 01953 * 01954 * @param[in] context Pointer to the FTP server context 01955 * @param[in] connection Pointer to the client connection 01956 * @param[in] param Command line parameters 01957 **/ 01958 01959 void ftpServerProcessAppe(FtpServerContext *context, 01960 FtpClientConnection *connection, char_t *param) 01961 { 01962 error_t error; 01963 uint_t perm; 01964 01965 //Ensure the user is logged in 01966 if(!connection->userLoggedIn) 01967 { 01968 //Format response message 01969 strcpy(connection->response, "530 Not logged in\r\n"); 01970 //Exit immediately 01971 return; 01972 } 01973 01974 //The argument specifies the pathname of the file to written 01975 if(*param == '\0') 01976 { 01977 //The argument is missing... 01978 strcpy(connection->response, "501 Missing parameter\r\n"); 01979 //Exit immediately 01980 return; 01981 } 01982 01983 //Retrieve the full pathname 01984 error = ftpServerGetPath(connection, param, 01985 connection->path, FTP_SERVER_MAX_PATH_LEN); 01986 01987 //Any error to report? 01988 if(error) 01989 { 01990 //The specified pathname is not valid... 01991 strcpy(connection->response, "501 Invalid parameter\r\n"); 01992 //Exit immediately 01993 return; 01994 } 01995 01996 //Retrieve permissions for the specified directory 01997 perm = ftpServerGetFilePermissions(context, connection, connection->path); 01998 01999 //Insufficient access rights? 02000 if(!(perm & FTP_FILE_PERM_WRITE)) 02001 { 02002 //Report an error 02003 strcpy(connection->response, "550 Access denied\r\n"); 02004 //Exit immediately 02005 return; 02006 } 02007 02008 //Open specified file for writing 02009 connection->file = fsOpenFile(connection->path, 02010 FS_FILE_MODE_WRITE | FS_FILE_MODE_CREATE); 02011 02012 //Failed to open the file? 02013 if(!connection->file) 02014 { 02015 //Report an error 02016 strcpy(connection->response, "550 File not found\r\n"); 02017 //Exit immediately 02018 return; 02019 } 02020 02021 //Move to the end of the file 02022 error = fsSeekFile(connection->file, 0, FS_SEEK_END); 02023 02024 //Any error to report? 02025 if(error) 02026 { 02027 //Clean up side effects 02028 fsCloseFile(connection->file); 02029 //Format response 02030 strcpy(connection->response, "550 File unavailable\r\n"); 02031 } 02032 02033 //Check current data transfer mode 02034 if(connection->passiveMode) 02035 { 02036 //Check whether the data connection is already opened 02037 if(connection->dataState == FTP_DATA_STATE_IDLE) 02038 connection->dataState = FTP_DATA_STATE_RECEIVE; 02039 } 02040 else 02041 { 02042 //Open the data connection 02043 error = ftpServerOpenDataConnection(context, connection); 02044 02045 //Any error to report? 02046 if(error) 02047 { 02048 //Clean up side effects 02049 fsCloseFile(connection->file); 02050 //Format response 02051 strcpy(connection->response, "450 Can't open data connection\r\n"); 02052 //Exit immediately 02053 return; 02054 } 02055 02056 //The data connection is ready to receive data 02057 connection->dataState = FTP_DATA_STATE_RECEIVE; 02058 } 02059 02060 //Flush reception buffer 02061 connection->bufferLength = 0; 02062 connection->bufferPos = 0; 02063 02064 //APPE command is being processed 02065 connection->controlState = FTP_CONTROL_STATE_APPE; 02066 02067 //Format response message 02068 strcpy(connection->response, "150 Opening data connection\r\n"); 02069 } 02070 02071 02072 /** 02073 * @brief RNFR command processing 02074 * 02075 * The RNFR command specifies the old pathname of the file which is 02076 * to be renamed 02077 * 02078 * @param[in] context Pointer to the FTP server context 02079 * @param[in] connection Pointer to the client connection 02080 * @param[in] param Command line parameters 02081 **/ 02082 02083 void ftpServerProcessRnfr(FtpServerContext *context, 02084 FtpClientConnection *connection, char_t *param) 02085 { 02086 error_t error; 02087 uint_t perm; 02088 02089 //Ensure the user is logged in 02090 if(!connection->userLoggedIn) 02091 { 02092 //Format response message 02093 strcpy(connection->response, "530 Not logged in\r\n"); 02094 //Exit immediately 02095 return; 02096 } 02097 02098 //The argument specifies the file to be renamed 02099 if(*param == '\0') 02100 { 02101 //The argument is missing... 02102 strcpy(connection->response, "501 Missing parameter\r\n"); 02103 //Exit immediately 02104 return; 02105 } 02106 02107 //Retrieve the full pathname 02108 error = ftpServerGetPath(connection, param, 02109 connection->path, FTP_SERVER_MAX_PATH_LEN); 02110 02111 //Any error to report? 02112 if(error) 02113 { 02114 //The specified pathname is not valid... 02115 strcpy(connection->response, "501 Invalid parameter\r\n"); 02116 //Exit immediately 02117 return; 02118 } 02119 02120 //Retrieve permissions for the specified directory 02121 perm = ftpServerGetFilePermissions(context, connection, connection->path); 02122 02123 //Insufficient access rights? 02124 if(!(perm & FTP_FILE_PERM_WRITE)) 02125 { 02126 //Report an error 02127 strcpy(connection->response, "550 Access denied\r\n"); 02128 //Exit immediately 02129 return; 02130 } 02131 02132 //Make sure the file exists 02133 if(!fsFileExists(connection->path) && !fsDirExists(connection->path)) 02134 { 02135 //No such file or directory... 02136 strcpy(connection->response, "550 File not found\r\n"); 02137 //Exit immediately 02138 return; 02139 } 02140 02141 //This command must be immediately followed by a RNTO command 02142 connection->controlState = FTP_CONTROL_STATE_RNFR; 02143 //Format the response message 02144 strcpy(connection->response, "350 File exists, ready for destination name\r\n"); 02145 } 02146 02147 02148 /** 02149 * @brief RNTO command processing 02150 * 02151 * The RNTO command specifies the new pathname of the file specified 02152 * in the immediately preceding RNFR command 02153 * 02154 * @param[in] context Pointer to the FTP server context 02155 * @param[in] connection Pointer to the client connection 02156 * @param[in] param Command line parameters 02157 **/ 02158 02159 void ftpServerProcessRnto(FtpServerContext *context, 02160 FtpClientConnection *connection, char_t *param) 02161 { 02162 error_t error; 02163 uint_t perm; 02164 char_t newPath[FTP_SERVER_MAX_PATH_LEN]; 02165 02166 //Ensure the user is logged in 02167 if(!connection->userLoggedIn) 02168 { 02169 //Format response message 02170 strcpy(connection->response, "530 Not logged in\r\n"); 02171 //Exit immediately 02172 return; 02173 } 02174 02175 //This command must immediately follow a RNFR command 02176 if(connection->controlState != FTP_CONTROL_STATE_RNFR) 02177 { 02178 //Switch to idle state 02179 connection->controlState = FTP_CONTROL_STATE_IDLE; 02180 //Report an error 02181 strcpy(connection->response, "503 Bad sequence of commands\r\n"); 02182 //Exit immediately 02183 return; 02184 } 02185 02186 //Switch to idle state 02187 connection->controlState = FTP_CONTROL_STATE_IDLE; 02188 02189 //The argument specifies the new pathname 02190 if(*param == '\0') 02191 { 02192 //The argument is missing... 02193 strcpy(connection->response, "501 Missing parameter\r\n"); 02194 //Exit immediately 02195 return; 02196 } 02197 02198 //Retrieve the full pathname 02199 error = ftpServerGetPath(connection, param, 02200 newPath, FTP_SERVER_MAX_PATH_LEN); 02201 02202 //Any error to report? 02203 if(error) 02204 { 02205 //The specified pathname is not valid... 02206 strcpy(connection->response, "501 Invalid parameter\r\n"); 02207 //Exit immediately 02208 return; 02209 } 02210 02211 //Retrieve permissions for the specified directory 02212 perm = ftpServerGetFilePermissions(context, connection, newPath); 02213 02214 //Insufficient access rights? 02215 if(!(perm & FTP_FILE_PERM_WRITE)) 02216 { 02217 //Report an error 02218 strcpy(connection->response, "550 Access denied\r\n"); 02219 //Exit immediately 02220 return; 02221 } 02222 02223 //Check whether the file name already exists 02224 if(fsFileExists(newPath) || fsDirExists(newPath)) 02225 { 02226 //Report an error 02227 strcpy(connection->response, "550 File already exists\r\n"); 02228 //Exit immediately 02229 return; 02230 } 02231 02232 //Rename the specified file 02233 error = fsRenameFile(connection->path, newPath); 02234 02235 //Any error to report? 02236 if(error) 02237 { 02238 //The specified file cannot be renamed 02239 strcpy(connection->response, "550 Can't rename file\r\n"); 02240 //Exit immediately 02241 return; 02242 } 02243 02244 //The specified file was successfully deleted 02245 strcpy(connection->response, "250 File renamed\r\n"); 02246 } 02247 02248 02249 /** 02250 * @brief DELE command processing 02251 * 02252 * The DELE command causes the file specified in the pathname to be 02253 * deleted at the server site 02254 * 02255 * @param[in] context Pointer to the FTP server context 02256 * @param[in] connection Pointer to the client connection 02257 * @param[in] param Command line parameters 02258 **/ 02259 02260 void ftpServerProcessDele(FtpServerContext *context, 02261 FtpClientConnection *connection, char_t *param) 02262 { 02263 error_t error; 02264 uint_t perm; 02265 02266 //Ensure the user is logged in 02267 if(!connection->userLoggedIn) 02268 { 02269 //Format response message 02270 strcpy(connection->response, "530 Not logged in\r\n"); 02271 //Exit immediately 02272 return; 02273 } 02274 02275 //The argument specifies the file to be deleted 02276 if(*param == '\0') 02277 { 02278 //The argument is missing... 02279 strcpy(connection->response, "501 Missing parameter\r\n"); 02280 //Exit immediately 02281 return; 02282 } 02283 02284 //Retrieve the full pathname of the file 02285 error = ftpServerGetPath(connection, param, 02286 connection->path, FTP_SERVER_MAX_PATH_LEN); 02287 02288 //Any error to report? 02289 if(error) 02290 { 02291 //The specified pathname is not valid... 02292 strcpy(connection->response, "501 Invalid parameter\r\n"); 02293 //Exit immediately 02294 return; 02295 } 02296 02297 //Retrieve permissions for the specified directory 02298 perm = ftpServerGetFilePermissions(context, connection, connection->path); 02299 02300 //Insufficient access rights? 02301 if(!(perm & FTP_FILE_PERM_WRITE)) 02302 { 02303 //Report an error 02304 strcpy(connection->response, "550 Access denied\r\n"); 02305 //Exit immediately 02306 return; 02307 } 02308 02309 //Delete the specified file 02310 error = fsDeleteFile(connection->path); 02311 02312 //Any error to report? 02313 if(error) 02314 { 02315 //The specified file cannot be deleted... 02316 strcpy(connection->response, "550 Can't delete file\r\n"); 02317 //Exit immediately 02318 return; 02319 } 02320 02321 //The specified file was successfully deleted 02322 strcpy(connection->response, "250 File deleted\r\n"); 02323 } 02324 02325 #endif 02326
Generated on Tue Jul 12 2022 17:10:13 by
