16a739773SCodeforEvolution/*
26a739773SCodeforEvolution * Copyright 1998-1999 Be, Inc. All Rights Reserved.
36a739773SCodeforEvolution * Copyright 2003-2019 Haiku, Inc. All rights reserved.
46a739773SCodeforEvolution * Distributed under the terms of the MIT License.
56a739773SCodeforEvolution */
66a739773SCodeforEvolution
76a739773SCodeforEvolution
8dba5824bSPhil Greenway#include "FtpClient.h"
9dba5824bSPhil Greenway
10fb3e35fcSMichael Lotz#include <stdlib.h>
11fb3e35fcSMichael Lotz#include <string.h>
12fb3e35fcSMichael Lotz
13c4f9b1d1SStephan Aßmus#include <Catalog.h>
14c4f9b1d1SStephan Aßmus#include <Locale.h>
15c4f9b1d1SStephan Aßmus
16c4f9b1d1SStephan Aßmus
17546208a5SOliver Tappe#undef B_TRANSLATION_CONTEXT
18546208a5SOliver Tappe#define B_TRANSLATION_CONTEXT "FtpClient"
19c4f9b1d1SStephan Aßmus
20c4f9b1d1SStephan Aßmus
21dba5824bSPhil GreenwayFtpClient::FtpClient()
226a739773SCodeforEvolution	:
236a739773SCodeforEvolution	FileUploadClient(),
249d3f15c6SIngo Weinhold	fState(0),
259d3f15c6SIngo Weinhold	fControl(NULL),
269d3f15c6SIngo Weinhold	fData(NULL)
27dba5824bSPhil Greenway{
28dba5824bSPhil Greenway}
29dba5824bSPhil Greenway
30dba5824bSPhil Greenway
31dba5824bSPhil GreenwayFtpClient::~FtpClient()
32dba5824bSPhil Greenway{
339d3f15c6SIngo Weinhold	delete fControl;
349d3f15c6SIngo Weinhold	delete fData;
35dba5824bSPhil Greenway}
36dba5824bSPhil Greenway
37dba5824bSPhil Greenway
389d3f15c6SIngo Weinholdbool
399d3f15c6SIngo WeinholdFtpClient::ChangeDir(const string& dir)
40dba5824bSPhil Greenway{
41dba5824bSPhil Greenway	bool rc = false;
429d3f15c6SIngo Weinhold	int code, codeType;
436a739773SCodeforEvolution	string cmd = "CWD ";
446a739773SCodeforEvolution	string replyString;
45dba5824bSPhil Greenway
46dba5824bSPhil Greenway	cmd += dir;
47476fad7fSMatt Madia
489d3f15c6SIngo Weinhold	if (dir.length() == 0)
49dba5824bSPhil Greenway		cmd += '/';
509d3f15c6SIngo Weinhold
519d3f15c6SIngo Weinhold	if (_SendRequest(cmd) == true) {
529d3f15c6SIngo Weinhold		if (_GetReply(replyString, code, codeType) == true) {
539d3f15c6SIngo Weinhold			if (codeType == 2)
54dba5824bSPhil Greenway				rc = true;
55dba5824bSPhil Greenway		}
56dba5824bSPhil Greenway	}
57dba5824bSPhil Greenway	return rc;
58dba5824bSPhil Greenway}
59dba5824bSPhil Greenway
60dba5824bSPhil Greenway
619d3f15c6SIngo Weinholdbool
629d3f15c6SIngo WeinholdFtpClient::ListDirContents(string& listing)
63dba5824bSPhil Greenway{
64dba5824bSPhil Greenway	bool rc = false;
659d3f15c6SIngo Weinhold	string cmd, replyString;
669d3f15c6SIngo Weinhold	int code, codeType, numRead;
67dba5824bSPhil Greenway	char buf[513];
68476fad7fSMatt Madia
69dba5824bSPhil Greenway	cmd = "TYPE A";
70476fad7fSMatt Madia
719d3f15c6SIngo Weinhold	if (_SendRequest(cmd))
729d3f15c6SIngo Weinhold		_GetReply(replyString, code, codeType);
73dba5824bSPhil Greenway
749d3f15c6SIngo Weinhold	if (_OpenDataConnection()) {
75dba5824bSPhil Greenway		cmd = "LIST";
769d3f15c6SIngo Weinhold
779d3f15c6SIngo Weinhold		if (_SendRequest(cmd)) {
789d3f15c6SIngo Weinhold			if (_GetReply(replyString, code, codeType)) {
799d3f15c6SIngo Weinhold				if (codeType <= 2) {
809d3f15c6SIngo Weinhold					if (_AcceptDataConnection()) {
819d3f15c6SIngo Weinhold						numRead = 1;
829d3f15c6SIngo Weinhold						while (numRead > 0) {
83dba5824bSPhil Greenway							memset(buf, 0, sizeof(buf));
849d3f15c6SIngo Weinhold							numRead = fData->Receive(buf, sizeof(buf) - 1);
85dba5824bSPhil Greenway							listing += buf;
86dba5824bSPhil Greenway							printf(buf);
87dba5824bSPhil Greenway						}
889d3f15c6SIngo Weinhold						if (_GetReply(replyString, code, codeType)) {
899d3f15c6SIngo Weinhold							if (codeType <= 2)
90dba5824bSPhil Greenway								rc = true;
91dba5824bSPhil Greenway						}
92dba5824bSPhil Greenway					}
93dba5824bSPhil Greenway				}
94dba5824bSPhil Greenway			}
95dba5824bSPhil Greenway		}
96dba5824bSPhil Greenway	}
97dba5824bSPhil Greenway
989d3f15c6SIngo Weinhold	delete fData;
996a739773SCodeforEvolution	fData = NULL;
100dba5824bSPhil Greenway
101dba5824bSPhil Greenway	return rc;
102dba5824bSPhil Greenway}
103dba5824bSPhil Greenway
104dba5824bSPhil Greenway
1059d3f15c6SIngo Weinholdbool
1069d3f15c6SIngo WeinholdFtpClient::PrintWorkingDir(string& dir)
107dba5824bSPhil Greenway{
108dba5824bSPhil Greenway	bool rc = false;
1099d3f15c6SIngo Weinhold	int code, codeType;
1106a739773SCodeforEvolution	string cmd = "PWD";
1116a739773SCodeforEvolution	string replyString;
112dba5824bSPhil Greenway	long i;
113476fad7fSMatt Madia
1149d3f15c6SIngo Weinhold	if (_SendRequest(cmd) == true) {
1159d3f15c6SIngo Weinhold		if (_GetReply(replyString, code, codeType) == true) {
1169d3f15c6SIngo Weinhold			if (codeType == 2) {
1179d3f15c6SIngo Weinhold				i = replyString.find('"');
1189d3f15c6SIngo Weinhold				if (i != -1) {
119dba5824bSPhil Greenway					i++;
1209d3f15c6SIngo Weinhold					dir = replyString.substr(i, replyString.find('"') - i);
121dba5824bSPhil Greenway					rc = true;
122dba5824bSPhil Greenway				}
123dba5824bSPhil Greenway			}
124dba5824bSPhil Greenway		}
125dba5824bSPhil Greenway	}
126dba5824bSPhil Greenway
127dba5824bSPhil Greenway	return rc;
128dba5824bSPhil Greenway}
129dba5824bSPhil Greenway
130dba5824bSPhil Greenway
1319d3f15c6SIngo Weinholdbool
1326a739773SCodeforEvolutionFtpClient::Connect(const string& server, const string& login,
1336a739773SCodeforEvolution	const string& passwd)
134dba5824bSPhil Greenway{
135dba5824bSPhil Greenway	bool rc = false;
1369d3f15c6SIngo Weinhold	int code, codeType;
1379d3f15c6SIngo Weinhold	string cmd, replyString;
138dba5824bSPhil Greenway	BNetAddress addr;
139476fad7fSMatt Madia
1409d3f15c6SIngo Weinhold	delete fControl;
1419d3f15c6SIngo Weinhold	delete fData;
142476fad7fSMatt Madia
1436a739773SCodeforEvolution	fControl = new BNetEndpoint();
1449d3f15c6SIngo Weinhold
1459d3f15c6SIngo Weinhold	if (fControl->InitCheck() != B_NO_ERROR)
146dba5824bSPhil Greenway		return false;
1479d3f15c6SIngo Weinhold
148dba5824bSPhil Greenway	addr.SetTo(server.c_str(), "tcp", "ftp");
1499d3f15c6SIngo Weinhold	if (fControl->Connect(addr) == B_NO_ERROR) {
150dba5824bSPhil Greenway		// read the welcome message, do the login
151476fad7fSMatt Madia
1529d3f15c6SIngo Weinhold		if (_GetReply(replyString, code, codeType)) {
1539d3f15c6SIngo Weinhold			if (code != 421 && codeType != 5) {
1549d3f15c6SIngo Weinhold				cmd = "USER ";
1559d3f15c6SIngo Weinhold				cmd += login;
1569d3f15c6SIngo Weinhold				_SendRequest(cmd);
1579d3f15c6SIngo Weinhold
1589d3f15c6SIngo Weinhold				if (_GetReply(replyString, code, codeType)) {
1599d3f15c6SIngo Weinhold					switch (code) {
160476fad7fSMatt Madia						case 230:
161476fad7fSMatt Madia						case 202:
162dba5824bSPhil Greenway							rc = true;
1639d3f15c6SIngo Weinhold							break;
1649d3f15c6SIngo Weinhold
165dba5824bSPhil Greenway						case 331:  // password needed
1669d3f15c6SIngo Weinhold							cmd = "PASS ";
1679d3f15c6SIngo Weinhold							cmd += passwd;
1689d3f15c6SIngo Weinhold							_SendRequest(cmd);
1699d3f15c6SIngo Weinhold							if (_GetReply(replyString, code, codeType)) {
1709d3f15c6SIngo Weinhold								if (codeType == 2)
171dba5824bSPhil Greenway									rc = true;
172dba5824bSPhil Greenway							}
1739d3f15c6SIngo Weinhold							break;
1749d3f15c6SIngo Weinhold
175dba5824bSPhil Greenway						default:
1769d3f15c6SIngo Weinhold							break;
177476fad7fSMatt Madia
178dba5824bSPhil Greenway					}
179dba5824bSPhil Greenway				}
180dba5824bSPhil Greenway			}
181dba5824bSPhil Greenway		}
182476fad7fSMatt Madia	}
183dba5824bSPhil Greenway
1849d3f15c6SIngo Weinhold	if (rc == true)
1859d3f15c6SIngo Weinhold		_SetState(ftp_connected);
1869d3f15c6SIngo Weinhold	else {
1879d3f15c6SIngo Weinhold		delete fControl;
1886a739773SCodeforEvolution		fControl = NULL;
189dba5824bSPhil Greenway	}
1909d3f15c6SIngo Weinhold
191dba5824bSPhil Greenway	return rc;
192dba5824bSPhil Greenway}
193dba5824bSPhil Greenway
194dba5824bSPhil Greenway
1959d3f15c6SIngo Weinholdbool
1969d3f15c6SIngo WeinholdFtpClient::PutFile(const string& local, const string& remote, ftp_mode mode)
197dba5824bSPhil Greenway{
198dba5824bSPhil Greenway	bool rc = false;
1999d3f15c6SIngo Weinhold	string cmd, replyString;
2009d3f15c6SIngo Weinhold	int code, codeType, rlen, slen, i;
201dba5824bSPhil Greenway	BFile infile(local.c_str(), B_READ_ONLY);
2026a739773SCodeforEvolution	char buf[8192];
2036a739773SCodeforEvolution	char sbuf[16384];
2046a739773SCodeforEvolution	char* stmp;
205476fad7fSMatt Madia
2069d3f15c6SIngo Weinhold	if (infile.InitCheck() != B_NO_ERROR)
207dba5824bSPhil Greenway		return false;
2089d3f15c6SIngo Weinhold
2099d3f15c6SIngo Weinhold	if (mode == binary_mode)
210dba5824bSPhil Greenway		cmd = "TYPE I";
211dba5824bSPhil Greenway	else
212dba5824bSPhil Greenway		cmd = "TYPE A";
213476fad7fSMatt Madia
2149d3f15c6SIngo Weinhold	if (_SendRequest(cmd))
2159d3f15c6SIngo Weinhold		_GetReply(replyString, code, codeType);
216dba5824bSPhil Greenway
2179d3f15c6SIngo Weinhold	try {
2189d3f15c6SIngo Weinhold		if (_OpenDataConnection()) {
219dba5824bSPhil Greenway			cmd = "STOR ";
220dba5824bSPhil Greenway			cmd += remote;
2219d3f15c6SIngo Weinhold
2229d3f15c6SIngo Weinhold			if (_SendRequest(cmd)) {
2239d3f15c6SIngo Weinhold				if (_GetReply(replyString, code, codeType)) {
2249d3f15c6SIngo Weinhold					if (codeType <= 2) {
2259d3f15c6SIngo Weinhold						if (_AcceptDataConnection()) {
226dba5824bSPhil Greenway							rlen = 1;
2279d3f15c6SIngo Weinhold							while (rlen > 0) {
228dba5824bSPhil Greenway								memset(buf, 0, sizeof(buf));
229dba5824bSPhil Greenway								memset(sbuf, 0, sizeof(sbuf));
2309d3f15c6SIngo Weinhold								rlen = infile.Read((void*)buf, sizeof(buf));
231dba5824bSPhil Greenway								slen = rlen;
232dba5824bSPhil Greenway								stmp = buf;
2339d3f15c6SIngo Weinhold								if (mode == ascii_mode) {
234dba5824bSPhil Greenway									stmp = sbuf;
235dba5824bSPhil Greenway									slen = 0;
2369d3f15c6SIngo Weinhold									for (i = 0; i < rlen; i++) {
2379d3f15c6SIngo Weinhold										if (buf[i] == '\n') {
238dba5824bSPhil Greenway											*stmp = '\r';
239dba5824bSPhil Greenway											stmp++;
240dba5824bSPhil Greenway											slen++;
241dba5824bSPhil Greenway										}
242dba5824bSPhil Greenway										*stmp = buf[i];
243dba5824bSPhil Greenway										stmp++;
244dba5824bSPhil Greenway										slen++;
245dba5824bSPhil Greenway									}
246dba5824bSPhil Greenway									stmp = sbuf;
247dba5824bSPhil Greenway								}
2489d3f15c6SIngo Weinhold								if (slen > 0) {
2499d3f15c6SIngo Weinhold									if (fData->Send(stmp, slen) < 0)
250dba5824bSPhil Greenway										throw "bail";
251dba5824bSPhil Greenway								}
252dba5824bSPhil Greenway							}
253476fad7fSMatt Madia
254dba5824bSPhil Greenway							rc = true;
255dba5824bSPhil Greenway						}
256dba5824bSPhil Greenway					}
257dba5824bSPhil Greenway				}
258dba5824bSPhil Greenway			}
259dba5824bSPhil Greenway		}
260dba5824bSPhil Greenway	}
2619d3f15c6SIngo Weinhold
2629d3f15c6SIngo Weinhold	catch(const char* errorString)
263476fad7fSMatt Madia	{
264dba5824bSPhil Greenway	}
2659d3f15c6SIngo Weinhold
2669d3f15c6SIngo Weinhold	delete fData;
2676a739773SCodeforEvolution	fData = NULL;
2689d3f15c6SIngo Weinhold
2699d3f15c6SIngo Weinhold	if (rc) {
2709d3f15c6SIngo Weinhold		_GetReply(replyString, code, codeType);
2719d3f15c6SIngo Weinhold		rc = codeType <= 2;
272dba5824bSPhil Greenway	}
273476fad7fSMatt Madia
274dba5824bSPhil Greenway	return rc;
275dba5824bSPhil Greenway}
276dba5824bSPhil Greenway
277dba5824bSPhil Greenway
2789d3f15c6SIngo Weinholdbool
2799d3f15c6SIngo WeinholdFtpClient::GetFile(const string& remote, const string& local, ftp_mode mode)
280dba5824bSPhil Greenway{
281dba5824bSPhil Greenway	bool rc = false;
2829d3f15c6SIngo Weinhold	string cmd, replyString;
2839d3f15c6SIngo Weinhold	int code, codeType, rlen, slen, i;
284dba5824bSPhil Greenway	BFile outfile(local.c_str(), B_READ_WRITE | B_CREATE_FILE);
2856a739773SCodeforEvolution	char buf[8192];
2866a739773SCodeforEvolution	char sbuf[16384];
2876a739773SCodeforEvolution	char* stmp;
2889d3f15c6SIngo Weinhold	bool writeError = false;
2899d3f15c6SIngo Weinhold
2909d3f15c6SIngo Weinhold	if (outfile.InitCheck() != B_NO_ERROR)
291dba5824bSPhil Greenway		return false;
2929d3f15c6SIngo Weinhold
2939d3f15c6SIngo Weinhold	if (mode == binary_mode)
294dba5824bSPhil Greenway		cmd = "TYPE I";
295dba5824bSPhil Greenway	else
296dba5824bSPhil Greenway		cmd = "TYPE A";
297dba5824bSPhil Greenway
2989d3f15c6SIngo Weinhold	if (_SendRequest(cmd))
2999d3f15c6SIngo Weinhold		_GetReply(replyString, code, codeType);
3009d3f15c6SIngo Weinhold
3019d3f15c6SIngo Weinhold	if (_OpenDataConnection()) {
302dba5824bSPhil Greenway		cmd = "RETR ";
303dba5824bSPhil Greenway		cmd += remote;
304476fad7fSMatt Madia
3059d3f15c6SIngo Weinhold		if (_SendRequest(cmd)) {
3069d3f15c6SIngo Weinhold			if (_GetReply(replyString, code, codeType)) {
3079d3f15c6SIngo Weinhold				if (codeType <= 2) {
3089d3f15c6SIngo Weinhold					if (_AcceptDataConnection()) {
309dba5824bSPhil Greenway						rlen = 1;
310dba5824bSPhil Greenway						rc = true;
3119d3f15c6SIngo Weinhold						while (rlen > 0) {
312dba5824bSPhil Greenway							memset(buf, 0, sizeof(buf));
313dba5824bSPhil Greenway							memset(sbuf, 0, sizeof(sbuf));
3149d3f15c6SIngo Weinhold							rlen = fData->Receive(buf, sizeof(buf));
315476fad7fSMatt Madia
3169d3f15c6SIngo Weinhold							if (rlen > 0) {
3179d3f15c6SIngo Weinhold
318dba5824bSPhil Greenway								slen = rlen;
319dba5824bSPhil Greenway								stmp = buf;
3209d3f15c6SIngo Weinhold								if (mode == ascii_mode) {
321dba5824bSPhil Greenway									stmp = sbuf;
322dba5824bSPhil Greenway									slen = 0;
3239d3f15c6SIngo Weinhold									for (i = 0; i < rlen; i++) {
3249d3f15c6SIngo Weinhold										if (buf[i] == '\r')
325dba5824bSPhil Greenway											i++;
326dba5824bSPhil Greenway										*stmp = buf[i];
327dba5824bSPhil Greenway										stmp++;
328dba5824bSPhil Greenway										slen++;
329dba5824bSPhil Greenway									}
330dba5824bSPhil Greenway									stmp = sbuf;
331dba5824bSPhil Greenway								}
3329d3f15c6SIngo Weinhold
3339d3f15c6SIngo Weinhold								if (slen > 0) {
3349d3f15c6SIngo Weinhold									if (outfile.Write(stmp, slen) < 0)
335476fad7fSMatt Madia										writeError = true;
336dba5824bSPhil Greenway								}
337dba5824bSPhil Greenway							}
338dba5824bSPhil Greenway						}
339dba5824bSPhil Greenway					}
340dba5824bSPhil Greenway				}
341dba5824bSPhil Greenway			}
342dba5824bSPhil Greenway		}
343dba5824bSPhil Greenway	}
3449d3f15c6SIngo Weinhold
3459d3f15c6SIngo Weinhold	delete fData;
3466a739773SCodeforEvolution	fData = NULL;
347476fad7fSMatt Madia
348476fad7fSMatt Madia	if (rc) {
3499d3f15c6SIngo Weinhold		_GetReply(replyString, code, codeType);
3509d3f15c6SIngo Weinhold		rc = (codeType <= 2 && writeError == false);
3519d3f15c6SIngo Weinhold	}
352dba5824bSPhil Greenway	return rc;
353dba5824bSPhil Greenway}
354dba5824bSPhil Greenway
3559d3f15c6SIngo Weinhold
356dba5824bSPhil Greenway// Note: this only works for local remote moves, cross filesystem moves
357dba5824bSPhil Greenway// will not work
3589d3f15c6SIngo Weinholdbool
3599d3f15c6SIngo WeinholdFtpClient::MoveFile(const string& oldPath, const string& newPath)
360dba5824bSPhil Greenway{
361dba5824bSPhil Greenway	bool rc = false;
362dba5824bSPhil Greenway	string from = "RNFR ";
363dba5824bSPhil Greenway	string to = "RNTO ";
3649d3f15c6SIngo Weinhold	string  replyString;
3659d3f15c6SIngo Weinhold	int code, codeType;
3669d3f15c6SIngo Weinhold
3679d3f15c6SIngo Weinhold	from += oldPath;
3689d3f15c6SIngo Weinhold	to += newPath;
3699d3f15c6SIngo Weinhold
3709d3f15c6SIngo Weinhold	if (_SendRequest(from)) {
3719d3f15c6SIngo Weinhold		if (_GetReply(replyString, code, codeType)) {
3729d3f15c6SIngo Weinhold			if (codeType == 3) {
3739d3f15c6SIngo Weinhold				if (_SendRequest(to)) {
3749d3f15c6SIngo Weinhold					if (_GetReply(replyString, code, codeType)) {
3759d3f15c6SIngo Weinhold						if(codeType == 2)
376dba5824bSPhil Greenway							rc = true;
377dba5824bSPhil Greenway					}
378dba5824bSPhil Greenway				}
379dba5824bSPhil Greenway			}
380dba5824bSPhil Greenway		}
381dba5824bSPhil Greenway	}
382dba5824bSPhil Greenway	return rc;
383dba5824bSPhil Greenway}
384dba5824bSPhil Greenway
385dba5824bSPhil Greenway
386ef89d967SFrançois Revolbool
387ef89d967SFrançois RevolFtpClient::Chmod(const string& path, const string& mod)
388ef89d967SFrançois Revol{
389ef89d967SFrançois Revol	bool rc = false;
390ef89d967SFrançois Revol	int code, codeType;
3916a739773SCodeforEvolution	string cmd = "SITE CHMOD ";
3926a739773SCodeforEvolution	string replyString;
393ef89d967SFrançois Revol
394ef89d967SFrançois Revol	cmd += mod;
395ef89d967SFrançois Revol	cmd += " ";
396ef89d967SFrançois Revol	cmd += path;
397476fad7fSMatt Madia
398ef89d967SFrançois Revol	if (path.length() == 0)
399ef89d967SFrançois Revol		cmd += '/';
4006a739773SCodeforEvolution	printf(B_TRANSLATE("cmd: '%s'\n"), cmd.c_str());
4016a739773SCodeforEvolution
402ef89d967SFrançois Revol	if (_SendRequest(cmd) == true) {
403ef89d967SFrançois Revol		if (_GetReply(replyString, code, codeType) == true) {
4046a739773SCodeforEvolution			printf(B_TRANSLATE("reply: %d, %d\n"), code, codeType);
405ef89d967SFrançois Revol			if (codeType == 2)
406ef89d967SFrançois Revol				rc = true;
407ef89d967SFrançois Revol		}
408ef89d967SFrançois Revol	}
409ef89d967SFrançois Revol	return rc;
410ef89d967SFrançois Revol}
411ef89d967SFrançois Revol
412ef89d967SFrançois Revol
4139d3f15c6SIngo Weinholdvoid
4149d3f15c6SIngo WeinholdFtpClient::SetPassive(bool on)
415dba5824bSPhil Greenway{
4169d3f15c6SIngo Weinhold	if (on)
4179d3f15c6SIngo Weinhold		_SetState(ftp_passive);
418dba5824bSPhil Greenway	else
4199d3f15c6SIngo Weinhold		_ClearState(ftp_passive);
420dba5824bSPhil Greenway}
421dba5824bSPhil Greenway
422dba5824bSPhil Greenway
4239d3f15c6SIngo Weinholdbool
4249d3f15c6SIngo WeinholdFtpClient::_TestState(unsigned long state)
425dba5824bSPhil Greenway{
4269d3f15c6SIngo Weinhold	return ((fState & state) != 0);
427dba5824bSPhil Greenway}
428dba5824bSPhil Greenway
429dba5824bSPhil Greenway
4309d3f15c6SIngo Weinholdvoid
4319d3f15c6SIngo WeinholdFtpClient::_SetState(unsigned long state)
432dba5824bSPhil Greenway{
4339d3f15c6SIngo Weinhold	fState |= state;
434dba5824bSPhil Greenway}
435dba5824bSPhil Greenway
436dba5824bSPhil Greenway
4379d3f15c6SIngo Weinholdvoid
4389d3f15c6SIngo WeinholdFtpClient::_ClearState(unsigned long state)
439dba5824bSPhil Greenway{
4409d3f15c6SIngo Weinhold	fState &= ~state;
441dba5824bSPhil Greenway}
442dba5824bSPhil Greenway
443dba5824bSPhil Greenway
4449d3f15c6SIngo Weinholdbool
4459d3f15c6SIngo WeinholdFtpClient::_SendRequest(const string& cmd)
446dba5824bSPhil Greenway{
447dba5824bSPhil Greenway	bool rc = false;
448dba5824bSPhil Greenway	string ccmd = cmd;
449476fad7fSMatt Madia
4509d3f15c6SIngo Weinhold	if (fControl != 0) {
4519d3f15c6SIngo Weinhold		if (cmd.find("PASS") != string::npos)
452c4f9b1d1SStephan Aßmus			printf(B_TRANSLATE("PASS <suppressed>  (real password sent)\n"));
453dba5824bSPhil Greenway		else
454dba5824bSPhil Greenway			printf("%s\n", ccmd.c_str());
4559d3f15c6SIngo Weinhold
456dba5824bSPhil Greenway		ccmd += "\r\n";
4579d3f15c6SIngo Weinhold		if (fControl->Send(ccmd.c_str(), ccmd.length()) >= 0)
458dba5824bSPhil Greenway			rc = true;
459dba5824bSPhil Greenway	}
460476fad7fSMatt Madia
461dba5824bSPhil Greenway	return rc;
462dba5824bSPhil Greenway}
463dba5824bSPhil Greenway
464dba5824bSPhil Greenway
4659d3f15c6SIngo Weinholdbool
4669d3f15c6SIngo WeinholdFtpClient::_GetReplyLine(string& line)
467dba5824bSPhil Greenway{
468dba5824bSPhil Greenway	bool rc = false;
469dba5824bSPhil Greenway	int c = 0;
470dba5824bSPhil Greenway	bool done = false;
471dba5824bSPhil Greenway
4726a739773SCodeforEvolution	line = "";
4736a739773SCodeforEvolution		// Thanks to Stephen van Egmond for catching a bug here
474476fad7fSMatt Madia
4756a739773SCodeforEvolution	if (fControl != NULL) {
476dba5824bSPhil Greenway		rc = true;
4779d3f15c6SIngo Weinhold		while (done == false && fControl->Receive(&c, 1) > 0) {
4789d3f15c6SIngo Weinhold			if (c == EOF || c == xEOF || c == '\n') {
479dba5824bSPhil Greenway				done = true;
480dba5824bSPhil Greenway			} else {
4819d3f15c6SIngo Weinhold				if (c == IAC) {
4829d3f15c6SIngo Weinhold					fControl->Receive(&c, 1);
4839d3f15c6SIngo Weinhold					switch (c) {
484dba5824bSPhil Greenway						unsigned char treply[3];
485dba5824bSPhil Greenway						case WILL:
486dba5824bSPhil Greenway						case WONT:
4879d3f15c6SIngo Weinhold							fControl->Receive(&c, 1);
488dba5824bSPhil Greenway							treply[0] = IAC;
489dba5824bSPhil Greenway							treply[1] = DONT;
490dba5824bSPhil Greenway							treply[2] = c;
4919d3f15c6SIngo Weinhold							fControl->Send(treply, 3);
492dba5824bSPhil Greenway						break;
4939d3f15c6SIngo Weinhold
494dba5824bSPhil Greenway						case DO:
495dba5824bSPhil Greenway						case DONT:
4969d3f15c6SIngo Weinhold							fControl->Receive(&c, 1);
4979d3f15c6SIngo Weinhold							fControl->Receive(&c, 1);
498dba5824bSPhil Greenway							treply[0] = IAC;
499dba5824bSPhil Greenway							treply[1] = WONT;
500dba5824bSPhil Greenway							treply[2] = c;
5019d3f15c6SIngo Weinhold							fControl->Send(treply, 3);
502dba5824bSPhil Greenway						break;
503476fad7fSMatt Madia
504dba5824bSPhil Greenway						case EOF:
505dba5824bSPhil Greenway						case xEOF:
506dba5824bSPhil Greenway							done = true;
507dba5824bSPhil Greenway						break;
508476fad7fSMatt Madia
509dba5824bSPhil Greenway						default:
510dba5824bSPhil Greenway							line += c;
511dba5824bSPhil Greenway						break;
512dba5824bSPhil Greenway					}
513dba5824bSPhil Greenway				} else {
514dba5824bSPhil Greenway					// normal char
5159d3f15c6SIngo Weinhold					if (c != '\r')
516dba5824bSPhil Greenway						line += c;
517dba5824bSPhil Greenway				}
518dba5824bSPhil Greenway			}
519476fad7fSMatt Madia		}
520dba5824bSPhil Greenway	}
521476fad7fSMatt Madia
522dba5824bSPhil Greenway	return rc;
523dba5824bSPhil Greenway}
524dba5824bSPhil Greenway
525dba5824bSPhil Greenway
5269d3f15c6SIngo Weinholdbool
5279d3f15c6SIngo WeinholdFtpClient::_GetReply(string& outString, int& outCode, int& codeType)
528dba5824bSPhil Greenway{
529dba5824bSPhil Greenway	bool rc = false;
5309d3f15c6SIngo Weinhold	string line, tempString;
531476fad7fSMatt Madia
532dba5824bSPhil Greenway	//
533dba5824bSPhil Greenway	// comment from the ncftp source:
534dba5824bSPhil Greenway	//
535476fad7fSMatt Madia
536dba5824bSPhil Greenway	/* RFC 959 states that a reply may span multiple lines.  A single
537dba5824bSPhil Greenway	 * line message would have the 3-digit code <space> then the msg.
538dba5824bSPhil Greenway	 * A multi-line message would have the code <dash> and the first
539dba5824bSPhil Greenway	 * line of the msg, then additional lines, until the last line,
540dba5824bSPhil Greenway	 * which has the code <space> and last line of the msg.
541dba5824bSPhil Greenway	 *
542dba5824bSPhil Greenway	 * For example:
543dba5824bSPhil Greenway	 *	123-First line
544dba5824bSPhil Greenway	 *	Second line
545dba5824bSPhil Greenway	 *	234 A line beginning with numbers
546dba5824bSPhil Greenway	 *	123 The last line
547dba5824bSPhil Greenway	 */
5489d3f15c6SIngo Weinhold
5496a739773SCodeforEvolution	rc = _GetReplyLine(line);
5506a739773SCodeforEvolution	if (rc == true) {
5519d3f15c6SIngo Weinhold		outString = line;
5529d3f15c6SIngo Weinhold		outString += '\n';
5539d3f15c6SIngo Weinhold		printf(outString.c_str());
5549d3f15c6SIngo Weinhold		tempString = line.substr(0, 3);
5559d3f15c6SIngo Weinhold		outCode = atoi(tempString.c_str());
556476fad7fSMatt Madia
5579d3f15c6SIngo Weinhold		if (line[3] == '-') {
5586a739773SCodeforEvolution			rc = _GetReplyLine(line);
5596a739773SCodeforEvolution			while (rc == true) {
560476fad7fSMatt Madia				outString += line;
5619d3f15c6SIngo Weinhold				outString += '\n';
5629d3f15c6SIngo Weinhold				printf(outString.c_str());
563dba5824bSPhil Greenway				// we're done with nnn when we get to a "nnn blahblahblah"
5649d3f15c6SIngo Weinhold				if ((line.find(tempString) == 0) && line[3] == ' ')
565dba5824bSPhil Greenway					break;
5666a739773SCodeforEvolution
5676a739773SCodeforEvolution				rc = _GetReplyLine(line);
568dba5824bSPhil Greenway			}
569dba5824bSPhil Greenway		}
570dba5824bSPhil Greenway	}
571476fad7fSMatt Madia
5729d3f15c6SIngo Weinhold	if (!rc && outCode != 421) {
573c4f9b1d1SStephan Aßmus		outString += B_TRANSLATE("Remote host has closed the connection.\n");
5749d3f15c6SIngo Weinhold		outCode = 421;
575dba5824bSPhil Greenway	}
576dba5824bSPhil Greenway
5779d3f15c6SIngo Weinhold	if (outCode == 421) {
578476fad7fSMatt Madia		delete fControl;
5796a739773SCodeforEvolution		fControl = NULL;
5809d3f15c6SIngo Weinhold		_ClearState(ftp_connected);
581dba5824bSPhil Greenway	}
5829d3f15c6SIngo Weinhold
5839d3f15c6SIngo Weinhold	codeType = outCode / 100;
584476fad7fSMatt Madia
585dba5824bSPhil Greenway	return rc;
586dba5824bSPhil Greenway}
587dba5824bSPhil Greenway
588dba5824bSPhil Greenway
5899d3f15c6SIngo Weinholdbool
5909d3f15c6SIngo WeinholdFtpClient::_OpenDataConnection()
591dba5824bSPhil Greenway{
5929d3f15c6SIngo Weinhold	string host, cmd, replyString;
593dba5824bSPhil Greenway	unsigned short port;
594dba5824bSPhil Greenway	BNetAddress addr;
5959d3f15c6SIngo Weinhold	int i, code, codeType;
596dba5824bSPhil Greenway	bool rc = false;
597dba5824bSPhil Greenway	struct sockaddr_in sa;
598476fad7fSMatt Madia
5999d3f15c6SIngo Weinhold	delete fData;
6006a739773SCodeforEvolution	fData = NULL;
601476fad7fSMatt Madia
6026a739773SCodeforEvolution	fData = new BNetEndpoint();
603476fad7fSMatt Madia
6049d3f15c6SIngo Weinhold	if (_TestState(ftp_passive)) {
605dba5824bSPhil Greenway		// Here we send a "pasv" command and connect to the remote server
606dba5824bSPhil Greenway		// on the port it sends back to us
607dba5824bSPhil Greenway		cmd = "PASV";
6089d3f15c6SIngo Weinhold		if (_SendRequest(cmd)) {
6099d3f15c6SIngo Weinhold			if (_GetReply(replyString, code, codeType)) {
6109d3f15c6SIngo Weinhold
6119d3f15c6SIngo Weinhold				if (codeType == 2) {
612dba5824bSPhil Greenway					 //  It should give us something like:
613dba5824bSPhil Greenway			 		 // "227 Entering Passive Mode (192,168,1,1,10,187)"
614dba5824bSPhil Greenway					int paddr[6];
615dba5824bSPhil Greenway					unsigned char ucaddr[6];
6169d3f15c6SIngo Weinhold
6179d3f15c6SIngo Weinhold					i = replyString.find('(');
618dba5824bSPhil Greenway					i++;
619dba5824bSPhil Greenway
6206a739773SCodeforEvolution					replyString = replyString.substr(i,
6216a739773SCodeforEvolution						replyString.find(')') - i);
6229d3f15c6SIngo Weinhold					if (sscanf(replyString.c_str(), "%d,%d,%d,%d,%d,%d",
623476fad7fSMatt Madia						&paddr[0], &paddr[1], &paddr[2], &paddr[3],
6249d3f15c6SIngo Weinhold						&paddr[4], &paddr[5]) != 6) {
6256a739773SCodeforEvolution							// Cannot do passive.
6266a739773SCodeforEvolution							// Do a little harmless rercursion here.
6276a739773SCodeforEvolution							_ClearState(ftp_passive);
6286a739773SCodeforEvolution							return _OpenDataConnection();
629dba5824bSPhil Greenway						}
6309d3f15c6SIngo Weinhold
6319d3f15c6SIngo Weinhold					for (i = 0; i < 6; i++)
6329d3f15c6SIngo Weinhold						ucaddr[i] = (unsigned char)(paddr[i] & 0xff);
6339d3f15c6SIngo Weinhold
634dba5824bSPhil Greenway					memcpy(&sa.sin_addr, &ucaddr[0], (size_t) 4);
635dba5824bSPhil Greenway					memcpy(&sa.sin_port, &ucaddr[4], (size_t) 2);
636dba5824bSPhil Greenway					addr.SetTo(sa);
6379d3f15c6SIngo Weinhold					if (fData->Connect(addr) == B_NO_ERROR)
638dba5824bSPhil Greenway						rc = true;
6399d3f15c6SIngo Weinhold
640dba5824bSPhil Greenway				}
641dba5824bSPhil Greenway			}
642dba5824bSPhil Greenway		} else {
643dba5824bSPhil Greenway			// cannot do passive.  Do a little harmless rercursion here
6449d3f15c6SIngo Weinhold			_ClearState(ftp_passive);
6459d3f15c6SIngo Weinhold			rc = _OpenDataConnection();
646dba5824bSPhil Greenway		}
647dba5824bSPhil Greenway
648dba5824bSPhil Greenway	} else {
649dba5824bSPhil Greenway		// Here we bind to a local port and send a PORT command
6509d3f15c6SIngo Weinhold		if (fData->Bind() == B_NO_ERROR) {
651dba5824bSPhil Greenway			char buf[255];
6529d3f15c6SIngo Weinhold
6539d3f15c6SIngo Weinhold			fData->Listen();
6549d3f15c6SIngo Weinhold			addr = fData->LocalAddr();
655dba5824bSPhil Greenway			addr.GetAddr(buf, &port);
656dba5824bSPhil Greenway			host = buf;
6579d3f15c6SIngo Weinhold
6589d3f15c6SIngo Weinhold			i = 0;
6599d3f15c6SIngo Weinhold			while (i >= 0) {
660dba5824bSPhil Greenway				i = host.find('.', i);
6619d3f15c6SIngo Weinhold				if (i >= 0)
662dba5824bSPhil Greenway					host[i] = ',';
663dba5824bSPhil Greenway			}
6649d3f15c6SIngo Weinhold
665dba5824bSPhil Greenway			sprintf(buf, ",%d,%d", (port & 0xff00) >> 8, port & 0x00ff);
666dba5824bSPhil Greenway			cmd = "PORT ";
6679d3f15c6SIngo Weinhold			cmd += host;
6689d3f15c6SIngo Weinhold			cmd += buf;
6699d3f15c6SIngo Weinhold			_SendRequest(cmd);
6709d3f15c6SIngo Weinhold			_GetReply(replyString, code, codeType);
671dba5824bSPhil Greenway			// PORT failure is in the 500-range
6729d3f15c6SIngo Weinhold			if (codeType == 2)
673dba5824bSPhil Greenway				rc = true;
674dba5824bSPhil Greenway		}
675dba5824bSPhil Greenway	}
676476fad7fSMatt Madia
677dba5824bSPhil Greenway	return rc;
678dba5824bSPhil Greenway}
679dba5824bSPhil Greenway
680dba5824bSPhil Greenway
6819d3f15c6SIngo Weinholdbool
6829d3f15c6SIngo WeinholdFtpClient::_AcceptDataConnection()
683dba5824bSPhil Greenway{
6849d3f15c6SIngo Weinhold	BNetEndpoint* endPoint;
685dba5824bSPhil Greenway	bool rc = false;
686dba5824bSPhil Greenway
687476fad7fSMatt Madia	if (_TestState(ftp_passive) == false) {
6886a739773SCodeforEvolution		if (fData != NULL) {
6899d3f15c6SIngo Weinhold			endPoint = fData->Accept();
6906a739773SCodeforEvolution			if (endPoint != NULL) {
6919d3f15c6SIngo Weinhold				delete fData;
6929d3f15c6SIngo Weinhold				fData = endPoint;
693dba5824bSPhil Greenway				rc = true;
694476fad7fSMatt Madia			}
695dba5824bSPhil Greenway		}
696476fad7fSMatt Madia
6979d3f15c6SIngo Weinhold	}
6989d3f15c6SIngo Weinhold	else
699dba5824bSPhil Greenway		rc = true;
7009d3f15c6SIngo Weinhold
701476fad7fSMatt Madia	return rc;
702dba5824bSPhil Greenway}
703