1 //
2 // C++ Implementation: ftp
3 //
4 // Description: This file contains the implementation of the mFTP class with
5 // support to files bigger than 2GB. This is made using the wxLongLong.
6 //
7 //
8 // Author: Max Magalhães Velasques <maxvelasques@gmail.com>, (C) 2006
9 //
10 // Copyright: See COPYING file that comes with this distribution
11 //
12 //
13
14 #include "wxDFast.h"
15
16 class wxInputFTPStream : public wxSocketInputStream
17 {
18 public:
wxInputFTPStream(wxFTP * ftp,wxSocketBase * sock)19 wxInputFTPStream(wxFTP *ftp, wxSocketBase *sock)
20 : wxSocketInputStream(*sock)
21 {
22 m_ftp = ftp;
23 // socket timeout automatically set in GetPort function
24 }
25
~wxInputFTPStream()26 virtual ~wxInputFTPStream()
27 {
28 delete m_i_socket; // keep at top
29
30 // when checking the result, the stream will
31 // almost always show an error, even if the file was
32 // properly transfered, thus, lets just grab the result
33
34 // we are looking for "226 transfer completed"
35 char code = m_ftp->GetResult();
36 if ('2' == code)
37 {
38 // it was a good transfer.
39 // we're done!
40 m_ftp->m_streaming = false;
41 return;
42 }
43 // did we timeout?
44 if (0 == code)
45 {
46 // the connection is probably toast. issue an abort, and
47 // then a close. there won't be any more waiting
48 // for this connection
49 m_ftp->Abort();
50 m_ftp->Close();
51 return;
52 }
53 // There was a problem with the transfer and the server
54 // has acknowledged it. If we issue an "ABORT" now, the user
55 // would get the "226" for the abort and think the xfer was
56 // complete, thus, don't do anything here, just return
57 }
58
59 wxFTP *m_ftp;
60
61 DECLARE_NO_COPY_CLASS(wxInputFTPStream)
62 };
63
GetFileSize(const wxString & fileName)64 wxLongLong mFTP::GetFileSize(const wxString& fileName)
65 {
66 // return the filesize of the given file if possible
67 // return -1 otherwise (predominantly if file doesn't exist
68 // in current dir)
69 wxLongLong filesize = -1;
70
71 // Check for existance of file via wxFTP::FileExists(...)
72 wxLogDebug(wxT("Checking FTP file exists"));
73 if ( FileExists(fileName) )
74 {
75 wxLogDebug(wxT("Foundit"));
76 wxString command;
77
78 // First try "SIZE" command using BINARY(IMAGE) transfermode
79 // Especially UNIX ftp-servers distinguish between the different
80 // transfermodes and reports different filesizes accordingly.
81 // The BINARY size is the interesting one: How much memory
82 // will we need to hold this file?
83 TransferMode oldTransfermode = m_currentTransfermode;
84 SetTransferMode(BINARY);
85 wxLogDebug(wxT("Sending SIZE command"));
86 command << _T("SIZE ") << fileName;
87
88 bool ok = CheckCommand(command, '2');
89
90 if ( ok )
91 {
92 wxLogDebug(wxT("Received ok message"));
93 // The answer should be one line: "213 <filesize>\n"
94 // 213 is File Status (STD9)
95 // "SIZE" is not described anywhere..? It works on most servers
96 long statuscode;
97 wxStringTokenizer lastresult(GetLastResult(),wxT(" "));
98 if (lastresult.CountTokens() == 2)
99 {
100 ok = true;
101 if (!lastresult.GetNextToken().ToLong(&statuscode))
102 ok = false;
103 filesize = MyUtilFunctions::wxstrtolonglong(lastresult.GetNextToken());
104 if (filesize < 0)
105 filesize = 0;
106 }
107 else
108 {
109 // Something bad happened.. A "2yz" reply with no size
110 // Fallback
111 ok = false;
112 }
113 }
114
115 // Set transfermode back to the original. Only the "SIZE"-command
116 // is dependant on transfermode
117 if ( oldTransfermode != NONE )
118 {
119 SetTransferMode(oldTransfermode);
120 }
121
122 // this is not a direct else clause.. The size command might return an
123 // invalid "2yz" reply
124 if ( !ok )
125 {
126 wxLogDebug(wxT("Received error message"));
127 // The server didn't understand the "SIZE"-command or it
128 // returned an invalid reply.
129 // We now try to get details for the file with a "LIST"-command
130 // and then parse the output from there..
131 wxLogDebug(wxT("Getting filelist"));
132 wxArrayString fileList;
133 if ( GetList(fileList, fileName, true) )
134 {
135 if ( !fileList.IsEmpty() )
136 {
137 // We _should_ only get one line in return, but just to be
138 // safe we run through the line(s) returned and look for a
139 // substring containing the name we are looking for. We
140 // stop the iteration at the first occurrence of the
141 // filename. The search is not case-sensitive.
142 bool foundIt = false;
143
144 size_t i;
145 for ( i = 0; !foundIt && i < fileList.Count(); i++ )
146 {
147 foundIt = fileList[i].Upper().Contains(fileName.Upper());
148 }
149 wxLogDebug(wxT("Found the file"));
150
151 if ( foundIt )
152 {
153 // The index i points to the first occurrence of
154 // fileName in the array Now we have to find out what
155 // format the LIST has returned. There are two
156 // "schools": Unix-like
157 //
158 // '-rw-rw-rw- owner group size month day time filename'
159 //
160 // or Windows-like
161 //
162 // 'date size filename'
163
164 // check if the first character is '-'. This would
165 // indicate Unix-style (this also limits this function
166 // to searching for files, not directories)
167 wxLogDebug(wxT("Checking the file list"));
168 if ( fileList[i].Mid(0, 1) == _T("-") )
169 {
170 wxStringTokenizer lastresult(fileList[i],wxT(" "));
171 if (lastresult.CountTokens() == 9)
172 {
173 wxLogDebug(lastresult.GetNextToken());
174 wxLogDebug(lastresult.GetNextToken());
175 wxLogDebug(lastresult.GetNextToken());
176 wxLogDebug(lastresult.GetNextToken());
177 filesize = MyUtilFunctions::wxstrtolonglong(lastresult.GetNextToken());
178 if (filesize < 0)
179 filesize = 0;
180 }
181 else
182 {
183 // Hmm... Invalid response
184 wxLogTrace(FTP_TRACE_MASK,
185 _T("Invalid LIST response"));
186 }
187 }
188 else // Windows-style response (?)
189 {
190 wxStringTokenizer lastresult(fileList[i],wxT(" "));
191 if (lastresult.CountTokens() == 4)
192 {
193 lastresult.GetNextToken();
194 lastresult.GetNextToken();
195 filesize = MyUtilFunctions::wxstrtolonglong(lastresult.GetNextToken());
196 if (filesize < 0)
197 filesize = 0;
198 }
199 else
200 {
201 // something bad happened..?
202 wxLogTrace(FTP_TRACE_MASK,
203 _T("Invalid or unknown LIST response"));
204 }
205 }
206 }
207 }
208 }
209 }
210 }
211
212 // filesize might still be -1 when exiting
213 return filesize;
214 }
215
GetInputStream(const wxString & path)216 wxInputStream *mFTP::GetInputStream(const wxString& path)
217 {
218 if ( ( m_currentTransfermode == NONE ) && !SetTransferMode(BINARY) )
219 return NULL;
220
221 wxSocketBase *sock = GetPort();
222
223 if ( !sock )
224 {
225 m_lastError = wxPROTO_NETERR;
226 return NULL;
227 }
228
229 wxString tmp_str = wxT("RETR ") + wxURI::Unescape(path);
230 if ( !CheckCommand(tmp_str, '1') )
231 return NULL;
232
233 sock = AcceptIfActive(sock);
234 if ( !sock )
235 return NULL;
236
237 sock->SetFlags(wxSOCKET_NOWAIT);
238
239 m_streaming = true;
240
241 wxInputFTPStream *in_stream;
242 in_stream = new wxInputFTPStream(this, sock);
243
244 return in_stream;
245 }
246
247