This source file includes following definitions.
- mLooper
- cd
- ls
- pwd
- connect
- putFile
- getFile
- moveFile
- makeDir
- setPassive
- p_testState
- p_setState
- p_clearState
- p_sendRequest
- p_getReplyLine
- p_getReply
- p_openDataConnection
- p_acceptDataConnection
1
2
3
4
5
6 #include <NetworkKit.h>
7 #include <File.h>
8 #include <stdio.h>
9 #include <String.h>
10 #include <stdlib.h>
11 #include "FtpClient.h"
12
13
14
15
16 #define IAC 255
17 #define DONT 254
18 #define DO 253
19 #define WONT 252
20 #define WILL 251
21 #define xEOF 236
22
23 FtpClient::FtpClient(BHandler *handler) :
24 m_state(0), m_control(NULL), m_data(NULL), mHandler(handler), mLooper(NULL)
25 {
26 if(mHandler)
27 mLooper = mHandler->Looper();
28 }
29
30
31
32 FtpClient::~FtpClient()
33 {
34 delete m_control;
35 delete m_data;
36 }
37
38
39 bool FtpClient::cd(const string &dir)
40 {
41 bool rc = false;
42 int code, codetype;
43 string cmd = "CWD ", replystr;
44
45
46 cmd += dir;
47
48 if(dir.length() == 0)
49 cmd += '/';
50
51 if(p_sendRequest(cmd) != true){
52 if(mHandler && mLooper){
53 BMessage sentMsg(CD_COMPLETE);
54 sentMsg.AddBool("success", rc);
55 sentMsg.AddString("pwd", dir.c_str());
56 mLooper->PostMessage(&sentMsg, mHandler);
57 }
58 return rc;
59 }
60
61 if(p_getReply(replystr, code, codetype) == true && codetype == 2)
62 rc = true;
63
64 if(mHandler && mLooper){
65 BMessage sentMsg(CD_COMPLETE);
66 sentMsg.AddBool("success", rc);
67 sentMsg.AddString("pwd", dir.c_str());
68 mLooper->PostMessage(&sentMsg, mHandler);
69 }
70 return rc;
71 }
72
73
74 bool FtpClient::ls(BString &listing)
75 {
76 bool rc = false;
77 string cmd, replystr;
78 int code, codetype, numread;
79 char buf[513];
80
81 cmd = "TYPE A";
82
83 listing.SetTo("");
84
85 if(p_sendRequest(cmd))
86 p_getReply(replystr, code, codetype);
87
88 if(!p_openDataConnection()) {
89 delete m_data;
90 m_data = 0;
91 if(mHandler && mLooper){
92 BMessage sentMsg(LS_COMPLETE);
93 sentMsg.AddBool("success", rc);
94 sentMsg.AddString("listing", listing.String());
95 mLooper->PostMessage(&sentMsg, mHandler);
96 }
97 return rc;
98 }
99
100 cmd = "LIST";
101
102 if(!p_sendRequest(cmd) || !p_getReply(replystr, code, codetype)
103 || codetype > 2 || !p_acceptDataConnection()) {
104 delete m_data;
105 m_data = 0; if(mHandler && mLooper){
106 BMessage sentMsg(LS_COMPLETE);
107 sentMsg.AddBool("success", rc);
108 sentMsg.AddString("listing", listing.String());
109 mLooper->PostMessage(&sentMsg, mHandler);
110 }
111 return rc;
112 }
113
114 listing.SetTo("");
115 numread = 1;
116 while(numread > 0)
117 {
118 memset(buf, 0, sizeof(buf));
119 numread = m_data->Receive(buf, sizeof(buf) - 1);
120 listing.Append(buf);
121 }
122
123 if(p_getReply(replystr, code, codetype) && codetype <= 2)
124 rc = true;
125
126 delete m_data;
127 m_data = 0;
128
129 if(mHandler && mLooper){
130 BMessage sentMsg(LS_COMPLETE);
131 sentMsg.AddBool("success", rc);
132 sentMsg.AddString("listing", listing.String());
133 mLooper->PostMessage(&sentMsg, mHandler);
134 }
135
136 return rc;
137 }
138
139
140 bool FtpClient::pwd(string &dir)
141 {
142 bool rc = false;
143 int code, codetype;
144 string cmd = "PWD", replystr;
145 string::size_type i;
146
147 if(p_sendRequest(cmd) && p_getReply(replystr, code, codetype) && codetype == 2)
148 {
149 i = replystr.find('"');
150 if(i != string::npos)
151 {
152 i++;
153 dir = replystr.substr(i, replystr.find('"') - i);
154 rc = true;
155 }
156 }
157
158 if(mHandler && mLooper){
159 BMessage sentMsg(PWD_COMPLETE);
160 sentMsg.AddBool("success", rc);
161 sentMsg.AddString("dir", dir.c_str());
162 mLooper->PostMessage(&sentMsg, mHandler);
163 }
164 return rc;
165 }
166
167
168 bool FtpClient::connect(const string &server, const string &login, const string &passwd)
169 {
170 bool rc = false;
171 int code, codetype;
172 string cmd, replystr;
173 BNetAddress addr;
174
175 delete m_control;
176 delete m_data;
177
178
179 m_control = new BNetEndpoint;
180
181 if(m_control->InitCheck() != B_NO_ERROR){
182 if(mHandler && mLooper){
183 BMessage sentMsg(CONNECT_COMPLETE);
184 sentMsg.AddBool("success", rc);
185 sentMsg.AddString("server", server.c_str());
186 sentMsg.AddString("login", login.c_str());
187 sentMsg.AddString("passwd", passwd.c_str());
188 mLooper->PostMessage(&sentMsg, mHandler);
189 }
190 return false;
191 }
192
193 addr.SetTo(server.c_str(), "tcp", "ftp");
194 if(m_control->Connect(addr) != B_NO_ERROR || !p_getReply(replystr, code, codetype)
195 || (code == 421 && codetype == 5)){
196 delete m_control;
197 m_control = 0;
198 if(mHandler && mLooper){
199 BMessage sentMsg(CONNECT_COMPLETE);
200 sentMsg.AddBool("success", rc);
201 sentMsg.AddString("server", server.c_str());
202 sentMsg.AddString("login", login.c_str());
203 sentMsg.AddString("passwd", passwd.c_str());
204 mLooper->PostMessage(&sentMsg, mHandler);
205 }
206 return rc;
207 }
208
209 cmd = "USER "; cmd += login;
210 p_sendRequest(cmd);
211 if(p_getReply(replystr, code, codetype) == false)
212 {
213 delete m_control;
214 m_control = 0;
215 if(mHandler && mLooper){
216 BMessage sentMsg(CONNECT_COMPLETE);
217 sentMsg.AddBool("success", rc);
218 sentMsg.AddString("server", server.c_str());
219 sentMsg.AddString("login", login.c_str());
220 sentMsg.AddString("passwd", passwd.c_str());
221 mLooper->PostMessage(&sentMsg, mHandler);
222 }
223 return rc;
224 }
225
226 switch(code)
227 {
228 case 230:
229 case 202:
230 rc = true;
231 break;
232
233 case 331:
234 cmd = "PASS "; cmd += passwd;
235 p_sendRequest(cmd);
236 if(p_getReply(replystr, code, codetype) && codetype == 2)
237 rc = true;
238 break;
239
240 default:
241 break;
242
243 }
244
245 if(rc == true)
246 p_setState(ftp_connected);
247 else {
248 delete m_control;
249 m_control = 0;
250 }
251
252 if(mHandler && mLooper){
253 BMessage sentMsg(CONNECT_COMPLETE);
254 sentMsg.AddBool("success", rc);
255 sentMsg.AddString("server", server.c_str());
256 sentMsg.AddString("login", login.c_str());
257 sentMsg.AddString("passwd", passwd.c_str());
258 mLooper->PostMessage(&sentMsg, mHandler);
259 }
260 return rc;
261 }
262
263
264
265 bool FtpClient::putFile(const string &local, const string &remote, ftp_mode mode)
266 {
267 bool rc = false;
268 string cmd, replystr;
269 int code, codetype, i;
270 ssize_t rlen, slen;
271 BFile infile(local.c_str(), B_READ_ONLY);
272 char buf[8192], sbuf[16384], *stmp;
273
274 if(infile.InitCheck() != B_NO_ERROR){
275 if(mHandler && mLooper){
276 BMessage sentMsg(PUT_COMPLETE);
277 sentMsg.AddBool("success", rc);
278 sentMsg.AddString("local", local.c_str());
279 sentMsg.AddString("remote", remote.c_str());
280 mLooper->PostMessage(&sentMsg, mHandler);
281 }
282 return false;
283 }
284 if(mode == binary_mode)
285 cmd = "TYPE I";
286 else
287 cmd = "TYPE A";
288
289 if(p_sendRequest(cmd))
290 p_getReply(replystr, code, codetype);
291
292 try
293 {
294 cmd = "STOR ";
295 cmd += remote;
296 if(p_openDataConnection() && p_sendRequest(cmd) && p_getReply(replystr, code, codetype)
297 && codetype <= 2 && p_acceptDataConnection()) {
298 rlen = 1;
299 while(rlen > 0)
300 {
301 memset(buf, 0, sizeof(buf));
302 memset(sbuf, 0, sizeof(sbuf));
303 rlen = infile.Read((void *) buf, sizeof(buf));
304 slen = rlen;
305 stmp = buf;
306 if(mode == ascii_mode)
307 {
308 stmp = sbuf;
309 slen = 0;
310 for(i=0;i<rlen;i++)
311 {
312 if(buf[i] == '\n')
313 {
314 *stmp = '\r';
315 stmp++;
316 slen++;
317 }
318 *stmp = buf[i];
319 stmp++;
320 slen++;
321 }
322 stmp = sbuf;
323 }
324 if(slen > 0)
325 {
326 size_t amount = m_data->Send(stmp, (size_t) slen);
327 if(amount < 0)
328 throw "bail";
329 if(mHandler && mLooper && amount > 0){
330 BMessage sentMsg(AMOUNT_SENT);
331 sentMsg.AddInt32("amount", amount);
332 mLooper->PostMessage(&sentMsg, mHandler);
333 }
334 }
335 }
336
337 rc = true;
338 }
339 }
340
341 catch(const char *errstr)
342 {
343
344 }
345
346 delete m_data;
347 m_data = 0;
348
349 if(rc == true)
350 {
351 p_getReply(replystr, code, codetype);
352 rc = (bool) codetype <= 2;
353 }
354
355 if(mHandler && mLooper){
356 BMessage sentMsg(PUT_COMPLETE);
357 sentMsg.AddBool("success", rc);
358 sentMsg.AddString("local", local.c_str());
359 sentMsg.AddString("remote", remote.c_str());
360 mLooper->PostMessage(&sentMsg, mHandler);
361 }
362 return rc;
363 }
364
365
366
367 bool FtpClient::getFile(const string &remote, const string &local, ftp_mode mode)
368 {
369 bool rc = false;
370 string cmd, replystr;
371 int code, codetype, i;
372 ssize_t rlen, slen;
373 BFile outfile(local.c_str(), B_READ_WRITE | B_CREATE_FILE);
374 char buf[8192], sbuf[16384], *stmp;
375 bool writeerr = false;
376
377 if(outfile.InitCheck() != B_NO_ERROR){
378 if(mHandler && mLooper){
379 BMessage sentMsg(GET_COMPLETE);
380 sentMsg.AddBool("success", rc);
381 sentMsg.AddString("remote", remote.c_str());
382 sentMsg.AddString("local", local.c_str());
383 mLooper->PostMessage(&sentMsg, mHandler);
384 }
385 return false;
386 }
387
388 if(mode == binary_mode)
389 cmd = "TYPE I";
390 else
391 cmd = "TYPE A";
392
393 if(p_sendRequest(cmd))
394 p_getReply(replystr, code, codetype);
395
396
397 cmd = "RETR ";
398 cmd += remote;
399 if(p_openDataConnection() &&p_sendRequest(cmd) && p_getReply(replystr, code, codetype)
400 && codetype <= 2 && p_acceptDataConnection())
401 {
402 rlen = 1;
403 rc = true;
404 while(rlen > 0)
405 {
406 memset(buf, 0, sizeof(buf));
407 memset(sbuf, 0, sizeof(sbuf));
408 rlen = m_data->Receive(buf, sizeof(buf));
409
410 if(rlen > 0)
411 {
412
413 slen = rlen;
414 stmp = buf;
415 if(mode == ascii_mode)
416 {
417 stmp = sbuf;
418 slen = 0;
419 for(i=0;i<rlen;i++)
420 {
421 if(buf[i] == '\r')
422 {
423 i++;
424 }
425 *stmp = buf[i];
426 stmp++;
427 slen++;
428 }
429 stmp = sbuf;
430 }
431
432 if(slen > 0)
433 {
434 if(outfile.Write(stmp, (size_t) slen) < 0)
435 {
436 writeerr = true;
437 }
438
439 }
440 }
441 }
442 }
443
444 delete m_data;
445 m_data = 0;
446
447 if(rc == true)
448 {
449 p_getReply(replystr, code, codetype);
450 rc = (bool) ((codetype <= 2) && (writeerr == false));
451 }
452
453 if(mHandler && mLooper){
454 BMessage sentMsg(GET_COMPLETE);
455 sentMsg.AddBool("success", rc);
456 sentMsg.AddString("remote", remote.c_str());
457 sentMsg.AddString("local", local.c_str());
458 mLooper->PostMessage(&sentMsg, mHandler);
459 }
460 return rc;
461 }
462
463
464
465 bool FtpClient::moveFile(const string &oldpath, const string &newpath)
466 {
467 bool rc = false;
468 string from = "RNFR ";
469 string to = "RNTO ";
470 string replystr;
471 int code, codetype;
472
473 from += oldpath;
474 to += newpath;
475
476 if(p_sendRequest(from)
477 && p_getReply(replystr, code, codetype)
478 && codetype == 3
479 && p_sendRequest(to)
480 && p_getReply(replystr, code, codetype)
481 && codetype == 2)
482 rc = true;
483
484 if(mHandler && mLooper){
485 BMessage sentMsg(MOVE_COMPLETE);
486 sentMsg.AddBool("success", rc);
487 sentMsg.AddString("old", oldpath.c_str());
488 sentMsg.AddString("new", newpath.c_str());
489 mLooper->PostMessage(&sentMsg, mHandler);
490 }
491
492 return rc;
493 }
494
495 bool
496 FtpClient::makeDir(const string &newdir)
497 {
498 bool rc = false;
499 string request = "MKD ";
500 string replystr;
501 int code, codetype;
502
503 request += newdir;
504
505 if(p_sendRequest(request)
506 && p_getReply(replystr, code, codetype)
507 && codetype == 2)
508 rc = true;
509
510 if(mHandler && mLooper){
511 BMessage sentMsg(MAKE_COMPLETE);
512 sentMsg.AddBool("success", rc);
513 sentMsg.AddString("new", newdir.c_str());
514 mLooper->PostMessage(&sentMsg, mHandler);
515 }
516
517 return rc;
518 }
519
520
521 void FtpClient::setPassive(bool on)
522 {
523 if(on)
524 p_setState(ftp_passive);
525 else
526 p_clearState(ftp_passive);
527 }
528
529
530
531 bool FtpClient::p_testState(unsigned long state)
532 {
533 return (bool) ((m_state & state) != 0);
534 }
535
536
537
538 void FtpClient::p_setState(unsigned long state)
539 {
540 m_state |= state;
541 }
542
543
544
545 void FtpClient::p_clearState(unsigned long state)
546 {
547 m_state &= ~state;
548 }
549
550
551
552
553 bool FtpClient::p_sendRequest(const string &cmd)
554 {
555 bool rc = false;
556 string ccmd = cmd;
557 ccmd += "\r\n";
558
559 if(m_control != 0 && m_control->Send(ccmd.c_str(), ccmd.length()) >= 0)
560 rc = true;
561
562 return rc;
563 }
564
565
566
567 bool FtpClient::p_getReplyLine(string &line)
568 {
569 bool rc = false;
570 int c = 0;
571 bool done = false;
572
573 line = "";
574
575 if(m_control != 0)
576 {
577 rc = true;
578 while(done == false && (m_control->Receive(&c, 1) > 0))
579 {
580 if(c == EOF || c == xEOF || c == '\n')
581 {
582 done = true;
583 } else {
584 if(c == IAC)
585 {
586 m_control->Receive(&c, 1);
587 switch(c)
588 {
589 unsigned char treply[3];
590 case WILL:
591 case WONT:
592 m_control->Receive(&c, 1);
593 treply[0] = IAC;
594 treply[1] = DONT;
595 treply[2] = (unsigned char) c;
596 m_control->Send(treply, 3);
597 break;
598
599 case DO:
600 case DONT:
601 m_control->Receive(&c, 1);
602 m_control->Receive(&c, 1);
603 treply[0] = IAC;
604 treply[1] = WONT;
605 treply[2] = (unsigned char) c;
606 m_control->Send(treply, 3);
607 break;
608
609 case EOF:
610 case xEOF:
611 done = true;
612 break;
613
614 default:
615 line += (char) c;
616 break;
617 }
618 } else {
619
620 if(c != '\r')
621 line += (char) c;
622 }
623 }
624 }
625 }
626
627 return rc;
628 }
629
630
631 bool FtpClient::p_getReply(string &outstr, int &outcode, int &codetype)
632 {
633 bool rc = false;
634 string line, tempstr;
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650 if((rc = p_getReplyLine(line)) == true)
651 {
652 outstr = line;
653 outstr += '\n';
654 tempstr = line.substr(0, 3);
655 outcode = atoi(tempstr.c_str());
656
657 if(line[3] == '-')
658 {
659 while((rc = p_getReplyLine(line)) == true)
660 {
661 outstr += line;
662 outstr += '\n';
663
664 if((line.find(tempstr) == 0) && line[3] == ' ')
665 break;
666 }
667 }
668 }
669
670 if(rc == false && outcode != 421)
671 {
672 outstr += "Remote host has closed the connection.\n";
673 outcode = 421;
674 }
675
676 if(outcode == 421)
677 {
678 delete m_control;
679 m_control = 0;
680 p_clearState(ftp_connected);
681 }
682
683 codetype = outcode / 100;
684
685 return rc;
686 }
687
688
689
690 bool FtpClient::p_openDataConnection()
691 {
692 string host, cmd, repstr;
693 unsigned short port;
694 BNetAddress addr;
695 int code, codetype;
696 string::size_type i;
697 bool rc = false;
698 struct sockaddr_in sa;
699
700 delete m_data;
701 m_data = 0;
702
703 m_data = new BNetEndpoint;
704
705 if(p_testState(ftp_passive))
706 {
707
708
709 cmd = "PASV";
710 if(p_sendRequest(cmd))
711 {
712 if(p_getReply(repstr, code, codetype))
713 {
714
715 if(codetype == 2)
716 {
717
718
719 int paddr[6];
720 unsigned char ucaddr[6];
721
722 i = repstr.find('(');
723 i++;
724
725 repstr = repstr.substr(i, repstr.find(')') - i);
726 if (sscanf(repstr.c_str(), "%d,%d,%d,%d,%d,%d",
727 &paddr[0], &paddr[1], &paddr[2], &paddr[3],
728 &paddr[4], &paddr[5]) != 6)
729 {
730
731 p_clearState(ftp_passive);
732 return p_openDataConnection();
733 }
734 for(i=0;i<6;i++)
735 {
736 ucaddr[i] = (unsigned char) (paddr[i] & 0xff);
737 }
738 memcpy(&sa.sin_addr, &ucaddr[0], (size_t) 4);
739 memcpy(&sa.sin_port, &ucaddr[4], (size_t) 2);
740 addr.SetTo(sa);
741 if(m_data->Connect(addr) == B_NO_ERROR)
742 {
743 rc = true;
744 }
745 }
746 }
747 } else {
748
749 p_clearState(ftp_passive);
750 rc = p_openDataConnection();
751 }
752
753 } else {
754
755 if(m_data->Bind() == B_NO_ERROR)
756 {
757 char buf[255];
758
759 m_data->Listen();
760 addr = m_data->LocalAddr();
761 addr.GetAddr(buf, &port);
762 host = buf;
763
764 i=0;
765 while(i != string::npos)
766 {
767 i = host.find('.', i);
768 if(i != string::npos)
769 host[i] = ',';
770 }
771
772 sprintf(buf, ",%d,%d", (port & 0xff00) >> 8, port & 0x00ff);
773 cmd = "PORT ";
774 cmd += host; cmd += buf;
775 p_sendRequest(cmd);
776 p_getReply(repstr, code, codetype);
777
778 if(codetype == 2)
779 rc = true;
780 }
781 }
782
783 return rc;
784 }
785
786
787 bool FtpClient::p_acceptDataConnection()
788 {
789 BNetEndpoint *ep;
790 bool rc = false;
791
792 if(p_testState(ftp_passive) == false)
793 {
794 if(m_data != 0)
795 {
796 ep = m_data->Accept();
797 if(ep != 0)
798 {
799 delete m_data;
800 m_data = ep;
801 rc = true;
802 }
803 }
804
805 } else {
806 rc = true;
807 }
808
809 return rc;
810 }
811