comparison data/fileio/RemoteFile.cpp @ 0:fc9323a41f5a

start base : Sonic Visualiser sv1-1.0rc1
author lbajardsilogic
date Fri, 11 May 2007 09:08:14 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:fc9323a41f5a
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Sonic Visualiser
5 An audio file viewer and annotation editor.
6 Centre for Digital Music, Queen Mary, University of London.
7 This file copyright 2007 QMUL.
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version. See the file
13 COPYING included with this distribution for more information.
14 */
15
16 #include "RemoteFile.h"
17 #include "base/TempDirectory.h"
18 #include "base/Exceptions.h"
19
20 #include <QHttp>
21 #include <QFtp>
22 #include <QFileInfo>
23 #include <QDir>
24 #include <QApplication>
25 #include <QProgressDialog>
26 #include <QHttpResponseHeader>
27
28 #include <iostream>
29
30 int
31 RemoteFile::m_count = 0;
32
33 QMutex
34 RemoteFile::m_fileCreationMutex;
35
36 RemoteFile::RemoteFile(QUrl url) :
37 m_ftp(0),
38 m_http(0),
39 m_localFile(0),
40 m_ok(false),
41 m_lastStatus(0),
42 m_done(false),
43 m_progressDialog(0),
44 m_progressShowTimer(this)
45 {
46 if (!canHandleScheme(url)) {
47 std::cerr << "RemoteFile::RemoteFile: ERROR: Unsupported scheme in URL \"" << url.toString().toStdString() << "\"" << std::endl;
48 return;
49 }
50
51 m_localFilename = createLocalFile(url);
52 if (m_localFilename == "") return;
53 m_localFile = new QFile(m_localFilename);
54 m_localFile->open(QFile::WriteOnly);
55
56 QString scheme = url.scheme().toLower();
57
58 if (scheme == "http") {
59
60 m_ok = true;
61 m_http = new QHttp(url.host(), url.port(80));
62 connect(m_http, SIGNAL(done(bool)), this, SLOT(done(bool)));
63 connect(m_http, SIGNAL(dataReadProgress(int, int)),
64 this, SLOT(dataReadProgress(int, int)));
65 connect(m_http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)),
66 this, SLOT(httpResponseHeaderReceived(const QHttpResponseHeader &)));
67 m_http->get(url.path(), m_localFile);
68
69 } else if (scheme == "ftp") {
70
71 m_ok = true;
72 m_ftp = new QFtp;
73 connect(m_ftp, SIGNAL(done(bool)), this, SLOT(done(bool)));
74 connect(m_ftp, SIGNAL(commandFinished(int, bool)),
75 this, SLOT(ftpCommandFinished(int, bool)));
76 connect(m_ftp, SIGNAL(dataTransferProgress(qint64, qint64)),
77 this, SLOT(dataTransferProgress(qint64, qint64)));
78 m_ftp->connectToHost(url.host(), url.port(21));
79
80 QString username = url.userName();
81 if (username == "") {
82 username = "anonymous";
83 }
84
85 QString password = url.password();
86 if (password == "") {
87 password = QString("%1@%2").arg(getenv("USER")).arg(getenv("HOST"));
88 }
89
90 m_ftp->login(username, password);
91
92 QString dirpath = url.path().section('/', 0, -2);
93 QString filename = url.path().section('/', -1);
94
95 if (dirpath == "") dirpath = "/";
96 m_ftp->cd(dirpath);
97 m_ftp->get(filename, m_localFile);
98 }
99
100 if (m_ok) {
101 m_progressDialog = new QProgressDialog(tr("Downloading %1...").arg(url.toString()), tr("Cancel"), 0, 100);
102 m_progressDialog->hide();
103 connect(&m_progressShowTimer, SIGNAL(timeout()),
104 this, SLOT(showProgressDialog()));
105 connect(m_progressDialog, SIGNAL(canceled()), this, SLOT(cancelled()));
106 m_progressShowTimer.setSingleShot(true);
107 m_progressShowTimer.start(2000);
108 }
109 }
110
111 RemoteFile::~RemoteFile()
112 {
113 cleanup();
114 }
115
116 void
117 RemoteFile::cleanup()
118 {
119 // std::cerr << "RemoteFile::cleanup" << std::endl;
120 m_done = true;
121 if (m_http) {
122 delete m_http;
123 m_http = 0;
124 }
125 if (m_ftp) {
126 m_ftp->abort();
127 m_ftp->deleteLater();
128 m_ftp = 0;
129 }
130 delete m_progressDialog;
131 m_progressDialog = 0;
132 delete m_localFile;
133 m_localFile = 0;
134 }
135
136 bool
137 RemoteFile::canHandleScheme(QUrl url)
138 {
139 QString scheme = url.scheme().toLower();
140 return (scheme == "http" || scheme == "ftp");
141 }
142
143 bool
144 RemoteFile::isAvailable()
145 {
146 while (m_ok && (!m_done && m_lastStatus == 0)) {
147 QApplication::processEvents();
148 }
149 bool available = true;
150 if (!m_ok) available = false;
151 else available = (m_lastStatus / 100 == 2);
152 std::cerr << "RemoteFile::isAvailable: " << (available ? "yes" : "no")
153 << std::endl;
154 return available;
155 }
156
157 void
158 RemoteFile::wait()
159 {
160 while (m_ok && !m_done) {
161 QApplication::processEvents();
162 }
163 }
164
165 bool
166 RemoteFile::isOK() const
167 {
168 return m_ok;
169 }
170
171 bool
172 RemoteFile::isDone() const
173 {
174 return m_done;
175 }
176
177 QString
178 RemoteFile::getLocalFilename() const
179 {
180 return m_localFilename;
181 }
182
183 QString
184 RemoteFile::getErrorString() const
185 {
186 return m_errorString;
187 }
188
189 void
190 RemoteFile::dataReadProgress(int done, int total)
191 {
192 dataTransferProgress(done, total);
193 }
194
195 void
196 RemoteFile::httpResponseHeaderReceived(const QHttpResponseHeader &resp)
197 {
198 m_lastStatus = resp.statusCode();
199 if (m_lastStatus / 100 >= 4) {
200 m_errorString = QString("%1 %2")
201 .arg(resp.statusCode()).arg(resp.reasonPhrase());
202 std::cerr << "RemoteFile::responseHeaderReceived: "
203 << m_errorString.toStdString() << std::endl;
204 } else {
205 std::cerr << "RemoteFile::responseHeaderReceived: "
206 << m_lastStatus << std::endl;
207 }
208 }
209
210 void
211 RemoteFile::ftpCommandFinished(int id, bool error)
212 {
213 std::cerr << "RemoteFile::ftpCommandFinished(" << id << ", " << error << ")" << std::endl;
214
215 if (!m_ftp) return;
216
217 QFtp::Command command = m_ftp->currentCommand();
218
219 if (!error) {
220 std::cerr << "RemoteFile::ftpCommandFinished: success for command "
221 << command << std::endl;
222 return;
223 }
224
225 if (command == QFtp::ConnectToHost) {
226 m_errorString = tr("Failed to connect to FTP server");
227 } else if (command == QFtp::Login) {
228 m_errorString = tr("Login failed");
229 } else if (command == QFtp::Cd) {
230 m_errorString = tr("Failed to change to correct directory");
231 } else if (command == QFtp::Get) {
232 m_errorString = tr("FTP download aborted");
233 }
234
235 m_lastStatus = 400; // for done()
236 }
237
238 void
239 RemoteFile::dataTransferProgress(qint64 done, qint64 total)
240 {
241 if (!m_progressDialog) return;
242
243 int percent = int((double(done) / double(total)) * 100.0 - 0.1);
244 emit progress(percent);
245
246 if (percent > 0) {
247 m_progressDialog->setValue(percent);
248 m_progressDialog->show();
249 }
250 }
251
252 void
253 RemoteFile::cancelled()
254 {
255 deleteLocalFile();
256 m_done = true;
257 m_ok = false;
258 m_errorString = tr("Download cancelled");
259 }
260
261 void
262 RemoteFile::done(bool error)
263 {
264 std::cerr << "RemoteFile::done(" << error << ")" << std::endl;
265
266 if (m_done) return;
267
268 emit progress(100);
269
270 if (error) {
271 if (m_http) {
272 m_errorString = m_http->errorString();
273 } else if (m_ftp) {
274 m_errorString = m_ftp->errorString();
275 }
276 }
277
278 if (m_lastStatus / 100 >= 4) {
279 error = true;
280 }
281
282 cleanup();
283
284 if (!error) {
285 QFileInfo fi(m_localFilename);
286 if (!fi.exists()) {
287 m_errorString = tr("Failed to create local file %1").arg(m_localFilename);
288 error = true;
289 } else if (fi.size() == 0) {
290 m_errorString = tr("File contains no data!");
291 error = true;
292 }
293 }
294
295 if (error) {
296 deleteLocalFile();
297 }
298
299 m_ok = !error;
300 m_done = true;
301 }
302
303 void
304 RemoteFile::deleteLocalFile()
305 {
306 // std::cerr << "RemoteFile::deleteLocalFile" << std::endl;
307
308 cleanup();
309
310 if (m_localFilename == "") return;
311
312 m_fileCreationMutex.lock();
313
314 if (!QFile(m_localFilename).remove()) {
315 std::cerr << "RemoteFile::deleteLocalFile: ERROR: Failed to delete file \"" << m_localFilename.toStdString() << "\"" << std::endl;
316 } else {
317 m_localFilename = "";
318 }
319
320 m_fileCreationMutex.unlock();
321
322 m_done = true;
323 }
324
325 void
326 RemoteFile::showProgressDialog()
327 {
328 if (m_progressDialog) m_progressDialog->show();
329 }
330
331 QString
332 RemoteFile::createLocalFile(QUrl url)
333 {
334 QDir dir;
335 try {
336 dir = TempDirectory::getInstance()->getSubDirectoryPath("download");
337 } catch (DirectoryCreationFailed f) {
338 std::cerr << "RemoteFile::createLocalFile: ERROR: Failed to create temporary directory: " << f.what() << std::endl;
339 return "";
340 }
341
342 QString filepart = url.path().section('/', -1, -1,
343 QString::SectionSkipEmpty);
344
345 QString extension = filepart.section('.', -1);
346 QString base = filepart;
347 if (extension != "") {
348 base = base.left(base.length() - extension.length() - 1);
349 }
350 if (base == "") base = "remote";
351
352 QString filename;
353
354 if (extension == "") {
355 filename = base;
356 } else {
357 filename = QString("%1.%2").arg(base).arg(extension);
358 }
359
360 QString filepath(dir.filePath(filename));
361
362 std::cerr << "RemoteFile::createLocalFile: URL is \"" << url.toString().toStdString() << "\", dir is \"" << dir.path().toStdString() << "\", base \"" << base.toStdString() << "\", extension \"" << extension.toStdString() << "\", filebase \"" << filename.toStdString() << "\", filename \"" << filepath.toStdString() << "\"" << std::endl;
363
364 m_fileCreationMutex.lock();
365 ++m_count;
366
367 if (QFileInfo(filepath).exists() ||
368 !QFile(filepath).open(QFile::WriteOnly)) {
369
370 std::cerr << "RemoteFile::createLocalFile: Failed to create local file \""
371 << filepath.toStdString() << "\" for URL \""
372 << url.toString().toStdString() << "\" (or file already exists): appending suffix instead" << std::endl;
373
374
375 if (extension == "") {
376 filename = QString("%1_%2").arg(base).arg(m_count);
377 } else {
378 filename = QString("%1_%2.%3").arg(base).arg(m_count).arg(extension);
379 }
380 filepath = dir.filePath(filename);
381
382 if (QFileInfo(filepath).exists() ||
383 !QFile(filepath).open(QFile::WriteOnly)) {
384
385 std::cerr << "RemoteFile::createLocalFile: ERROR: Failed to create local file \""
386 << filepath.toStdString() << "\" for URL \""
387 << url.toString().toStdString() << "\" (or file already exists)" << std::endl;
388
389 m_fileCreationMutex.unlock();
390 return "";
391 }
392 }
393
394 m_fileCreationMutex.unlock();
395
396 std::cerr << "RemoteFile::createLocalFile: url "
397 << url.toString().toStdString() << " -> local filename "
398 << filepath.toStdString() << std::endl;
399
400 return filepath;
401 }