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