10 #undef B_TRANSLATION_CONTEXT
11 #define B_TRANSLATION_CONTEXT "FtpClient"
14 FtpClient::FtpClient()
23 FtpClient::~FtpClient()
31 FtpClient::ChangeDir(const string
& dir
)
35 string cmd
= "CWD ", replyString
;
39 if (dir
.length() == 0)
42 if (_SendRequest(cmd
) == true) {
43 if (_GetReply(replyString
, code
, codeType
) == true) {
53 FtpClient::ListDirContents(string
& listing
)
56 string cmd
, replyString
;
57 int code
, codeType
, numRead
;
62 if (_SendRequest(cmd
))
63 _GetReply(replyString
, code
, codeType
);
65 if (_OpenDataConnection()) {
68 if (_SendRequest(cmd
)) {
69 if (_GetReply(replyString
, code
, codeType
)) {
71 if (_AcceptDataConnection()) {
74 memset(buf
, 0, sizeof(buf
));
75 numRead
= fData
->Receive(buf
, sizeof(buf
) - 1);
79 if (_GetReply(replyString
, code
, codeType
)) {
97 FtpClient::PrintWorkingDir(string
& dir
)
101 string cmd
= "PWD", replyString
;
104 if (_SendRequest(cmd
) == true) {
105 if (_GetReply(replyString
, code
, codeType
) == true) {
107 i
= replyString
.find('"');
110 dir
= replyString
.substr(i
, replyString
.find('"') - i
);
122 FtpClient::Connect(const string
& server
, const string
& login
, const string
& passwd
)
126 string cmd
, replyString
;
132 fControl
= new BNetEndpoint
;
134 if (fControl
->InitCheck() != B_NO_ERROR
)
137 addr
.SetTo(server
.c_str(), "tcp", "ftp");
138 if (fControl
->Connect(addr
) == B_NO_ERROR
) {
139 // read the welcome message, do the login
141 if (_GetReply(replyString
, code
, codeType
)) {
142 if (code
!= 421 && codeType
!= 5) {
147 if (_GetReply(replyString
, code
, codeType
)) {
154 case 331: // password needed
158 if (_GetReply(replyString
, code
, codeType
)) {
174 _SetState(ftp_connected
);
185 FtpClient::PutFile(const string
& local
, const string
& remote
, ftp_mode mode
)
188 string cmd
, replyString
;
189 int code
, codeType
, rlen
, slen
, i
;
190 BFile
infile(local
.c_str(), B_READ_ONLY
);
191 char buf
[8192], sbuf
[16384], *stmp
;
193 if (infile
.InitCheck() != B_NO_ERROR
)
196 if (mode
== binary_mode
)
201 if (_SendRequest(cmd
))
202 _GetReply(replyString
, code
, codeType
);
205 if (_OpenDataConnection()) {
209 if (_SendRequest(cmd
)) {
210 if (_GetReply(replyString
, code
, codeType
)) {
212 if (_AcceptDataConnection()) {
215 memset(buf
, 0, sizeof(buf
));
216 memset(sbuf
, 0, sizeof(sbuf
));
217 rlen
= infile
.Read((void*)buf
, sizeof(buf
));
220 if (mode
== ascii_mode
) {
223 for (i
= 0; i
< rlen
; i
++) {
224 if (buf
[i
] == '\n') {
236 if (fData
->Send(stmp
, slen
) < 0)
249 catch(const char* errorString
)
257 _GetReply(replyString
, code
, codeType
);
266 FtpClient::GetFile(const string
& remote
, const string
& local
, ftp_mode mode
)
269 string cmd
, replyString
;
270 int code
, codeType
, rlen
, slen
, i
;
271 BFile
outfile(local
.c_str(), B_READ_WRITE
| B_CREATE_FILE
);
272 char buf
[8192], sbuf
[16384], *stmp
;
273 bool writeError
= false;
275 if (outfile
.InitCheck() != B_NO_ERROR
)
278 if (mode
== binary_mode
)
283 if (_SendRequest(cmd
))
284 _GetReply(replyString
, code
, codeType
);
286 if (_OpenDataConnection()) {
290 if (_SendRequest(cmd
)) {
291 if (_GetReply(replyString
, code
, codeType
)) {
293 if (_AcceptDataConnection()) {
297 memset(buf
, 0, sizeof(buf
));
298 memset(sbuf
, 0, sizeof(sbuf
));
299 rlen
= fData
->Receive(buf
, sizeof(buf
));
305 if (mode
== ascii_mode
) {
308 for (i
= 0; i
< rlen
; i
++) {
319 if (outfile
.Write(stmp
, slen
) < 0)
334 _GetReply(replyString
, code
, codeType
);
335 rc
= (codeType
<= 2 && writeError
== false);
341 // Note: this only works for local remote moves, cross filesystem moves
344 FtpClient::MoveFile(const string
& oldPath
, const string
& newPath
)
347 string from
= "RNFR ";
355 if (_SendRequest(from
)) {
356 if (_GetReply(replyString
, code
, codeType
)) {
358 if (_SendRequest(to
)) {
359 if (_GetReply(replyString
, code
, codeType
)) {
372 FtpClient::Chmod(const string
& path
, const string
& mod
)
376 string cmd
= "SITE CHMOD ", replyString
;
382 if (path
.length() == 0)
384 printf(B_TRANSLATE("cmd: '%s'\n"), cmd
.c_str());
385 if (_SendRequest(cmd
) == true) {
386 if (_GetReply(replyString
, code
, codeType
) == true) {
387 printf(B_TRANSLATE("reply: %d, %d\n"), code
, codeType
);
397 FtpClient::SetPassive(bool on
)
400 _SetState(ftp_passive
);
402 _ClearState(ftp_passive
);
407 FtpClient::_TestState(unsigned long state
)
409 return ((fState
& state
) != 0);
414 FtpClient::_SetState(unsigned long state
)
421 FtpClient::_ClearState(unsigned long state
)
428 FtpClient::_SendRequest(const string
& cmd
)
434 if (cmd
.find("PASS") != string::npos
)
435 printf(B_TRANSLATE("PASS <suppressed> (real password sent)\n"));
437 printf("%s\n", ccmd
.c_str());
440 if (fControl
->Send(ccmd
.c_str(), ccmd
.length()) >= 0)
449 FtpClient::_GetReplyLine(string
& line
)
455 line
= ""; // Thanks to Stephen van Egmond for catching a bug here
459 while (done
== false && fControl
->Receive(&c
, 1) > 0) {
460 if (c
== EOF
|| c
== xEOF
|| c
== '\n') {
464 fControl
->Receive(&c
, 1);
466 unsigned char treply
[3];
469 fControl
->Receive(&c
, 1);
473 fControl
->Send(treply
, 3);
478 fControl
->Receive(&c
, 1);
479 fControl
->Receive(&c
, 1);
483 fControl
->Send(treply
, 3);
509 FtpClient::_GetReply(string
& outString
, int& outCode
, int& codeType
)
512 string line
, tempString
;
515 // comment from the ncftp source:
518 /* RFC 959 states that a reply may span multiple lines. A single
519 * line message would have the 3-digit code <space> then the msg.
520 * A multi-line message would have the code <dash> and the first
521 * line of the msg, then additional lines, until the last line,
522 * which has the code <space> and last line of the msg.
527 * 234 A line beginning with numbers
531 if ((rc
= _GetReplyLine(line
)) == true) {
534 printf(outString
.c_str());
535 tempString
= line
.substr(0, 3);
536 outCode
= atoi(tempString
.c_str());
538 if (line
[3] == '-') {
539 while ((rc
= _GetReplyLine(line
)) == true) {
542 printf(outString
.c_str());
543 // we're done with nnn when we get to a "nnn blahblahblah"
544 if ((line
.find(tempString
) == 0) && line
[3] == ' ')
550 if (!rc
&& outCode
!= 421) {
551 outString
+= B_TRANSLATE("Remote host has closed the connection.\n");
555 if (outCode
== 421) {
558 _ClearState(ftp_connected
);
561 codeType
= outCode
/ 100;
568 FtpClient::_OpenDataConnection()
570 string host
, cmd
, replyString
;
573 int i
, code
, codeType
;
575 struct sockaddr_in sa
;
580 fData
= new BNetEndpoint
;
582 if (_TestState(ftp_passive
)) {
583 // Here we send a "pasv" command and connect to the remote server
584 // on the port it sends back to us
586 if (_SendRequest(cmd
)) {
587 if (_GetReply(replyString
, code
, codeType
)) {
590 // It should give us something like:
591 // "227 Entering Passive Mode (192,168,1,1,10,187)"
593 unsigned char ucaddr
[6];
595 i
= replyString
.find('(');
598 replyString
= replyString
.substr(i
, replyString
.find(')') - i
);
599 if (sscanf(replyString
.c_str(), "%d,%d,%d,%d,%d,%d",
600 &paddr
[0], &paddr
[1], &paddr
[2], &paddr
[3],
601 &paddr
[4], &paddr
[5]) != 6) {
602 // cannot do passive. Do a little harmless rercursion here
603 _ClearState(ftp_passive
);
604 return _OpenDataConnection();
607 for (i
= 0; i
< 6; i
++)
608 ucaddr
[i
] = (unsigned char)(paddr
[i
] & 0xff);
610 memcpy(&sa
.sin_addr
, &ucaddr
[0], (size_t) 4);
611 memcpy(&sa
.sin_port
, &ucaddr
[4], (size_t) 2);
613 if (fData
->Connect(addr
) == B_NO_ERROR
)
619 // cannot do passive. Do a little harmless rercursion here
620 _ClearState(ftp_passive
);
621 rc
= _OpenDataConnection();
625 // Here we bind to a local port and send a PORT command
626 if (fData
->Bind() == B_NO_ERROR
) {
630 addr
= fData
->LocalAddr();
631 addr
.GetAddr(buf
, &port
);
636 i
= host
.find('.', i
);
641 sprintf(buf
, ",%d,%d", (port
& 0xff00) >> 8, port
& 0x00ff);
646 _GetReply(replyString
, code
, codeType
);
647 // PORT failure is in the 500-range
658 FtpClient::_AcceptDataConnection()
660 BNetEndpoint
* endPoint
;
663 if (_TestState(ftp_passive
) == false) {
665 endPoint
= fData
->Accept();