2 // This file is part of the aMule Project.
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002 Tiku
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include <wx/wfstream.h>
28 #include <wx/protocol/http.h>
31 #include "HTTPDownload.h" // Interface declarations
32 #include <common/StringFunctions.h> // Needed for unicode2char
33 #include "OtherFunctions.h" // Needed for CastChild
34 #include "Logger.h" // Needed for AddLogLine*
35 #include <common/Format.h> // Needed for CFormat
36 #include "InternalEvents.h" // Needed for CMuleInternalEvent
37 #include "Preferences.h"
41 #include "inetdownload.h" // Needed for inetDownload
42 #include "muuli_wdr.h" // Needed for ID_CANCEL: Let it here or will fail on win32
43 #include "MuleGifCtrl.h"
46 typedef wxGauge95 wxGaugeControl
;
48 typedef wxGauge wxGaugeControl
;
52 DECLARE_LOCAL_EVENT_TYPE(wxEVT_HTTP_PROGRESS
, wxANY_ID
)
53 DECLARE_LOCAL_EVENT_TYPE(wxEVT_HTTP_SHUTDOWN
, wxANY_ID
)
56 class CHTTPDownloadDialog
: public wxDialog
59 CHTTPDownloadDialog(CHTTPDownloadThread
* thread
)
60 : wxDialog(wxTheApp
->GetTopWindow(), -1, _("Downloading..."),
61 wxDefaultPosition
, wxDefaultSize
, wxDEFAULT_DIALOG_STYLE
| wxSYSTEM_MENU
)
63 downloadDlg(this, true)->Show(this, true);
65 m_progressbar
= CastChild(ID_HTTPDOWNLOADPROGRESS
, wxGaugeControl
);
66 m_progressbar
->SetRange(100);
68 m_ani
= CastChild(ID_ANIMATE
, MuleGifCtrl
);
69 m_ani
->LoadData((const char*)inetDownload
, sizeof(inetDownload
));
75 ~CHTTPDownloadDialog() {
79 void UpdateGauge(int total
, int current
) {
80 CFormat
label(_("( %s / %s )"));
82 label
% CastItoXBytes(current
);
84 label
% CastItoXBytes(total
);
89 CastChild(IDC_DOWNLOADSIZE
, wxStaticText
)->SetLabel(label
.GetString());
91 if (total
&& (total
!= m_progressbar
->GetRange())) {
92 m_progressbar
->SetRange(total
);
95 if (current
&& (current
<= total
)) {
96 m_progressbar
->SetValue(current
);
111 void OnBtnCancel(wxCommandEvent
& WXUNUSED(evt
)) {
112 printf("HTTP download cancelled\n");
117 void OnProgress(CMuleInternalEvent
& evt
) {
118 UpdateGauge(evt
.GetExtraLong(), evt
.GetInt());
121 void OnShutdown(CMuleInternalEvent
& WXUNUSED(evt
)) {
126 CMuleThread
* m_thread
;
128 wxGaugeControl
* m_progressbar
;
130 DECLARE_EVENT_TABLE()
134 BEGIN_EVENT_TABLE(CHTTPDownloadDialog
, wxDialog
)
135 EVT_BUTTON(ID_HTTPCANCEL
, CHTTPDownloadDialog::OnBtnCancel
)
136 EVT_MULE_INTERNAL(wxEVT_HTTP_PROGRESS
, -1, CHTTPDownloadDialog::OnProgress
)
137 EVT_MULE_INTERNAL(wxEVT_HTTP_SHUTDOWN
, -1, CHTTPDownloadDialog::OnShutdown
)
140 DEFINE_LOCAL_EVENT_TYPE(wxEVT_HTTP_PROGRESS
)
141 DEFINE_LOCAL_EVENT_TYPE(wxEVT_HTTP_SHUTDOWN
)
146 CHTTPDownloadThread::CHTTPDownloadThread(const wxChar
* url
, const wxChar
* filename
, HTTP_Download_File file_id
, bool showDialog
)
148 : CMuleThread(wxTHREAD_DETACHED
),
150 : CMuleThread(showDialog
? wxTHREAD_JOINABLE
: wxTHREAD_DETACHED
),
153 m_tempfile(filename
),
160 CHTTPDownloadDialog
* dialog
= new CHTTPDownloadDialog(this);
162 m_companion
= dialog
;
168 CMuleThread::ExitCode
CHTTPDownloadThread::Entry()
174 wxHTTP
* url_handler
= NULL
;
175 wxInputStream
* url_read_stream
= NULL
;
177 printf("HTTP download thread started\n");
179 const CProxyData
* proxy_data
= thePrefs::GetProxyData();
180 bool use_proxy
= proxy_data
!= NULL
&& proxy_data
->m_proxyEnable
;
183 wxFFileOutputStream
outfile(m_tempfile
);
186 throw wxString(CFormat(wxT("Unable to create destination file %s for download!\n")) % m_tempfile
);
189 if ( m_url
.IsEmpty() ) {
190 // Nowhere to download from!
191 throw wxString(wxT("The URL to download can't be empty\n"));
194 url_handler
= new wxHTTP
;
195 url_handler
->SetProxyMode(use_proxy
);
197 url_read_stream
= GetInputStream(&url_handler
, m_url
, use_proxy
);
199 if (!url_read_stream
) {
200 throw wxString(CFormat(wxT("The URL %s returned: %i - Error (%i)!")) % m_url
% url_handler
->GetResponse() % url_handler
->GetError());
203 int download_size
= url_read_stream
->GetSize();
204 printf("Download size: %i\n",download_size
);
206 // Here is our read buffer
207 // <ken> Still, I'm sure 4092 is probably a better size.
208 const unsigned MAX_HTTP_READ
= 4092;
210 char buffer
[MAX_HTTP_READ
];
211 int current_read
= 0;
214 url_read_stream
->Read(buffer
, MAX_HTTP_READ
);
215 current_read
= url_read_stream
->LastRead();
217 total_read
+= current_read
;
218 outfile
.Write(buffer
,current_read
);
219 int current_write
= outfile
.LastWrite();
220 if (current_read
!= current_write
) {
221 throw wxString(wxT("Critical error while writing downloaded file"));
222 } else if (m_companion
) {
224 CMuleInternalEvent
evt(wxEVT_HTTP_PROGRESS
);
225 evt
.SetInt(total_read
);
226 evt
.SetExtraLong(download_size
);
227 wxPostEvent(m_companion
, evt
);
231 } while (current_read
&& !TestDestroy());
233 if (current_read
== 0) {
234 // Download was succesful.
237 } catch (const wxString
& error
) {
238 if (wxFileExists(m_tempfile
)) {
239 wxRemoveFile(m_tempfile
);
242 AddLogLineM(false, error
);
245 delete url_read_stream
;
247 url_handler
->Destroy();
250 printf("HTTP download thread ended\n");
256 void CHTTPDownloadThread::OnExit()
260 CMuleInternalEvent
termEvent(wxEVT_HTTP_SHUTDOWN
);
261 wxPostEvent(m_companion
, termEvent
);
265 // Notice the app that the file finished download
266 CMuleInternalEvent
evt(wxEVT_CORE_FINISHED_HTTP_DOWNLOAD
);
267 evt
.SetInt((int)m_file_id
);
268 evt
.SetExtraLong((long)m_result
);
269 wxPostEvent(wxTheApp
, evt
);
273 //! This function's purpose is to handle redirections in a proper way.
274 wxInputStream
* CHTTPDownloadThread::GetInputStream(wxHTTP
** url_handler
, const wxString
& location
, bool proxy
)
280 if (!location
.StartsWith(wxT("http://"))) {
281 // This is not a http url
282 throw wxString(wxT("Invalid URL for server.met download or http redirection (did you forget 'http://' ?)"));
287 // Remove the "http://"
288 wxString host
= location
.Right(location
.Len() - 7); // strlen("http://") -> 7
290 // I belive this is a bug...
291 // Sometimes "Location" header looks like this:
292 // "http://www.whatever.com:8080http://www.whatever.com/downloads/something.zip"
293 // So let's clean it...
295 int bad_url_pos
= host
.Find(wxT("http://"));
296 wxString location_url
;
297 if (bad_url_pos
!= -1) {
298 // Malformed Location field on header (bug above?)
299 location_url
= host
.Mid(bad_url_pos
);
300 host
= host
.Left(bad_url_pos
);
301 // After the first '/' non-http-related, it's the location part of the URL
302 location_url
= location_url
.Right(location_url
.Len() - 7).AfterFirst(wxT('/'));
304 // Regular Location field
305 // After the first '/', it's the location part of the URL
306 location_url
= host
.AfterFirst(wxT('/'));
307 // The host is everything till the first "/"
308 host
= host
.BeforeFirst(wxT('/'));
311 // Build the cleaned url now
312 wxString url
= wxT("http://") + host
+ wxT("/") + location_url
;
315 if (host
.Find(wxT(':')) != -1) {
316 // This http url has a port
317 port
= wxAtoi(host
.AfterFirst(wxT(':')));
318 host
= host
.BeforeFirst(wxT(':'));
324 if (!(*url_handler
)->Connect(addr
, true)) {
325 throw wxString(wxT("Unable to connect to http download server"));
328 wxInputStream
* url_read_stream
= (*url_handler
)->GetInputStream(url
);
330 printf("Host: %s:%i\n",(const char*)unicode2char(host
),port
);
331 printf("URL: %s\n",(const char*)unicode2char(url
));
332 printf("Response: %i (Error: %i)\n",(*url_handler
)->GetResponse(), (*url_handler
)->GetError());
334 if (!(*url_handler
)->GetResponse()) {
335 printf("WARNING: Void response on stream creation\n");
336 // WTF? Why does this happen?
337 // This is probably produced by an already existing connection, because
338 // the input stream is created nevertheless. However, data is not the same.
339 delete url_read_stream
;
340 throw wxString(wxT("Invalid response from http download server"));
343 if ((*url_handler
)->GetResponse() == 301 // Moved permanently
344 || (*url_handler
)->GetResponse() == 302 // Moved temporarily
345 // What about 300 (multiple choices)? Do we have to handle it?
348 // We have to remove the current stream.
349 delete url_read_stream
;
351 wxString new_location
= (*url_handler
)->GetHeader(wxT("Location"));
353 (*url_handler
)->Destroy();
354 if (!new_location
.IsEmpty()) {
355 (*url_handler
) = new wxHTTP
;
356 (*url_handler
)->SetProxyMode(proxy
);
357 url_read_stream
= GetInputStream(url_handler
, new_location
, proxy
);
359 printf("ERROR: Redirection code received with no URL\n");
361 url_read_stream
= NULL
;
365 return url_read_stream
;
368 // File_checked_for_headers