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