BTRFS: Implement BTree::Path and change _Find.
[haiku.git] / src / apps / codycam / FtpClient.cpp
blob3939ab046e40a03ff940132d61eec2b9bb9c5dce
1 #include "FtpClient.h"
3 #include <stdlib.h>
4 #include <string.h>
6 #include <Catalog.h>
7 #include <Locale.h>
10 #undef B_TRANSLATION_CONTEXT
11 #define B_TRANSLATION_CONTEXT "FtpClient"
14 FtpClient::FtpClient()
15 : FileUploadClient(),
16 fState(0),
17 fControl(NULL),
18 fData(NULL)
23 FtpClient::~FtpClient()
25 delete fControl;
26 delete fData;
30 bool
31 FtpClient::ChangeDir(const string& dir)
33 bool rc = false;
34 int code, codeType;
35 string cmd = "CWD ", replyString;
37 cmd += dir;
39 if (dir.length() == 0)
40 cmd += '/';
42 if (_SendRequest(cmd) == true) {
43 if (_GetReply(replyString, code, codeType) == true) {
44 if (codeType == 2)
45 rc = true;
48 return rc;
52 bool
53 FtpClient::ListDirContents(string& listing)
55 bool rc = false;
56 string cmd, replyString;
57 int code, codeType, numRead;
58 char buf[513];
60 cmd = "TYPE A";
62 if (_SendRequest(cmd))
63 _GetReply(replyString, code, codeType);
65 if (_OpenDataConnection()) {
66 cmd = "LIST";
68 if (_SendRequest(cmd)) {
69 if (_GetReply(replyString, code, codeType)) {
70 if (codeType <= 2) {
71 if (_AcceptDataConnection()) {
72 numRead = 1;
73 while (numRead > 0) {
74 memset(buf, 0, sizeof(buf));
75 numRead = fData->Receive(buf, sizeof(buf) - 1);
76 listing += buf;
77 printf(buf);
79 if (_GetReply(replyString, code, codeType)) {
80 if (codeType <= 2)
81 rc = true;
89 delete fData;
90 fData = 0;
92 return rc;
96 bool
97 FtpClient::PrintWorkingDir(string& dir)
99 bool rc = false;
100 int code, codeType;
101 string cmd = "PWD", replyString;
102 long i;
104 if (_SendRequest(cmd) == true) {
105 if (_GetReply(replyString, code, codeType) == true) {
106 if (codeType == 2) {
107 i = replyString.find('"');
108 if (i != -1) {
109 i++;
110 dir = replyString.substr(i, replyString.find('"') - i);
111 rc = true;
117 return rc;
121 bool
122 FtpClient::Connect(const string& server, const string& login, const string& passwd)
124 bool rc = false;
125 int code, codeType;
126 string cmd, replyString;
127 BNetAddress addr;
129 delete fControl;
130 delete fData;
132 fControl = new BNetEndpoint;
134 if (fControl->InitCheck() != B_NO_ERROR)
135 return false;
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) {
143 cmd = "USER ";
144 cmd += login;
145 _SendRequest(cmd);
147 if (_GetReply(replyString, code, codeType)) {
148 switch (code) {
149 case 230:
150 case 202:
151 rc = true;
152 break;
154 case 331: // password needed
155 cmd = "PASS ";
156 cmd += passwd;
157 _SendRequest(cmd);
158 if (_GetReply(replyString, code, codeType)) {
159 if (codeType == 2)
160 rc = true;
162 break;
164 default:
165 break;
173 if (rc == true)
174 _SetState(ftp_connected);
175 else {
176 delete fControl;
177 fControl = 0;
180 return rc;
184 bool
185 FtpClient::PutFile(const string& local, const string& remote, ftp_mode mode)
187 bool rc = false;
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)
194 return false;
196 if (mode == binary_mode)
197 cmd = "TYPE I";
198 else
199 cmd = "TYPE A";
201 if (_SendRequest(cmd))
202 _GetReply(replyString, code, codeType);
204 try {
205 if (_OpenDataConnection()) {
206 cmd = "STOR ";
207 cmd += remote;
209 if (_SendRequest(cmd)) {
210 if (_GetReply(replyString, code, codeType)) {
211 if (codeType <= 2) {
212 if (_AcceptDataConnection()) {
213 rlen = 1;
214 while (rlen > 0) {
215 memset(buf, 0, sizeof(buf));
216 memset(sbuf, 0, sizeof(sbuf));
217 rlen = infile.Read((void*)buf, sizeof(buf));
218 slen = rlen;
219 stmp = buf;
220 if (mode == ascii_mode) {
221 stmp = sbuf;
222 slen = 0;
223 for (i = 0; i < rlen; i++) {
224 if (buf[i] == '\n') {
225 *stmp = '\r';
226 stmp++;
227 slen++;
229 *stmp = buf[i];
230 stmp++;
231 slen++;
233 stmp = sbuf;
235 if (slen > 0) {
236 if (fData->Send(stmp, slen) < 0)
237 throw "bail";
241 rc = true;
249 catch(const char* errorString)
253 delete fData;
254 fData = 0;
256 if (rc) {
257 _GetReply(replyString, code, codeType);
258 rc = codeType <= 2;
261 return rc;
265 bool
266 FtpClient::GetFile(const string& remote, const string& local, ftp_mode mode)
268 bool rc = false;
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)
276 return false;
278 if (mode == binary_mode)
279 cmd = "TYPE I";
280 else
281 cmd = "TYPE A";
283 if (_SendRequest(cmd))
284 _GetReply(replyString, code, codeType);
286 if (_OpenDataConnection()) {
287 cmd = "RETR ";
288 cmd += remote;
290 if (_SendRequest(cmd)) {
291 if (_GetReply(replyString, code, codeType)) {
292 if (codeType <= 2) {
293 if (_AcceptDataConnection()) {
294 rlen = 1;
295 rc = true;
296 while (rlen > 0) {
297 memset(buf, 0, sizeof(buf));
298 memset(sbuf, 0, sizeof(sbuf));
299 rlen = fData->Receive(buf, sizeof(buf));
301 if (rlen > 0) {
303 slen = rlen;
304 stmp = buf;
305 if (mode == ascii_mode) {
306 stmp = sbuf;
307 slen = 0;
308 for (i = 0; i < rlen; i++) {
309 if (buf[i] == '\r')
310 i++;
311 *stmp = buf[i];
312 stmp++;
313 slen++;
315 stmp = sbuf;
318 if (slen > 0) {
319 if (outfile.Write(stmp, slen) < 0)
320 writeError = true;
330 delete fData;
331 fData = 0;
333 if (rc) {
334 _GetReply(replyString, code, codeType);
335 rc = (codeType <= 2 && writeError == false);
337 return rc;
341 // Note: this only works for local remote moves, cross filesystem moves
342 // will not work
343 bool
344 FtpClient::MoveFile(const string& oldPath, const string& newPath)
346 bool rc = false;
347 string from = "RNFR ";
348 string to = "RNTO ";
349 string replyString;
350 int code, codeType;
352 from += oldPath;
353 to += newPath;
355 if (_SendRequest(from)) {
356 if (_GetReply(replyString, code, codeType)) {
357 if (codeType == 3) {
358 if (_SendRequest(to)) {
359 if (_GetReply(replyString, code, codeType)) {
360 if(codeType == 2)
361 rc = true;
367 return rc;
371 bool
372 FtpClient::Chmod(const string& path, const string& mod)
374 bool rc = false;
375 int code, codeType;
376 string cmd = "SITE CHMOD ", replyString;
378 cmd += mod;
379 cmd += " ";
380 cmd += path;
382 if (path.length() == 0)
383 cmd += '/';
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);
388 if (codeType == 2)
389 rc = true;
392 return rc;
396 void
397 FtpClient::SetPassive(bool on)
399 if (on)
400 _SetState(ftp_passive);
401 else
402 _ClearState(ftp_passive);
406 bool
407 FtpClient::_TestState(unsigned long state)
409 return ((fState & state) != 0);
413 void
414 FtpClient::_SetState(unsigned long state)
416 fState |= state;
420 void
421 FtpClient::_ClearState(unsigned long state)
423 fState &= ~state;
427 bool
428 FtpClient::_SendRequest(const string& cmd)
430 bool rc = false;
431 string ccmd = cmd;
433 if (fControl != 0) {
434 if (cmd.find("PASS") != string::npos)
435 printf(B_TRANSLATE("PASS <suppressed> (real password sent)\n"));
436 else
437 printf("%s\n", ccmd.c_str());
439 ccmd += "\r\n";
440 if (fControl->Send(ccmd.c_str(), ccmd.length()) >= 0)
441 rc = true;
444 return rc;
448 bool
449 FtpClient::_GetReplyLine(string& line)
451 bool rc = false;
452 int c = 0;
453 bool done = false;
455 line = ""; // Thanks to Stephen van Egmond for catching a bug here
457 if (fControl != 0) {
458 rc = true;
459 while (done == false && fControl->Receive(&c, 1) > 0) {
460 if (c == EOF || c == xEOF || c == '\n') {
461 done = true;
462 } else {
463 if (c == IAC) {
464 fControl->Receive(&c, 1);
465 switch (c) {
466 unsigned char treply[3];
467 case WILL:
468 case WONT:
469 fControl->Receive(&c, 1);
470 treply[0] = IAC;
471 treply[1] = DONT;
472 treply[2] = c;
473 fControl->Send(treply, 3);
474 break;
476 case DO:
477 case DONT:
478 fControl->Receive(&c, 1);
479 fControl->Receive(&c, 1);
480 treply[0] = IAC;
481 treply[1] = WONT;
482 treply[2] = c;
483 fControl->Send(treply, 3);
484 break;
486 case EOF:
487 case xEOF:
488 done = true;
489 break;
491 default:
492 line += c;
493 break;
495 } else {
496 // normal char
497 if (c != '\r')
498 line += c;
504 return rc;
508 bool
509 FtpClient::_GetReply(string& outString, int& outCode, int& codeType)
511 bool rc = false;
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.
524 * For example:
525 * 123-First line
526 * Second line
527 * 234 A line beginning with numbers
528 * 123 The last line
531 if ((rc = _GetReplyLine(line)) == true) {
532 outString = line;
533 outString += '\n';
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) {
540 outString += line;
541 outString += '\n';
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] == ' ')
545 break;
550 if (!rc && outCode != 421) {
551 outString += B_TRANSLATE("Remote host has closed the connection.\n");
552 outCode = 421;
555 if (outCode == 421) {
556 delete fControl;
557 fControl = 0;
558 _ClearState(ftp_connected);
561 codeType = outCode / 100;
563 return rc;
567 bool
568 FtpClient::_OpenDataConnection()
570 string host, cmd, replyString;
571 unsigned short port;
572 BNetAddress addr;
573 int i, code, codeType;
574 bool rc = false;
575 struct sockaddr_in sa;
577 delete fData;
578 fData = 0;
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
585 cmd = "PASV";
586 if (_SendRequest(cmd)) {
587 if (_GetReply(replyString, code, codeType)) {
589 if (codeType == 2) {
590 // It should give us something like:
591 // "227 Entering Passive Mode (192,168,1,1,10,187)"
592 int paddr[6];
593 unsigned char ucaddr[6];
595 i = replyString.find('(');
596 i++;
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);
612 addr.SetTo(sa);
613 if (fData->Connect(addr) == B_NO_ERROR)
614 rc = true;
618 } else {
619 // cannot do passive. Do a little harmless rercursion here
620 _ClearState(ftp_passive);
621 rc = _OpenDataConnection();
624 } else {
625 // Here we bind to a local port and send a PORT command
626 if (fData->Bind() == B_NO_ERROR) {
627 char buf[255];
629 fData->Listen();
630 addr = fData->LocalAddr();
631 addr.GetAddr(buf, &port);
632 host = buf;
634 i = 0;
635 while (i >= 0) {
636 i = host.find('.', i);
637 if (i >= 0)
638 host[i] = ',';
641 sprintf(buf, ",%d,%d", (port & 0xff00) >> 8, port & 0x00ff);
642 cmd = "PORT ";
643 cmd += host;
644 cmd += buf;
645 _SendRequest(cmd);
646 _GetReply(replyString, code, codeType);
647 // PORT failure is in the 500-range
648 if (codeType == 2)
649 rc = true;
653 return rc;
657 bool
658 FtpClient::_AcceptDataConnection()
660 BNetEndpoint* endPoint;
661 bool rc = false;
663 if (_TestState(ftp_passive) == false) {
664 if (fData != 0) {
665 endPoint = fData->Accept();
666 if (endPoint != 0) {
667 delete fData;
668 fData = endPoint;
669 rc = true;
674 else
675 rc = true;
677 return rc;