Added a parameter to semaphore constructor to avoid ambiguity
[pwlib.git] / src / ptclib / ftpclnt.cxx
blob1d09e46f29d415adbdbaabaeddfc05b02675a66f
1 /*
2 * ftpclnt.cxx
4 * FTP client class.
6 * Portable Windows Library
8 * Copyright (c) 1993-2002 Equivalence Pty. Ltd.
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/
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.
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
26 * $Log$
27 * Revision 1.11 2002/11/06 22:47:24 robertj
28 * Fixed header comment (copyright etc)
30 * Revision 1.10 2002/10/10 04:43:44 robertj
31 * VxWorks port, thanks Martijn Roest
33 * Revision 1.9 2000/06/21 01:14:23 robertj
34 * AIX port, thanks Wolfgang Platzer (wolfgang.platzer@infonova.at).
36 * Revision 1.8 2000/04/07 06:29:46 rogerh
37 * Add a short term workaround for an Internal Compiler Error on MAC OS X when
38 * returning certain types of PString. Submitted by Kevin Packard.
40 * Revision 1.7 1998/12/23 00:34:55 robertj
41 * Fixed normal TCP socket support after adding SOCKS support.
43 * Revision 1.6 1998/12/22 10:29:42 robertj
44 * Added support for SOCKS based channels.
46 * Revision 1.5 1998/12/18 03:48:32 robertj
47 * Fixed wanring on PPC linux compile
49 * Revision 1.4 1998/11/30 04:50:47 robertj
50 * New directory structure
52 * Revision 1.3 1998/09/23 06:22:00 robertj
53 * Added open source copyright license.
55 * Revision 1.2 1997/03/28 13:06:58 robertj
56 * made STAT command more robust for getting file info from weird FTP servers.
58 * Revision 1.1 1996/09/14 13:02:18 robertj
59 * Initial revision
63 #include <ptlib.h>
64 #include <ptlib/sockets.h>
65 #include <ptclib/ftp.h>
68 /////////////////////////////////////////////////////////
69 // FTP Client
71 PFTPClient::PFTPClient()
76 PFTPClient::~PFTPClient()
78 Close();
82 BOOL PFTPClient::Close()
84 if (!IsOpen())
85 return FALSE;
86 BOOL ok = ExecuteCommand(QUIT)/100 == 2;
87 return PFTP::Close() && ok;
90 BOOL PFTPClient::OnOpen()
92 if (!ReadResponse() || lastResponseCode != 220)
93 return FALSE;
95 // the default data port for a server is the adjacent port
96 PIPSocket::Address remoteHost;
97 PIPSocket * socket = GetSocket();
98 if (socket == NULL)
99 return FALSE;
101 socket->GetPeerAddress(remoteHost, remotePort);
102 remotePort--;
103 return TRUE;
107 BOOL PFTPClient::LogIn(const PString & username, const PString & password)
109 if (ExecuteCommand(USER, username)/100 != 3)
110 return FALSE;
111 return ExecuteCommand(PASS, password)/100 == 2;
115 PString PFTPClient::GetSystemType()
117 if (ExecuteCommand(SYST)/100 != 2)
118 return PString();
120 return lastResponseInfo.Left(lastResponseInfo.Find(' '));
124 BOOL PFTPClient::SetType(RepresentationType type)
126 static const char * const typeCode[] = { "A", "E", "I" };
127 PAssert((PINDEX)type < PARRAYSIZE(typeCode), PInvalidParameter);
128 return ExecuteCommand(TYPE, typeCode[type])/100 == 2;
132 BOOL PFTPClient::ChangeDirectory(const PString & dirPath)
134 return ExecuteCommand(CWD, dirPath)/100 == 2;
138 PString PFTPClient::GetCurrentDirectory()
140 if (ExecuteCommand(PWD) != 257)
141 return PString();
143 PINDEX quote1 = lastResponseInfo.Find('"');
144 if (quote1 == P_MAX_INDEX)
145 return PString();
147 PINDEX quote2 = quote1 + 1;
148 do {
149 quote2 = lastResponseInfo.Find('"', quote2);
150 if (quote2 == P_MAX_INDEX)
151 return PString();
153 while (lastResponseInfo[quote2]=='"' && lastResponseInfo[quote2+1]=='"')
154 quote2 += 2;
156 } while (lastResponseInfo[quote2] != '"');
158 // make Apple's and Tornado's gnu compiler happy
159 PString retval = lastResponseInfo(quote1+1, quote2-1);
160 return retval;
164 PStringArray PFTPClient::GetDirectoryNames(NameTypes type,
165 DataChannelType ctype)
167 return GetDirectoryNames(PString(), type, ctype);
171 PStringArray PFTPClient::GetDirectoryNames(const PString & path,
172 NameTypes type,
173 DataChannelType ctype)
175 SetType(PFTP::ASCII);
177 Commands lcmd = type == DetailedNames ? LIST : NLST;
178 PTCPSocket * socket = ctype != Passive ? NormalClientTransfer(lcmd, path)
179 : PassiveClientTransfer(lcmd, path);
180 if (socket == NULL)
181 return PStringArray();
183 PString response = lastResponseInfo;
184 PString str;
185 int count = 0;
186 while(socket->Read(str.GetPointer(count+1000)+count, 1000))
187 count += socket->GetLastReadCount();
188 str.SetSize(count+1);
190 delete socket;
191 ReadResponse();
192 lastResponseInfo = response + '\n' + lastResponseInfo;
193 return str.Lines();
197 PString PFTPClient::GetFileStatus(const PString & path, DataChannelType ctype)
199 if (ExecuteCommand(STATcmd, path)/100 == 2 && lastResponseInfo.Find(path) != P_MAX_INDEX) {
200 PINDEX start = lastResponseInfo.Find('\n');
201 if (start != P_MAX_INDEX) {
202 PINDEX end = lastResponseInfo.Find('\n', ++start);
203 if (end != P_MAX_INDEX)
204 return lastResponseInfo(start, end-1);
208 PTCPSocket * socket = ctype != Passive ? NormalClientTransfer(LIST, path)
209 : PassiveClientTransfer(LIST, path);
210 if (socket == NULL)
211 return PString();
213 PString str;
214 socket->Read(str.GetPointer(200), 199);
215 str[socket->GetLastReadCount()] = '\0';
216 delete socket;
217 ReadResponse();
219 PINDEX end = str.FindOneOf("\r\n");
220 if (end != P_MAX_INDEX)
221 str[end] = '\0';
222 return str;
226 PTCPSocket * PFTPClient::NormalClientTransfer(Commands cmd,
227 const PString & args)
229 PIPSocket * socket = GetSocket();
230 if (socket == NULL)
231 return NULL;
233 // setup a socket so we can tell the host where to connect to
234 PTCPSocket * listenSocket = (PTCPSocket *)socket->Clone();
235 listenSocket->SetPort(0); // Want new random port number
236 listenSocket->Listen();
238 // The following is just used to automatically delete listenSocket
239 PIndirectChannel autoDeleteSocket;
240 autoDeleteSocket.Open(listenSocket);
242 // get host address and port to send to other end
243 WORD localPort = listenSocket->GetPort();
244 PIPSocket::Address localAddr;
245 socket->GetLocalAddress(localAddr);
247 // send PORT command to host
248 if (!SendPORT(localAddr, localPort))
249 return NULL;
251 if (ExecuteCommand(cmd, args)/100 != 1)
252 return NULL;
254 PTCPSocket * dataSocket = (PTCPSocket *)socket->Clone();
255 if (dataSocket->Accept(*listenSocket))
256 return dataSocket;
258 delete dataSocket;
259 return NULL;
263 PTCPSocket * PFTPClient::PassiveClientTransfer(Commands cmd,
264 const PString & args)
266 PIPSocket::Address passiveAddress;
267 WORD passivePort;
269 if (ExecuteCommand(PASV) != 227)
270 return NULL;
272 PINDEX start = lastResponseInfo.FindOneOf("0123456789");
273 if (start == P_MAX_INDEX)
274 return NULL;
276 PStringArray bytes = lastResponseInfo(start, P_MAX_INDEX).Tokenise(',');
277 if (bytes.GetSize() != 6)
278 return NULL;
280 passiveAddress = PIPSocket::Address((BYTE)bytes[0].AsInteger(),
281 (BYTE)bytes[1].AsInteger(),
282 (BYTE)bytes[2].AsInteger(),
283 (BYTE)bytes[3].AsInteger());
284 passivePort = (WORD)(bytes[4].AsInteger()*256 + bytes[5].AsInteger());
286 PTCPSocket * socket = new PTCPSocket(passiveAddress, passivePort);
287 if (socket->IsOpen())
288 if (ExecuteCommand(cmd, args)/100 == 1)
289 return socket;
291 delete socket;
292 return NULL;
296 PTCPSocket * PFTPClient::GetFile(const PString & filename,
297 DataChannelType channel)
299 return channel != Passive ? NormalClientTransfer(RETR, filename)
300 : PassiveClientTransfer(RETR, filename);
304 PTCPSocket * PFTPClient::PutFile(const PString & filename,
305 DataChannelType channel)
307 return channel != Passive ? NormalClientTransfer(STOR, filename)
308 : PassiveClientTransfer(STOR, filename);
313 // End of File ///////////////////////////////////////////////////////////////