1 /*
2 * ftpsrvr.cxx
3 *
4 * FTP server class.
5 *
6 * Portable Windows Library
7 *
8 * Copyright (c) 1993-2002 Equivalence Pty. Ltd.
9 *
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
14 *
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * The Original Code is Portable Windows Library.
21 *
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
23 *
24 * Contributor(s): ______________________________________.
25 *
26 * $Revision: 20385 $
27 * $Author: rjongbloed $
28 * $Date: 2008-06-04 05:40:38 -0500 (Wed, 04 Jun 2008) $
29 */
30
31 #include <ptlib.h>
32 #include <ptlib/sockets.h>
33 #include <ptclib/ftp.h>
34
35 #define new PNEW
36
37
38 #define READY_STRING "PWLib FTP Server v1.0 ready"
39 #define GOOBYE_STRING "Goodbye"
40
41
42 /////////////////////////////////////////////////////////
43 // FTPServer
44
PFTPServer()45 PFTPServer::PFTPServer()
46 : readyString(PIPSocket::GetHostName() & READY_STRING)
47 {
48 Construct();
49 }
50
51
PFTPServer(const PString & readyStr)52 PFTPServer::PFTPServer(const PString & readyStr)
53 : readyString(readyStr)
54 {
55 Construct();
56 }
57
58
Construct()59 void PFTPServer::Construct()
60 {
61 thirdPartyPort = PFalse;
62 illegalPasswordCount = 0;
63 state = NotConnected;
64 type = 'A';
65 structure = 'F';
66 mode = 'S';
67 passiveSocket = NULL;
68 }
69
70
~PFTPServer()71 PFTPServer::~PFTPServer()
72 {
73 delete passiveSocket;
74 }
75
76
OnOpen()77 PBoolean PFTPServer::OnOpen()
78 {
79 // the default data port for a client is the same port
80 PIPSocket * socket = GetSocket();
81 if (socket == NULL)
82 return PFalse;
83
84 state = NeedUser;
85 if (!WriteResponse(220, readyString))
86 return PFalse;
87
88 socket->GetPeerAddress(remoteHost, remotePort);
89 return PTrue;
90 }
91
92
GetHelloString(const PString & user) const93 PString PFTPServer::GetHelloString(const PString & user) const
94 {
95 return PString("User") & user & "logged in.";
96 }
97
98
GetGoodbyeString(const PString &) const99 PString PFTPServer::GetGoodbyeString(const PString &) const
100 {
101 return PString(GOOBYE_STRING);
102 }
103
104
GetSystemTypeString() const105 PString PFTPServer::GetSystemTypeString() const
106 {
107 return PProcess::GetOSClass() + " " + PProcess::GetOSName() + " " + PProcess::GetOSVersion();
108 }
109
110
AuthoriseUser(const PString &,const PString &,PBoolean &)111 PBoolean PFTPServer::AuthoriseUser(const PString &, const PString &, PBoolean &)
112 {
113 return PTrue;
114 }
115
116
ProcessCommand()117 PBoolean PFTPServer::ProcessCommand()
118 {
119 PString args;
120 PINDEX code;
121 if (!ReadCommand(code, args))
122 return PFalse;
123
124 if (code == P_MAX_INDEX)
125 return OnUnknown(args);
126
127 // handle commands that require login
128 if (state == Connected || !CheckLoginRequired(code))
129 return DispatchCommand(code, args);
130
131 // otherwise enforce login
132 WriteResponse(530, "Please login with USER and PASS.");
133 return PTrue;
134 }
135
136
DispatchCommand(PINDEX code,const PString & args)137 PBoolean PFTPServer::DispatchCommand(PINDEX code, const PString & args)
138 {
139 switch (code) {
140
141 // mandatory commands
142 case USER:
143 return OnUSER(args);
144 case PASS:
145 return OnPASS(args);
146 case QUIT:
147 return OnQUIT(args);
148 case PORT:
149 return OnPORT(args);
150 case STRU:
151 return OnSTRU(args);
152 case MODE:
153 return OnMODE(args);
154 case NOOP:
155 return OnNOOP(args);
156 case TYPE:
157 return OnTYPE(args);
158 case RETR:
159 return OnRETR(args);
160 case STOR:
161 return OnSTOR(args);
162 case SYST:
163 return OnSYST(args);
164 case STATcmd:
165 return OnSTAT(args);
166 case ACCT:
167 return OnACCT(args);
168 case CWD:
169 return OnCWD(args);
170 case CDUP:
171 return OnCDUP(args);
172 case PASV:
173 return OnPASV(args);
174 case APPE:
175 return OnAPPE(args);
176 case RNFR:
177 return OnRNFR(args);
178 case RNTO:
179 return OnRNTO(args);
180 case DELE:
181 return OnDELE(args);
182 case RMD:
183 return OnRMD(args);
184 case MKD:
185 return OnMKD(args);
186 case PWD:
187 return OnPWD(args);
188 case LIST:
189 return OnLIST(args);
190 case NLST:
191 return OnNLST(args);
192
193 // optional commands
194 case HELP:
195 return OnHELP(args);
196 case SITE:
197 return OnSITE(args);
198 case ABOR:
199 return OnABOR(args);
200 case SMNT:
201 return OnSMNT(args);
202 case REIN:
203 return OnREIN(args);
204 case STOU:
205 return OnSTOU(args);
206 case ALLO:
207 return OnALLO(args);
208 case REST:
209 return OnREST(args);
210 default:
211 PAssertAlways("Registered FTP command not handled");
212 return PFalse;
213 }
214 return PTrue;
215 }
216
217
CheckLoginRequired(PINDEX cmd)218 PBoolean PFTPServer::CheckLoginRequired(PINDEX cmd)
219 {
220 static const BYTE RequiresLogin[NumCommands] = {
221 1, // USER
222 1, // PASS
223 0, // ACCT
224 0, // CWD
225 0, // CDUP
226 0, // SMNT
227 1, // QUIT
228 0, // REIN
229 1, // PORT
230 0, // PASV
231 1, // TYPE
232 1, // STRU
233 1, // MODE
234 0, // RETR
235 0, // STOR
236 0, // STOU
237 0, // APPE
238 0, // ALLO
239 0, // REST
240 0, // RNFR
241 0, // RNTO
242 1, // ABOR
243 0, // DELE
244 0, // RMD
245 0, // MKD
246 0, // PWD
247 0, // LIST
248 0, // NLST
249 1, // SITE
250 1, // SYST
251 1, // STAT
252 1, // HELP
253 1, // NOOP
254 };
255 if (cmd < NumCommands)
256 return RequiresLogin[cmd] == 0;
257 else
258 return PTrue;
259 }
260
261
OnUnknown(const PCaselessString & command)262 PBoolean PFTPServer::OnUnknown(const PCaselessString & command)
263 {
264 WriteResponse(500, "\"" + command + "\" command unrecognised.");
265 return PTrue;
266 }
267
268
OnError(PINDEX errorCode,PINDEX cmdNum,const char * msg)269 void PFTPServer::OnError(PINDEX errorCode, PINDEX cmdNum, const char * msg)
270 {
271 if (cmdNum < commandNames.GetSize())
272 WriteResponse(errorCode, "Command \"" + commandNames[cmdNum] + "\":" + msg);
273 else
274 WriteResponse(errorCode, msg);
275 }
276
277
OnNotImplemented(PINDEX cmdNum)278 void PFTPServer::OnNotImplemented(PINDEX cmdNum)
279 {
280 OnError(502, cmdNum, "not implemented");
281 }
282
283
OnSyntaxError(PINDEX cmdNum)284 void PFTPServer::OnSyntaxError(PINDEX cmdNum)
285 {
286 OnError(501, cmdNum, "syntax error in parameters or arguments.");
287 }
288
289
OnCommandSuccessful(PINDEX cmdNum)290 void PFTPServer::OnCommandSuccessful(PINDEX cmdNum)
291 {
292 if (cmdNum < commandNames.GetSize())
293 WriteResponse(200, "\"" + commandNames[cmdNum] + "\" command successful.");
294 }
295
296
297 // mandatory commands that can be performed without loggin in
298
OnUSER(const PCaselessString & args)299 PBoolean PFTPServer::OnUSER(const PCaselessString & args)
300 {
301 userName = args;
302 state = NeedPassword;
303 WriteResponse(331, "Password required for " + args + ".");
304 return PTrue;
305 }
306
307
OnPASS(const PCaselessString & args)308 PBoolean PFTPServer::OnPASS(const PCaselessString & args)
309 {
310 PBoolean replied = PFalse;
311 if (state != NeedPassword)
312 WriteResponse(503, "Login with USER first.");
313 else if (!AuthoriseUser(userName, args, replied)) {
314 if (!replied)
315 WriteResponse(530, "Login incorrect.");
316 if (illegalPasswordCount++ == MaxIllegalPasswords)
317 return PFalse;
318 } else {
319 if (!replied)
320 WriteResponse(230, GetHelloString(userName));
321 illegalPasswordCount = 0;
322 state = Connected;
323 }
324 return PTrue;
325 }
326
327
OnQUIT(const PCaselessString & userName)328 PBoolean PFTPServer::OnQUIT(const PCaselessString & userName)
329 {
330 WriteResponse(221, GetGoodbyeString(userName));
331 return PFalse;
332 }
333
334
OnPORT(const PCaselessString & args)335 PBoolean PFTPServer::OnPORT(const PCaselessString & args)
336 {
337 PStringArray tokens = args.Tokenise(",");
338
339 long values[6];
340 PINDEX len = PMIN(args.GetSize(), 6);
341
342 PINDEX i;
343 for (i = 0; i < len; i++) {
344 values[i] = tokens[i].AsInteger();
345 if (values[i] < 0 || values[i] > 255)
346 break;
347 }
348 if (i < 6)
349 OnSyntaxError(PORT);
350 else {
351 PIPSocket * socket = GetSocket();
352 if (socket == NULL)
353 OnError(590, PORT, "not available on non-TCP transport.");
354 else {
355 remoteHost = PIPSocket::Address((BYTE)values[0],
356 (BYTE)values[1], (BYTE)values[2], (BYTE)values[3]);
357 remotePort = (WORD)(values[4]*256 + values[5]);
358 if (remotePort < 1024 && remotePort != socket->GetPort()-1)
359 OnError(590, PORT, "cannot access privileged port number.");
360 else {
361 PIPSocket::Address controlHost;
362 GetSocket()->GetPeerAddress(controlHost);
363 if (thirdPartyPort || remoteHost == controlHost)
364 OnCommandSuccessful(PORT);
365 else
366 OnError(591, PORT, "three way transfer not allowed.");
367 }
368 }
369 }
370 return PTrue;
371 }
372
373
OnPASV(const PCaselessString &)374 PBoolean PFTPServer::OnPASV(const PCaselessString &)
375 {
376 if (passiveSocket != NULL)
377 delete passiveSocket;
378
379 passiveSocket = new PTCPSocket;
380 passiveSocket->Listen();
381
382 WORD portNo = passiveSocket->GetPort();
383 PIPSocket::Address ourAddr;
384 PIPSocket * socket = GetSocket();
385 if (socket != NULL)
386 socket->GetLocalAddress(ourAddr);
387 PString str(PString::Printf,
388 "Entering Passive Mode (%i,%i,%i,%i,%i,%i)",
389 ourAddr.Byte1(),
390 ourAddr.Byte2(),
391 ourAddr.Byte3(),
392 ourAddr.Byte4(),
393 portNo/256, portNo%256);
394
395 return WriteResponse(227, str);
396 }
397
398
OnTYPE(const PCaselessString & args)399 PBoolean PFTPServer::OnTYPE(const PCaselessString & args)
400 {
401 if (args.IsEmpty())
402 OnSyntaxError(TYPE);
403 else {
404 switch (toupper(args[0])) {
405 case 'A':
406 type = 'A';
407 break;
408 case 'I':
409 type = 'I';
410 break;
411 case 'E':
412 case 'L':
413 WriteResponse(504, PString("TYPE not implemented for parameter ") + args);
414 return PTrue;
415
416 default:
417 OnSyntaxError(TYPE);
418 return PTrue;
419 }
420 }
421 OnCommandSuccessful(TYPE);
422 return PTrue;
423 }
424
425
OnMODE(const PCaselessString & args)426 PBoolean PFTPServer::OnMODE(const PCaselessString & args)
427 {
428 if (args.IsEmpty())
429 OnSyntaxError(MODE);
430 else {
431 switch (toupper(args[0])) {
432 case 'S':
433 structure = 'S';
434 break;
435 case 'B':
436 case 'C':
437 WriteResponse(504, PString("MODE not implemented for parameter ") + args);
438 return PTrue;
439 default:
440 OnSyntaxError(MODE);
441 return PTrue;
442 }
443 }
444 OnCommandSuccessful(MODE);
445 return PTrue;
446 }
447
448
OnSTRU(const PCaselessString & args)449 PBoolean PFTPServer::OnSTRU(const PCaselessString & args)
450 {
451 if (args.IsEmpty())
452 OnSyntaxError(STRU);
453 else {
454 switch (toupper(args[0])) {
455 case 'F':
456 structure = 'F';
457 break;
458 case 'R':
459 case 'P':
460 WriteResponse(504, PString("STRU not implemented for parameter ") + args);
461 return PTrue;
462 default:
463 OnSyntaxError(STRU);
464 return PTrue;
465 }
466 }
467 OnCommandSuccessful(STRU);
468 return PTrue;
469 }
470
471
OnNOOP(const PCaselessString &)472 PBoolean PFTPServer::OnNOOP(const PCaselessString &)
473 {
474 OnCommandSuccessful(NOOP);
475 return PTrue;
476 }
477
478
479 // mandatory commands that cannot be performed without logging in
480
OnRETR(const PCaselessString &)481 PBoolean PFTPServer::OnRETR(const PCaselessString &)
482 {
483 OnNotImplemented(RETR);
484 return PTrue;
485 }
486
487
OnSTOR(const PCaselessString &)488 PBoolean PFTPServer::OnSTOR(const PCaselessString &)
489 {
490 OnNotImplemented(STOR);
491 return PTrue;
492 }
493
494
OnACCT(const PCaselessString &)495 PBoolean PFTPServer::OnACCT(const PCaselessString &)
496 {
497 WriteResponse(532, "Need account for storing files");
498 return PTrue;
499 }
500
501
OnCWD(const PCaselessString &)502 PBoolean PFTPServer::OnCWD(const PCaselessString &)
503 {
504 OnNotImplemented(CWD);
505 return PTrue;
506 }
507
508
OnCDUP(const PCaselessString &)509 PBoolean PFTPServer::OnCDUP(const PCaselessString &)
510 {
511 OnNotImplemented(CDUP);
512 return PTrue;
513 }
514
515
OnSMNT(const PCaselessString &)516 PBoolean PFTPServer::OnSMNT(const PCaselessString &)
517 {
518 OnNotImplemented(SMNT);
519 return PTrue;
520 }
521
522
OnREIN(const PCaselessString &)523 PBoolean PFTPServer::OnREIN(const PCaselessString &)
524 {
525 OnNotImplemented(REIN);
526 return PTrue;
527 }
528
529
OnSTOU(const PCaselessString &)530 PBoolean PFTPServer::OnSTOU(const PCaselessString &)
531 {
532 OnNotImplemented(STOU);
533 return PTrue;
534 }
535
536
OnAPPE(const PCaselessString &)537 PBoolean PFTPServer::OnAPPE(const PCaselessString &)
538 {
539 OnNotImplemented(APPE);
540 return PTrue;
541 }
542
543
OnALLO(const PCaselessString &)544 PBoolean PFTPServer::OnALLO(const PCaselessString &)
545 {
546 OnNotImplemented(ALLO);
547 return PTrue;
548 }
549
550
OnREST(const PCaselessString &)551 PBoolean PFTPServer::OnREST(const PCaselessString &)
552 {
553 OnNotImplemented(REST);
554 return PTrue;
555 }
556
557
OnRNFR(const PCaselessString &)558 PBoolean PFTPServer::OnRNFR(const PCaselessString &)
559 {
560 OnNotImplemented(RNFR);
561 return PTrue;
562 }
563
564
OnRNTO(const PCaselessString &)565 PBoolean PFTPServer::OnRNTO(const PCaselessString &)
566 {
567 OnNotImplemented(RNTO);
568 return PTrue;
569 }
570
571
OnABOR(const PCaselessString &)572 PBoolean PFTPServer::OnABOR(const PCaselessString &)
573 {
574 OnNotImplemented(ABOR);
575 return PTrue;
576 }
577
578
OnDELE(const PCaselessString &)579 PBoolean PFTPServer::OnDELE(const PCaselessString &)
580 {
581 OnNotImplemented(DELE);
582 return PTrue;
583 }
584
585
OnRMD(const PCaselessString &)586 PBoolean PFTPServer::OnRMD(const PCaselessString &)
587 {
588 OnNotImplemented(RMD);
589 return PTrue;
590 }
591
592
OnMKD(const PCaselessString &)593 PBoolean PFTPServer::OnMKD(const PCaselessString &)
594 {
595 OnNotImplemented(MKD);
596 return PTrue;
597 }
598
599
OnPWD(const PCaselessString &)600 PBoolean PFTPServer::OnPWD(const PCaselessString &)
601 {
602 OnNotImplemented(PWD);
603 return PTrue;
604 }
605
606
OnLIST(const PCaselessString &)607 PBoolean PFTPServer::OnLIST(const PCaselessString &)
608 {
609 OnNotImplemented(LIST);
610 return PTrue;
611 }
612
613
OnNLST(const PCaselessString &)614 PBoolean PFTPServer::OnNLST(const PCaselessString &)
615 {
616 OnNotImplemented(NLST);
617 return PTrue;
618 }
619
620
OnSITE(const PCaselessString &)621 PBoolean PFTPServer::OnSITE(const PCaselessString &)
622 {
623 OnNotImplemented(SITE);
624 return PTrue;
625 }
626
627
OnSYST(const PCaselessString &)628 PBoolean PFTPServer::OnSYST(const PCaselessString &)
629 {
630 WriteResponse(215, GetSystemTypeString());
631 return PTrue;
632 }
633
634
OnSTAT(const PCaselessString &)635 PBoolean PFTPServer::OnSTAT(const PCaselessString &)
636 {
637 OnNotImplemented(STATcmd);
638 return PTrue;
639 }
640
641
OnHELP(const PCaselessString &)642 PBoolean PFTPServer::OnHELP(const PCaselessString &)
643 {
644 OnNotImplemented(HELP);
645 return PTrue;
646 }
647
648
SendToClient(const PFilePath & filename)649 void PFTPServer::SendToClient(const PFilePath & filename)
650 {
651 if (!PFile::Exists(filename))
652 WriteResponse(450, filename + ": file not found");
653 else {
654 PTCPSocket * dataSocket;
655 if (passiveSocket != NULL) {
656 dataSocket = new PTCPSocket(*passiveSocket);
657 delete passiveSocket;
658 passiveSocket = NULL;
659 } else
660 dataSocket = new PTCPSocket(remoteHost, remotePort);
661 if (!dataSocket->IsOpen())
662 WriteResponse(425, "Cannot open data connection");
663 else {
664 if (type == 'A') {
665 PTextFile file(filename, PFile::ReadOnly);
666 if (!file.IsOpen())
667 WriteResponse(450, filename + ": cannot open file");
668 else {
669 PString fileSize(PString::Unsigned, file.GetLength());
670 WriteResponse(150, PString("Opening ASCII data connection for " + filename.GetFileName() + "(" + fileSize + " bytes)"));
671 PString line;
672 PBoolean ok = PTrue;
673 while (ok && file.ReadLine(line)) {
674 if (!dataSocket->Write((const char *)line, line.GetLength())) {
675 WriteResponse(426, "Connection closed - transfer aborted");
676 ok = PFalse;
677 }
678 }
679 file.Close();
680 }
681 } else {
682 PFile file(filename, PFile::ReadOnly);
683 if (!file.IsOpen())
684 WriteResponse(450, filename + ": cannot open file");
685 else {
686 PString fileSize(PString::Unsigned, file.GetLength());
687 WriteResponse(150, PString("Opening BINARY data connection for " + filename.GetFileName() + "(" + fileSize + " bytes)"));
688 BYTE buffer[2048];
689 PBoolean ok = PTrue;
690 while (ok && file.Read(buffer, 2048)) {
691 if (!dataSocket->Write(buffer, file.GetLastReadCount())) {
692 WriteResponse(426, "Connection closed - transfer aborted");
693 ok = PFalse;
694 }
695 }
696 file.Close();
697 }
698 }
699 delete dataSocket;
700 WriteResponse(226, "Transfer complete");
701 }
702 }
703 }
704
705
706 // End of File ///////////////////////////////////////////////////////////////
707