Subversion Repositories HomeAutomation

Rev

Rev 363 | Blame | Compare with Previous | Last modification | View Log | SVN | RSS feed

  1. /*********************************************************************
  2.  *
  3.  *                  FTP ServerModule for Microchip TCP/IP Stack
  4.  *
  5.  *********************************************************************
  6.  * FileName:        FTP.c
  7.  * Dependencies:    StackTsk.h
  8.  *                  TCP.h
  9.  * Processor:       PIC18, PIC24F, PIC24H, dsPIC30F, dsPIC33F
  10.  * Complier:        Microchip C18 v3.02 or higher
  11.  *                  Microchip C30 v2.01 or higher
  12.  * Company:         Microchip Technology, Inc.
  13.  *
  14.  * Software License Agreement
  15.  *
  16.  * This software is owned by Microchip Technology Inc. ("Microchip")
  17.  * and is supplied to you for use exclusively as described in the
  18.  * associated software agreement.  This software is protected by
  19.  * software and other intellectual property laws.  Any use in
  20.  * violation of the software license may subject the user to criminal
  21.  * sanctions as well as civil liability.  Copyright 2006 Microchip
  22.  * Technology Inc.  All rights reserved.
  23.  *
  24.  * This software is provided "AS IS."  MICROCHIP DISCLAIMS ALL
  25.  * WARRANTIES, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, NOT LIMITED
  26.  * TO MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
  27.  * INFRINGEMENT.  Microchip shall in no event be liable for special,
  28.  * incidental, or consequential damages.
  29.  *
  30.  *
  31.  * Author               Date    Comment
  32.  *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  33.  * Nilesh Rajbharti     4/23/01  Original        (Rev 1.0)
  34.  * Nilesh Rajbharti     11/13/02 Fixed FTPServer()
  35.  * Howard Schlunder     07/10/06 Added hash printing to FTP client
  36.  * Howard Schlunder     07/20/06 Added FTP_RESP_DATA_NO_SOCKET error message
  37.   ********************************************************************/
  38. #define THIS_IS_FTP
  39.  
  40. #include <string.h>
  41. #include <stdlib.h>
  42. #include "..\Include\FTP.h"
  43. #include "..\Include\TCP.h"
  44. #include "..\Include\Tick.h"
  45.  
  46. #include "..\Include\MPFS.h"
  47.  
  48. #if defined(STACK_USE_FTP_SERVER)
  49.  
  50.  
  51. #define FTP_COMMAND_PORT                (21)
  52. #define FTP_DATA_PORT                   (20)
  53. #define FTP_TIMEOUT                     (TICK)((TICK)180 * TICK_SECOND)
  54. #define MAX_FTP_ARGS                    (7)
  55. #define MAX_FTP_CMD_STRING_LEN          (31)
  56.  
  57. typedef enum _SM_FTP
  58. {
  59.     SM_FTP_NOT_CONNECTED,
  60.     SM_FTP_CONNECTED,
  61.     SM_FTP_USER_NAME,
  62.     SM_FTP_USER_PASS,
  63.     SM_FTP_RESPOND
  64. } SM_FTP;
  65.  
  66. typedef enum _SM_FTP_CMD
  67. {
  68.     SM_FTP_CMD_IDLE,
  69.     SM_FTP_CMD_WAIT,
  70.     SM_FTP_CMD_RECEIVE,
  71.     SM_FTP_CMD_WAIT_FOR_DISCONNECT
  72. } SM_FTP_CMD;
  73.  
  74. typedef enum _FTP_COMMAND
  75. {
  76.     FTP_CMD_USER,
  77.     FTP_CMD_PASS,
  78.     FTP_CMD_QUIT,
  79.     FTP_CMD_STOR,
  80.     FTP_CMD_PORT,
  81.     FTP_CMD_ABORT,
  82.     FTP_CMD_UNKNOWN,
  83.     FTP_CMD_NONE,
  84. } FTP_COMMAND;
  85.  
  86. // Each entry in following table must match with that of FTP_COMMAND enum.
  87. ROM char *FTPCommandString[] =
  88. {
  89.     "USER",                         // FTP_CMD_USER
  90.     "PASS",                         // FTP_CMD_PASS
  91.     "QUIT",                         // FTP_CMD_QUIT
  92.     "STOR",                         // FTP_CMD_STOR
  93.     "PORT",                         // FTP_CMD_PORT
  94.     "ABOR"                          // FTP_CMD_ABORT
  95. };
  96. #define FTP_COMMAND_TABLE_SIZE  ( sizeof(FTPCommandString)/sizeof(FTPCommandString[0]) )
  97.  
  98.  
  99. typedef enum _FTP_RESPONSE
  100. {
  101.     FTP_RESP_BANNER,
  102.     FTP_RESP_USER_OK,
  103.     FTP_RESP_PASS_OK,
  104.     FTP_RESP_QUIT_OK,
  105.     FTP_RESP_STOR_OK,
  106.     FTP_RESP_UNKNOWN,
  107.     FTP_RESP_LOGIN,
  108.     FTP_RESP_DATA_OPEN,
  109.     FTP_RESP_DATA_READY,
  110.     FTP_RESP_DATA_CLOSE,
  111.     FTP_RESP_DATA_NO_SOCKET,
  112.     FTP_RESP_OK,
  113.  
  114.     FTP_RESP_NONE                       // This must always be the last
  115.                                         // There is no corresponding string.
  116. } FTP_RESPONSE;
  117.  
  118. // Each entry in following table must match with FTP_RESPONE enum
  119. ROM char *FTPResponseString[] =
  120. {
  121.     "220 Ready\r\n",                    // FTP_RESP_BANNER
  122.     "331 Password required\r\n",        // FTP_RESP_USER_OK
  123.     "230 Logged in\r\n",                // FTP_RESP_PASS_OK
  124.     "221 Bye\r\n",                      // FTP_RESP_QUIT_OK
  125.     "500 \r\n",                         // FTP_RESP_STOR_OK
  126.     "502 Not implemented\r\n",          // FTP_RESP_UNKNOWN
  127.     "530 Login required\r\n",           // FTP_RESP_LOGIN
  128.     "150 Transferring data...\r\n",     // FTP_RESP_DATA_OPEN
  129.     "125 Done\r\n",                     // FTP_RESP_DATA_READY
  130.     "\r\n226 Transfer Complete\r\n",    // FTP_RESP_DATA_CLOSE
  131.     "425 Can't create data socket.  Increase MAX_SOCKETS.\r\n", //FTP_RESP_DATA_NO_SOCKET
  132.     "200 Ok\r\n"                        // FTP_RESP_OK
  133. };
  134.  
  135.  
  136. static union
  137. {
  138.     struct
  139.     {
  140.         unsigned char bUserSupplied : 1;
  141.         unsigned char bLoggedIn: 1;
  142.     } Bits;
  143.     BYTE Val;
  144. } FTPFlags;
  145.  
  146.  
  147. static TCP_SOCKET       FTPSocket;      // Main ftp command socket.
  148. static TCP_SOCKET       FTPDataSocket;  // ftp data socket.
  149. static WORD_VAL         FTPDataPort;    // ftp data port number as supplied by client
  150.  
  151. static SM_FTP           smFTP;          // ftp server FSM state
  152. static SM_FTP_CMD       smFTPCommand;   // ftp command FSM state
  153.  
  154. static FTP_COMMAND      FTPCommand;
  155. static FTP_RESPONSE     FTPResponse;
  156.  
  157. static char             FTPUser[FTP_USER_NAME_LEN];
  158. static char             FTPString[MAX_FTP_CMD_STRING_LEN+2];
  159. static BYTE             FTPStringLen;
  160. static char             *FTP_argv[MAX_FTP_ARGS];    // Parameters for a ftp command
  161. static BYTE             FTP_argc;       // Total number of params for a ftp command
  162. static TICK             lastActivity;   // Timeout keeper.
  163.  
  164.  
  165. static MPFS             FTPFileHandle;
  166.  
  167. // Private helper functions.
  168. static void ParseFTPString(void);
  169. static FTP_COMMAND ParseFTPCommand(char *cmd);
  170. static void ParseFTPString(void);
  171. static BOOL ExecuteFTPCommand(FTP_COMMAND cmd);
  172. static BOOL PutFile(void);
  173. static BOOL Quit(void);
  174.  
  175.  
  176. // Uncomment following line if ftp transactions are to be displayed on
  177. // RS-232 - for debug purpose only.
  178.  
  179. //#define FTP_SERVER_DEBUG_MODE
  180. #define FTP_PUT_ENABLED
  181.  
  182. #if defined(FTP_SERVER_DEBUG_MODE)
  183. static USARTPut(BYTE c)
  184. {
  185.     while( !TXSTA_TRMT);
  186.     TXREG = c;
  187. }
  188. #else
  189.  
  190. #define USARTPut(a)
  191.  
  192. #endif
  193.  
  194.  
  195. /*********************************************************************
  196.  * Function:        void FTPInit(void)
  197.  *
  198.  * PreCondition:    TCP module is already initialized.
  199.  *
  200.  * Input:           None
  201.  *
  202.  * Output:          None
  203.  *
  204.  * Side Effects:    None
  205.  *
  206.  * Overview:        Initializes internal variables of FTP
  207.  *
  208.  * Note:
  209.  ********************************************************************/
  210. void FTPInit(void)
  211. {
  212.     FTPSocket       = TCPListen(FTP_COMMAND_PORT);
  213.     smFTP           = SM_FTP_NOT_CONNECTED;
  214.     FTPStringLen    = 0;
  215.     FTPFlags.Val    = 0;
  216.     FTPDataPort.Val = FTP_DATA_PORT;
  217. }
  218.  
  219.  
  220. /*********************************************************************
  221.  * Function:        void FTPServer(void)
  222.  *
  223.  * PreCondition:    FTPInit() must already be called.
  224.  *
  225.  * Input:           None
  226.  *
  227.  * Output:          Opened FTP connections are served.
  228.  *
  229.  * Side Effects:    None
  230.  *
  231.  * Overview:
  232.  *
  233.  * Note:            This function acts as a task (similar to one in
  234.  *                  RTOS).  This function performs its task in
  235.  *                  co-operative manner.  Main application must call
  236.  *                  this function repeatdly to ensure all open
  237.  *                  or new connections are served on time.
  238.  ********************************************************************/
  239. BOOL FTPServer(void)
  240. {
  241.     BYTE v;
  242.     TICK currentTick;
  243.  
  244.     if ( !TCPIsConnected(FTPSocket) )
  245.     {
  246.         FTPStringLen    = 0;
  247.         FTPCommand      = FTP_CMD_NONE;
  248.         smFTP           = SM_FTP_NOT_CONNECTED;
  249.         FTPFlags.Val    = 0;
  250.         smFTPCommand    = SM_FTP_CMD_IDLE;
  251.         return TRUE;
  252.     }
  253.  
  254.     if ( TCPIsGetReady(FTPSocket) )
  255.     {
  256.         lastActivity    = TickGet();
  257.  
  258.         while( TCPGet(FTPSocket, &v ) )
  259.         {
  260.             USARTPut(v);
  261.             FTPString[FTPStringLen++]   = v;
  262.             if ( FTPStringLen == MAX_FTP_CMD_STRING_LEN )
  263.                 FTPStringLen            = 0;
  264.         }
  265.         TCPDiscard(FTPSocket);
  266.  
  267.  
  268.         if ( v == '\n' )
  269.         {
  270.             FTPString[FTPStringLen]     = '\0';
  271.             FTPStringLen                = 0;
  272.             ParseFTPString();
  273.             FTPCommand                  = ParseFTPCommand(FTP_argv[0]);
  274.         }
  275.     }
  276.     else if ( smFTP != SM_FTP_NOT_CONNECTED )
  277.     {
  278.         currentTick = TickGet();
  279.         currentTick = TickGetDiff(currentTick, lastActivity);
  280.         if ( currentTick >= FTP_TIMEOUT )
  281.         {
  282.             lastActivity                = TickGet();
  283.             FTPCommand                  = FTP_CMD_QUIT;
  284.             smFTP                       = SM_FTP_CONNECTED;
  285.         }
  286.     }
  287.  
  288.     switch(smFTP)
  289.     {
  290.     case SM_FTP_NOT_CONNECTED:
  291.         FTPResponse = FTP_RESP_BANNER;
  292.         lastActivity = TickGet();
  293.         /* No break - Continue... */
  294.  
  295.     case SM_FTP_RESPOND:
  296. SM_FTP_RESPOND_Label:
  297.         if(!TCPIsPutReady(FTPSocket))
  298.         {
  299.             return TRUE;
  300.         }
  301.         else
  302.         {
  303.             ROM char* pMsg;
  304.            
  305.             pMsg = FTPResponseString[FTPResponse];
  306.            
  307.             while( (v = *pMsg++) )
  308.             {
  309.                 USARTPut(v);
  310.                 TCPPut(FTPSocket, v);
  311.             }
  312.             TCPFlush(FTPSocket);
  313.             FTPResponse = FTP_RESP_NONE;
  314.             smFTP = SM_FTP_CONNECTED;
  315.         }
  316.         // No break - this will speed up little bit
  317.  
  318.     case SM_FTP_CONNECTED:
  319.         if ( FTPCommand != FTP_CMD_NONE )
  320.         {
  321.             if ( ExecuteFTPCommand(FTPCommand) )
  322.             {
  323.                 if ( FTPResponse != FTP_RESP_NONE )
  324.                     smFTP = SM_FTP_RESPOND;
  325.                 else if ( FTPCommand == FTP_CMD_QUIT )
  326.                     smFTP = SM_FTP_NOT_CONNECTED;
  327.  
  328.                 FTPCommand = FTP_CMD_NONE;
  329.                 smFTPCommand = SM_FTP_CMD_IDLE;
  330.             }
  331.             else if ( FTPResponse != FTP_RESP_NONE )
  332.             {
  333.                 smFTP = SM_FTP_RESPOND;
  334.                 goto SM_FTP_RESPOND_Label;
  335.             }
  336.         }
  337.         break;
  338.  
  339.  
  340.     }
  341.  
  342.     return TRUE;
  343. }
  344.  
  345. static BOOL ExecuteFTPCommand(FTP_COMMAND cmd)
  346. {
  347.     switch(cmd)
  348.     {
  349.     case FTP_CMD_USER:
  350.         FTPFlags.Bits.bUserSupplied = TRUE;
  351.         FTPFlags.Bits.bLoggedIn = FALSE;
  352.         FTPResponse = FTP_RESP_USER_OK;
  353.         strncpy(FTPUser, FTP_argv[1], sizeof(FTPUser));
  354.         break;
  355.  
  356.     case FTP_CMD_PASS:
  357.         if ( !FTPFlags.Bits.bUserSupplied )
  358.             FTPResponse = FTP_RESP_LOGIN;
  359.         else
  360.         {
  361.             if ( FTPVerify(FTPUser, FTP_argv[1]) )
  362.             {
  363.                 FTPFlags.Bits.bLoggedIn = TRUE;
  364.                 FTPResponse = FTP_RESP_PASS_OK;
  365.             }
  366.             else
  367.                 FTPResponse = FTP_RESP_LOGIN;
  368.         }
  369.         break;
  370.  
  371.     case FTP_CMD_QUIT:
  372.         return Quit();
  373.  
  374.     case FTP_CMD_PORT:
  375.         FTPDataPort.v[1] = (BYTE)atoi(FTP_argv[5]);
  376.         FTPDataPort.v[0] = (BYTE)atoi(FTP_argv[6]);
  377.         FTPResponse = FTP_RESP_OK;
  378.         break;
  379.  
  380.     case FTP_CMD_STOR:
  381.         return PutFile();
  382.  
  383.     case FTP_CMD_ABORT:
  384.         FTPResponse = FTP_RESP_OK;
  385.         if ( FTPDataSocket != INVALID_SOCKET )
  386.             TCPDisconnect(FTPDataSocket);
  387.         break;
  388.  
  389.     default:
  390.         FTPResponse = FTP_RESP_UNKNOWN;
  391.         break;
  392.     }
  393.     return TRUE;
  394. }
  395.  
  396. static BOOL Quit(void)
  397. {
  398.     switch(smFTPCommand)
  399.     {
  400.     case SM_FTP_CMD_IDLE:
  401. #if defined(FTP_PUT_ENABLED)
  402.         if ( smFTPCommand == SM_FTP_CMD_RECEIVE )
  403.             MPFSClose();
  404. #endif
  405.  
  406.         if ( FTPDataSocket != INVALID_SOCKET )
  407.         {
  408. #if defined(FTP_PUT_ENABLED)
  409.             MPFSClose();
  410. #endif
  411.             TCPDisconnect(FTPDataSocket);
  412.             smFTPCommand = SM_FTP_CMD_WAIT;
  413.         }
  414.         else
  415.             goto Quit_Done;
  416.         break;
  417.  
  418.     case SM_FTP_CMD_WAIT:
  419.         if ( !TCPIsConnected(FTPDataSocket) )
  420.         {
  421. Quit_Done:
  422.             FTPResponse = FTP_RESP_QUIT_OK;
  423.             smFTPCommand = SM_FTP_CMD_WAIT_FOR_DISCONNECT;
  424.         }
  425.         break;
  426.  
  427.     case SM_FTP_CMD_WAIT_FOR_DISCONNECT:
  428.         if ( TCPIsPutReady(FTPSocket) )
  429.         {
  430.             if ( TCPIsConnected(FTPSocket) )
  431.                 TCPDisconnect(FTPSocket);
  432.         }
  433.         break;
  434.  
  435.     }
  436.     return FALSE;
  437. }
  438.  
  439.  
  440. static BOOL PutFile(void)
  441. {
  442.     BYTE v;
  443.  
  444.  
  445.     switch(smFTPCommand)
  446.     {
  447.     case SM_FTP_CMD_IDLE:
  448.         if ( !FTPFlags.Bits.bLoggedIn )
  449.         {
  450.             FTPResponse     = FTP_RESP_LOGIN;
  451.             return TRUE;
  452.         }
  453.         else
  454.         {
  455.             FTPResponse     = FTP_RESP_DATA_OPEN;
  456.             FTPDataSocket   = TCPConnect(&REMOTE_HOST(FTPSocket), FTPDataPort.Val);
  457.  
  458.             // Make sure that a valid socket was available and returned
  459.             // If not, return with an error
  460.             if(FTPDataSocket != INVALID_SOCKET)
  461.             {
  462.                 smFTPCommand = SM_FTP_CMD_WAIT;
  463.             }
  464.             else
  465.             {
  466.                 FTPResponse = FTP_RESP_DATA_NO_SOCKET;
  467.                 return TRUE;
  468.             }
  469.         }
  470.         break;
  471.  
  472.     case SM_FTP_CMD_WAIT:
  473.         if ( TCPIsConnected(FTPDataSocket) )
  474.         {
  475. #if defined(FTP_PUT_ENABLED)
  476.             if ( !MPFSIsInUse() )
  477. #endif
  478.             {
  479. #if defined(FTP_PUT_ENABLED)
  480.                 FTPFileHandle   = MPFSFormat();
  481. #endif
  482.  
  483.                 smFTPCommand    = SM_FTP_CMD_RECEIVE;
  484.             }
  485.         }
  486.         break;
  487.  
  488.     case SM_FTP_CMD_RECEIVE:
  489.         if ( TCPIsGetReady(FTPDataSocket) )
  490.         {
  491.             // Reload timeout timer.
  492.             lastActivity    = TickGet();
  493.             MPFSPutBegin(FTPFileHandle);
  494.             while( TCPGet(FTPDataSocket, &v) )
  495.             {
  496.                 USARTPut(v);
  497.  
  498. #if defined(FTP_PUT_ENABLED)
  499.                 MPFSPut(v);
  500. #endif
  501.             }
  502.             FTPFileHandle = MPFSPutEnd();
  503.             TCPDiscard(FTPDataSocket);
  504.  
  505.             // Print hash characters on FTP client display
  506.             if(TCPIsPutReady(FTPSocket))
  507.             {
  508.                 TCPPut(FTPSocket, '#');
  509.                 TCPFlush(FTPSocket);
  510.             }
  511.  
  512.         }
  513.         else if ( !TCPIsConnected(FTPDataSocket) )
  514.         {
  515. #if defined(FTP_PUT_ENABLED)
  516.             MPFSPutEnd();
  517.             MPFSClose();
  518. #endif
  519.             TCPDisconnect(FTPDataSocket);
  520.             FTPDataSocket   = INVALID_SOCKET;
  521.             FTPResponse     = FTP_RESP_DATA_CLOSE;
  522.             return TRUE;
  523.         }
  524.     }
  525.     return FALSE;
  526. }
  527.  
  528.  
  529.  
  530. static FTP_COMMAND ParseFTPCommand(char *cmd)
  531. {
  532.     FTP_COMMAND i;
  533.  
  534.     for ( i = 0; i < (FTP_COMMAND)FTP_COMMAND_TABLE_SIZE; i++ )
  535.     {
  536.     if ( !memcmppgm2ram((void*)cmd, (ROM void*)FTPCommandString[i], 4) )
  537.             return i;
  538.     }
  539.  
  540.     return FTP_CMD_UNKNOWN;
  541. }
  542.  
  543. static void ParseFTPString(void)
  544. {
  545.     BYTE *p;
  546.     BYTE v;
  547.     enum { SM_FTP_PARSE_PARAM, SM_FTP_PARSE_SPACE } smParseFTP;
  548.  
  549.     smParseFTP  = SM_FTP_PARSE_PARAM;
  550.     p           = (BYTE*)&FTPString[0];
  551.  
  552.     // Skip white blanks
  553.     while( *p == ' ' )
  554.         p++;
  555.  
  556.     FTP_argv[0]  = (char*)p;
  557.     FTP_argc     = 1;
  558.  
  559.     while( (v = *p) )
  560.     {
  561.         switch(smParseFTP)
  562.         {
  563.         case SM_FTP_PARSE_PARAM:
  564.             if ( v == ' ' || v == ',' )
  565.             {
  566.                 *p = '\0';
  567.                 smParseFTP = SM_FTP_PARSE_SPACE;
  568.             }
  569.             else if ( v == '\r' || v == '\n' )
  570.                 *p = '\0';
  571.             break;
  572.  
  573.         case SM_FTP_PARSE_SPACE:
  574.             if ( v != ' ' )
  575.             {
  576.                 FTP_argv[FTP_argc++] = (char*)p;
  577.                 smParseFTP = SM_FTP_PARSE_PARAM;
  578.             }
  579.             break;
  580.         }
  581.         p++;
  582.     }
  583. }
  584.  
  585. #endif  // #if defined(STACK_USE_FTP_SERVER)
  586.