Subversion Repositories HomeAutomation

Rev

Details | Last modification | View Log | SVN | RSS feed

Rev Author Line No. Line
363 johboh 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)