MythTV  0.28pre
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
mythdownloadmanager.cpp
Go to the documentation of this file.
1 // qt
2 #include <QCoreApplication>
3 #include <QRunnable>
4 #include <QString>
5 #include <QByteArray>
6 #include <QFile>
7 #include <QDir>
8 #include <QNetworkCookieJar>
9 #include <QNetworkCookie>
10 #include <QAuthenticator>
11 #include <QTextStream>
12 #include <QNetworkProxy>
13 #include <QMutexLocker>
14 
15 #include "stdlib.h"
16 
17 #include <unistd.h> // for usleep()
18 
19 // libmythbase
20 #include "compat.h"
21 #include "mythcorecontext.h"
22 #include "mythcoreutil.h"
23 #include "mthreadpool.h"
24 #include "mythdirs.h"
25 #include "mythevent.h"
26 #include "mythversion.h"
27 #include "remotefile.h"
28 #include "mythdate.h"
29 
30 #include "mythdownloadmanager.h"
31 #include "mythlogging.h"
32 #include <QUrl>
33 
34 using namespace std;
35 
36 #define LOC QString("DownloadManager: ")
37 #define CACHE_REDIRECTION_LIMIT 10
38 
40 QMutex dmCreateLock;
41 
46 {
47  public:
49  m_request(NULL), m_reply(NULL), m_data(NULL),
50  m_caller(NULL), m_requestType(kRequestGet),
51  m_reload(false), m_preferCache(false), m_syncMode(false),
52  m_processReply(true), m_done(false), m_bytesReceived(0),
53  m_bytesTotal(0), m_lastStat(MythDate::current()),
54  m_authCallback(NULL), m_authArg(NULL),
55  m_headers(NULL), m_errorCode(QNetworkReply::NoError)
56  {
57  qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
58  }
59 
61  {
62  if (m_request)
63  delete m_request;
64  if (m_reply && m_processReply)
65  m_reply->deleteLater();
66  }
67 
68  void detach(void)
69  {
70  m_url.detach();
71  m_outFile.detach();
72  }
73 
74  bool IsDone(void)
75  {
76  QMutexLocker lock(&m_lock);
77  return m_done;
78  }
79 
80  void SetDone(bool done)
81  {
82  QMutexLocker lock(&m_lock);
83  m_done = done;
84  }
85 
86  QString m_url;
88  QNetworkRequest *m_request;
89  QNetworkReply *m_reply;
90  QString m_outFile;
91  QByteArray *m_data;
92  QByteArray m_privData;
93  QObject *m_caller;
95  bool m_reload;
97  bool m_syncMode;
99  bool m_done;
101  qint64 m_bytesTotal;
102  QDateTime m_lastStat;
104  void *m_authArg;
105  const QHash<QByteArray, QByteArray> *m_headers;
106 
107  QNetworkReply::NetworkError m_errorCode;
108  QMutex m_lock;
109 };
110 
111 
116 class MythCookieJar : public QNetworkCookieJar
117 {
118  public:
119  MythCookieJar();
121  void load(const QString &filename);
122  void save(const QString &filename);
123 };
124 
128 class RemoteFileDownloadThread : public QRunnable
129 {
130  public:
132  MythDownloadInfo *dlInfo) :
133  m_parent(parent),
134  m_dlInfo(dlInfo)
135  {
136  m_dlInfo->detach();
137  }
138 
139  void run()
140  {
141  bool ok = false;
142 
143  RemoteFile *rf = new RemoteFile(m_dlInfo->m_url, false, false, 0);
144  ok = rf->SaveAs(m_dlInfo->m_privData);
145  delete rf;
146 
147  if (!ok)
148  m_dlInfo->m_errorCode = QNetworkReply::UnknownNetworkError;
149 
150  m_dlInfo->m_bytesReceived = m_dlInfo->m_privData.size();
151  m_dlInfo->m_bytesTotal = m_dlInfo->m_bytesReceived;
152 
153  m_parent->downloadFinished(m_dlInfo);
154  }
155 
156  private:
159 };
160 
164 {
165  if (downloadManager)
166  {
167  delete downloadManager;
168  downloadManager = NULL;
169  }
170 }
171 
176 {
177  if (downloadManager)
178  return downloadManager;
179 
180  QMutexLocker locker(&dmCreateLock);
181 
182  // Check once more in case the download manager was created
183  // while we were securing the lock.
184  if (downloadManager)
185  return downloadManager;
186 
188  tmpDLM->start();
189  while (!tmpDLM->getQueueThread())
190  usleep(10000);
191 
192  tmpDLM->moveToThread(tmpDLM->getQueueThread());
193  tmpDLM->setRunThread();
194 
195  while (!tmpDLM->isRunning())
196  usleep(10000);
197 
198  downloadManager = tmpDLM;
199 
201 
202  return downloadManager;
203 }
204 
209  MThread("DownloadManager"),
210  m_manager(NULL),
211  m_diskCache(NULL),
212  m_proxy(NULL),
213  m_infoLock(new QMutex(QMutex::Recursive)),
214  m_queueThread(NULL),
215  m_runThread(false),
216  m_isRunning(false),
217  m_inCookieJar(NULL)
218 {
219 }
220 
224 {
225  m_runThread = false;
226  m_queueWaitCond.wakeAll();
227 
228  wait();
229 
230  delete m_infoLock;
231 
232  if (m_inCookieJar)
233  delete m_inCookieJar;
234 }
235 
240 {
241  RunProlog();
242 
243  bool downloading = false;
244  bool itemsInQueue = false;
245  bool itemsInCancellationQueue = false;
246  bool waitAnyway = false;
247 
248  m_queueThread = QThread::currentThread();
249 
250  while (!m_runThread)
251  usleep(50000);
252 
253  m_manager = new QNetworkAccessManager(this);
254  m_diskCache = new QNetworkDiskCache(this);
255  m_proxy = new QNetworkProxy();
256  m_diskCache->setCacheDirectory(GetConfDir() + "/cache/" +
257  QCoreApplication::applicationName() + "-" +
259  m_manager->setCache(m_diskCache);
260 
261  // Set the proxy for the manager to be the application default proxy,
262  // which has already been setup
263  m_manager->setProxy(*m_proxy);
264 
265  // make sure the cookieJar is created in the same thread as the manager
266  // and set its parent to NULL so it can be shared between managers
267  m_manager->cookieJar()->setParent(NULL);
268 
269  QObject::connect(m_manager, SIGNAL(finished(QNetworkReply*)), this,
270  SLOT(downloadFinished(QNetworkReply*)));
271 
272  m_isRunning = true;
273  while (m_runThread)
274  {
275  if (m_inCookieJar)
276  {
277  LOG(VB_GENERAL, LOG_DEBUG, "Updating DLManager's Cookie Jar");
278  updateCookieJar();
279  }
280  m_infoLock->lock();
281  downloading = !m_downloadInfos.isEmpty();
282  itemsInCancellationQueue = !m_cancellationQueue.isEmpty();
283  m_infoLock->unlock();
284 
285  if (itemsInCancellationQueue)
286  {
288  }
289  if (downloading)
290  QCoreApplication::processEvents();
291 
292  m_infoLock->lock();
293  itemsInQueue = !m_downloadQueue.isEmpty();
294  m_infoLock->unlock();
295 
296  if (!itemsInQueue || waitAnyway)
297  {
298  waitAnyway = false;
299  m_queueWaitLock.lock();
300 
301  if (downloading)
302  m_queueWaitCond.wait(&m_queueWaitLock, 200);
303  else
305 
306  m_queueWaitLock.unlock();
307  }
308 
309  m_infoLock->lock();
310  if (!m_downloadQueue.isEmpty())
311  {
312  MythDownloadInfo *dlInfo = m_downloadQueue.front();
313 
314  m_downloadQueue.pop_front();
315 
316  if (!dlInfo)
317  {
318  m_infoLock->unlock();
319  continue;
320  }
321 
322  QUrl qurl(dlInfo->m_url);
323  if (m_downloadInfos.contains(qurl.toString()))
324  {
325  // Push request to the end of the queue to let others process.
326  // If this is the only item in the queue, force the loop to
327  // wait a little.
328  if (m_downloadQueue.isEmpty())
329  waitAnyway = true;
330  m_downloadQueue.push_back(dlInfo);
331  m_infoLock->unlock();
332  continue;
333  }
334 
335  if (dlInfo->m_url.startsWith("myth://"))
336  downloadRemoteFile(dlInfo);
337  else
338  {
339  QMutexLocker cLock(&m_cookieLock);
340  downloadQNetworkRequest(dlInfo);
341  }
342 
343  m_downloadInfos[qurl.toString()] = dlInfo;
344  }
345  m_infoLock->unlock();
346  }
347  m_isRunning = false;
348 
349  RunEpilog();
350 }
351 
362 void MythDownloadManager::queueItem(const QString &url, QNetworkRequest *req,
363  const QString &dest, QByteArray *data,
364  QObject *caller, const MRequestType reqType,
365  const bool reload)
366 {
367  MythDownloadInfo *dlInfo = new MythDownloadInfo;
368 
369  dlInfo->m_url = url;
370  dlInfo->m_request = req;
371  dlInfo->m_outFile = dest;
372  dlInfo->m_data = data;
373  dlInfo->m_caller = caller;
374  dlInfo->m_requestType = reqType;
375  dlInfo->m_reload = reload;
376 
377  dlInfo->detach();
378 
379  QMutexLocker locker(m_infoLock);
380  m_downloadQueue.push_back(dlInfo);
381  m_queueWaitCond.wakeAll();
382 }
383 
396 bool MythDownloadManager::processItem(const QString &url, QNetworkRequest *req,
397  const QString &dest, QByteArray *data,
398  const MRequestType reqType,
399  const bool reload,
400  AuthCallback authCallback, void *authArg,
401  const QHash<QByteArray, QByteArray> *headers)
402 {
403  MythDownloadInfo *dlInfo = new MythDownloadInfo;
404 
405  dlInfo->m_url = url;
406  dlInfo->m_request = req;
407  dlInfo->m_outFile = dest;
408  dlInfo->m_data = data;
409  dlInfo->m_requestType = reqType;
410  dlInfo->m_reload = reload;
411  dlInfo->m_syncMode = true;
412  dlInfo->m_authCallback = authCallback;
413  dlInfo->m_authArg = authArg;
414  dlInfo->m_headers = headers;
415 
416  return downloadNow(dlInfo, true);
417 }
418 
422 void MythDownloadManager::preCache(const QString &url)
423 {
424  LOG(VB_FILE, LOG_DEBUG, LOC + QString("preCache('%1')").arg(url));
425  queueItem(url, NULL, QString(), NULL, NULL);
426 }
427 
434 void MythDownloadManager::queueDownload(const QString &url,
435  const QString &dest,
436  QObject *caller,
437  const bool reload)
438 {
439  LOG(VB_FILE, LOG_DEBUG, LOC + QString("queueDownload('%1', '%2', %3)")
440  .arg(url).arg(dest).arg((long long)caller));
441 
442  queueItem(url, NULL, dest, NULL, caller, kRequestGet, reload);
443 }
444 
450 void MythDownloadManager::queueDownload(QNetworkRequest *req,
451  QByteArray *data,
452  QObject *caller)
453 {
454  LOG(VB_FILE, LOG_DEBUG, LOC + QString("queueDownload('%1', '%2', %3)")
455  .arg(req->url().toString()).arg((long long)data)
456  .arg((long long)caller));
457 
458  queueItem(req->url().toString(), req, QString(), data, caller,
459  kRequestGet,
460  (QNetworkRequest::AlwaysNetwork == req->attribute(
461  QNetworkRequest::CacheLoadControlAttribute,
462  QNetworkRequest::PreferNetwork).toInt()));
463 }
464 
471 bool MythDownloadManager::download(const QString &url, const QString &dest,
472  const bool reload)
473 {
474  return processItem(url, NULL, dest, NULL, kRequestGet, reload);
475 }
476 
483 bool MythDownloadManager::download(const QString &url, QByteArray *data,
484  const bool reload)
485 {
486  return processItem(url, NULL, QString(), data, kRequestGet, reload);
487 }
488 
495 QNetworkReply *MythDownloadManager::download(const QString &url,
496  const bool reload)
497 {
498  MythDownloadInfo *dlInfo = new MythDownloadInfo;
499  QNetworkReply *reply = NULL;
500 
501  dlInfo->m_url = url;
502  dlInfo->m_reload = reload;
503  dlInfo->m_syncMode = true;
504  dlInfo->m_processReply = false;
505 
506  if (downloadNow(dlInfo, false))
507  {
508  if (dlInfo->m_reply)
509  {
510  reply = dlInfo->m_reply;
511  // prevent dlInfo dtor from deleting the reply
512  dlInfo->m_reply = NULL;
513 
514  delete dlInfo;
515 
516  return reply;
517  }
518 
519  delete dlInfo;
520  }
521 
522  return NULL;
523 }
524 
530 bool MythDownloadManager::download(QNetworkRequest *req, QByteArray *data)
531 {
532  LOG(VB_FILE, LOG_DEBUG, LOC + QString("download('%1', '%2')")
533  .arg(req->url().toString()).arg((long long)data));
534  return processItem(req->url().toString(), req, QString(), data,
535  kRequestGet,
536  (QNetworkRequest::AlwaysNetwork == req->attribute(
537  QNetworkRequest::CacheLoadControlAttribute,
538  QNetworkRequest::PreferNetwork).toInt()));
539 }
540 
550 bool MythDownloadManager::downloadAuth(const QString &url, const QString &dest,
551  const bool reload, AuthCallback authCallback, void *authArg,
552  const QHash<QByteArray, QByteArray> *headers)
553 {
554  return processItem(url, NULL, dest, NULL, kRequestGet, reload, authCallback,
555  authArg, headers);
556 }
557 
558 
564 void MythDownloadManager::queuePost(const QString &url,
565  QByteArray *data,
566  QObject *caller)
567 {
568  LOG(VB_FILE, LOG_DEBUG, LOC + QString("queuePost('%1', '%2')")
569  .arg(url).arg((long long)data));
570 
571  if (!data)
572  {
573  LOG(VB_GENERAL, LOG_ERR, LOC + "queuePost(), data is NULL!");
574  return;
575  }
576 
577  queueItem(url, NULL, QString(), data, caller, kRequestPost);
578 }
579 
585 void MythDownloadManager::queuePost(QNetworkRequest *req,
586  QByteArray *data,
587  QObject *caller)
588 {
589  LOG(VB_FILE, LOG_DEBUG, LOC + QString("queuePost('%1', '%2')")
590  .arg(req->url().toString()).arg((long long)data));
591 
592  if (!data)
593  {
594  LOG(VB_GENERAL, LOG_ERR, LOC + "queuePost(), data is NULL!");
595  return;
596  }
597 
598  queueItem(req->url().toString(), req, QString(), data, caller,
599  kRequestPost,
600  (QNetworkRequest::AlwaysNetwork == req->attribute(
601  QNetworkRequest::CacheLoadControlAttribute,
602  QNetworkRequest::PreferNetwork).toInt()));
603 
604 }
605 
611 bool MythDownloadManager::post(const QString &url, QByteArray *data)
612 {
613  LOG(VB_FILE, LOG_DEBUG, LOC + QString("post('%1', '%2')")
614  .arg(url).arg((long long)data));
615 
616  if (!data)
617  {
618  LOG(VB_GENERAL, LOG_ERR, LOC + "post(), data is NULL!");
619  return false;
620  }
621 
622  return processItem(url, NULL, QString(), data, kRequestPost);
623 }
624 
630 bool MythDownloadManager::post(QNetworkRequest *req, QByteArray *data)
631 {
632  LOG(VB_FILE, LOG_DEBUG, LOC + QString("post('%1', '%2')")
633  .arg(req->url().toString()).arg((long long)data));
634 
635  if (!data)
636  {
637  LOG(VB_GENERAL, LOG_ERR, LOC + "post(), data is NULL!");
638  return false;
639  }
640 
641  return processItem(req->url().toString(), req, QString(), data,
642  kRequestPost,
643  (QNetworkRequest::AlwaysNetwork == req->attribute(
644  QNetworkRequest::CacheLoadControlAttribute,
645  QNetworkRequest::PreferNetwork).toInt()));
646 
647 }
648 
657 bool MythDownloadManager::postAuth(const QString &url, QByteArray *data,
658  AuthCallback authCallback, void *authArg,
659  const QHash<QByteArray, QByteArray> *headers)
660 {
661  LOG(VB_FILE, LOG_DEBUG, LOC + QString("postAuth('%1', '%2')")
662  .arg(url).arg((long long)data));
663 
664  if (!data)
665  {
666  LOG(VB_GENERAL, LOG_ERR, LOC + "postAuth(), data is NULL!");
667  return false;
668  }
669 
670  return processItem(url, NULL, NULL, data, kRequestPost, false, authCallback,
671  authArg, headers);
672 }
673 
678 {
679  RemoteFileDownloadThread *dlThread =
680  new RemoteFileDownloadThread(this, dlInfo);
681  MThreadPool::globalInstance()->start(dlThread, "RemoteFileDownload");
682 }
683 
688 {
689  if (!dlInfo)
690  return;
691 
692  static const char dateFormat[] = "ddd, dd MMM yyyy hh:mm:ss 'GMT'";
693  QUrl qurl(dlInfo->m_url);
694  QNetworkRequest request;
695 
696  if (dlInfo->m_request)
697  {
698  request = *dlInfo->m_request;
699  delete dlInfo->m_request;
700  dlInfo->m_request = NULL;
701  }
702  else
703  request.setUrl(qurl);
704 
705  if (dlInfo->m_reload)
706  {
707  request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
708  QNetworkRequest::AlwaysNetwork);
709  }
710  else
711  {
712  // Prefer the in-cache item if one exists and it is less than 5 minutes
713  // old and it will not expire in the next 10 seconds
714  QDateTime now = MythDate::current();
715 
716  // Handle redirects, we want the metadata of the file headers
717  QString redirectLoc;
718  int limit = 0;
719  while (!(redirectLoc = getHeader(qurl, "Location")).isNull())
720  {
721  if (limit == CACHE_REDIRECTION_LIMIT)
722  {
723  LOG(VB_GENERAL, LOG_WARNING, QString("Cache Redirection limit "
724  "reached for %1")
725  .arg(qurl.toString()));
726  return;
727  }
728  qurl.setUrl(redirectLoc);
729  limit++;
730  }
731 
732  LOG(VB_NETWORK, LOG_DEBUG, QString("Checking cache for %1")
733  .arg(qurl.toString()));
734 
735  m_infoLock->lock();
736  QNetworkCacheMetaData urlData = m_manager->cache()->metaData(qurl);
737  m_infoLock->unlock();
738  if ((urlData.isValid()) &&
739  ((!urlData.expirationDate().isValid()) ||
740  (QDateTime(urlData.expirationDate().toUTC()).secsTo(now) < 10)))
741  {
742  QString dateString = getHeader(urlData, "Date");
743 
744  if (!dateString.isNull())
745  {
746  QDateTime loadDate =
747  MythDate::fromString(dateString, dateFormat);
748  loadDate.setTimeSpec(Qt::UTC);
749  if (loadDate.secsTo(now) <= 720)
750  {
751  dlInfo->m_preferCache = true;
752  LOG(VB_NETWORK, LOG_DEBUG, QString("Preferring cache for %1")
753  .arg(qurl.toString()));
754  }
755  }
756  }
757  }
758 
759  if (dlInfo->m_preferCache)
760  request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
761  QNetworkRequest::PreferCache);
762 
763  request.setRawHeader("User-Agent",
764  "MythTV v" MYTH_BINARY_VERSION " MythDownloadManager");
765 
766  if (dlInfo->m_headers)
767  {
768  QHash<QByteArray, QByteArray>::const_iterator it =
769  dlInfo->m_headers->constBegin();
770  for ( ; it != dlInfo->m_headers->constEnd(); ++it )
771  {
772  if (!it.key().isEmpty() && !it.value().isEmpty())
773  {
774  request.setRawHeader(it.key(), it.value());
775  }
776  }
777  }
778 
779  switch (dlInfo->m_requestType)
780  {
781  case kRequestPost :
782  dlInfo->m_reply = m_manager->post(request, *dlInfo->m_data);
783  break;
784  case kRequestHead :
785  dlInfo->m_reply = m_manager->head(request);
786  break;
787  case kRequestGet :
788  default:
789  dlInfo->m_reply = m_manager->get(request);
790  break;
791  }
792 
793  m_downloadReplies[dlInfo->m_reply] = dlInfo;
794 
795  if (dlInfo->m_authCallback)
796  {
797  connect(m_manager, SIGNAL(authenticationRequired(QNetworkReply *,
798  QAuthenticator *)),
799  this, SLOT(authCallback(QNetworkReply *, QAuthenticator *)));
800  }
801 
802  connect(dlInfo->m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
803  SLOT(downloadError(QNetworkReply::NetworkError)));
804  connect(dlInfo->m_reply, SIGNAL(downloadProgress(qint64, qint64)),
805  this, SLOT(downloadProgress(qint64, qint64)));
806 }
807 
812 void MythDownloadManager::authCallback(QNetworkReply *reply,
813  QAuthenticator *authenticator)
814 {
815  if (!reply)
816  return;
817 
818  MythDownloadInfo *dlInfo = m_downloadReplies[reply];
819 
820  if (!dlInfo)
821  return;
822 
823  if (dlInfo->m_authCallback)
824  {
825  LOG(VB_FILE, LOG_DEBUG, "Calling auth callback");
826  dlInfo->m_authCallback(reply, authenticator, dlInfo->m_authArg);
827  }
828 }
829 
837 {
838  if (!dlInfo)
839  return false;
840 
841  dlInfo->m_syncMode = true;
842 
843  m_infoLock->lock();
844  m_downloadQueue.push_back(dlInfo);
845  m_infoLock->unlock();
846  m_queueWaitCond.wakeAll();
847 
848  // timeout myth:// RemoteFile transfers 20 seconds from now
849  // timeout non-myth:// QNetworkAccessManager transfers 60 seconds after
850  // their last progress update
851  QDateTime startedAt = MythDate::current();
852  m_infoLock->lock();
853  while ((!dlInfo->IsDone()) &&
854  (dlInfo->m_errorCode == QNetworkReply::NoError) &&
855  (((!dlInfo->m_url.startsWith("myth://")) &&
856  (dlInfo->m_lastStat.secsTo(MythDate::current()) < 60)) ||
857  ((dlInfo->m_url.startsWith("myth://")) &&
858  (startedAt.secsTo(MythDate::current()) < 20))))
859  {
860  m_infoLock->unlock();
861  m_queueWaitLock.lock();
862  m_queueWaitCond.wait(&m_queueWaitLock, 200);
863  m_queueWaitLock.unlock();
864  m_infoLock->lock();
865  }
866  bool done = dlInfo->IsDone();
867  bool success =
868  done && (dlInfo->m_errorCode == QNetworkReply::NoError);
869 
870  if (!done)
871  {
872  dlInfo->m_data = NULL; // Prevent downloadFinished() from updating
873  dlInfo->m_syncMode = false; // Let downloadFinished() cleanup for us
874  if ((dlInfo->m_reply) &&
875  (dlInfo->m_errorCode == QNetworkReply::NoError))
876  {
877  LOG(VB_FILE, LOG_DEBUG,
878  LOC + QString("Aborting download - lack of data transfer"));
879  dlInfo->m_reply->abort();
880  }
881  }
882  else if (deleteInfo)
883  delete dlInfo;
884 
885  m_infoLock->unlock();
886 
887  return success;
888 }
889 
893 void MythDownloadManager::cancelDownload(const QString &url, bool block)
894 {
895  cancelDownload(QStringList(url), block);
896 }
897 
901 void MythDownloadManager::cancelDownload(const QStringList &urls, bool block)
902 {
903  MythDownloadInfo *dlInfo;
904 
905  m_infoLock->lock();
906  foreach (QString url, urls)
907  {
908  QMutableListIterator<MythDownloadInfo*> lit(m_downloadQueue);
909  while (lit.hasNext())
910  {
911  lit.next();
912  dlInfo = lit.value();
913  if (dlInfo->m_url == url)
914  {
915  if (!m_cancellationQueue.contains(dlInfo))
916  m_cancellationQueue.append(dlInfo);
917  lit.remove();
918  }
919  }
920 
921  if (m_downloadInfos.contains(url))
922  {
923  dlInfo = m_downloadInfos[url];
924 
925  if (!m_cancellationQueue.contains(dlInfo))
926  m_cancellationQueue.append(dlInfo);
927 
928  if (dlInfo->m_reply)
929  m_downloadReplies.remove(dlInfo->m_reply);
930 
931  m_downloadInfos.remove(url);
932  }
933  }
934  m_infoLock->unlock();
935 
936  if (QThread::currentThread() == this->thread())
937  {
939  return;
940  }
941 
942  // wake-up running thread
943  m_queueWaitCond.wakeAll();
944 
945  if (!block)
946  return;
947 
948  while (!m_cancellationQueue.isEmpty())
949  {
950  usleep(50000); // re-test in another 50ms
951  }
952 }
953 
955 {
956  QMutexLocker locker(m_infoLock);
957  MythDownloadInfo *dlInfo;
958 
959  QMutableListIterator<MythDownloadInfo*> lit(m_cancellationQueue);
960  while (lit.hasNext())
961  {
962  lit.next();
963  dlInfo = lit.value();
964  dlInfo->m_lock.lock();
965 
966  if (dlInfo->m_reply)
967  {
968  LOG(VB_FILE, LOG_DEBUG,
969  LOC + QString("Aborting download - user request"));
970  dlInfo->m_reply->abort();
971  }
972  lit.remove();
973  if (dlInfo->m_done)
974  {
975  dlInfo->m_lock.unlock();
976  continue;
977  }
978  dlInfo->m_errorCode = QNetworkReply::OperationCanceledError;
979  dlInfo->m_done = true;
980  dlInfo->m_lock.unlock();
981  }
982 }
983 
989 {
990  QMutexLocker locker(m_infoLock);
991  MythDownloadInfo *dlInfo;
992 
993  QList <MythDownloadInfo*>::iterator lit = m_downloadQueue.begin();
994  for (; lit != m_downloadQueue.end(); ++lit)
995  {
996  dlInfo = *lit;
997  if (dlInfo->m_caller == caller)
998  {
999  dlInfo->m_caller = NULL;
1000  dlInfo->m_outFile = QString();
1001  dlInfo->m_data = NULL;
1002  }
1003  }
1004 
1005  QMap <QString, MythDownloadInfo*>::iterator mit = m_downloadInfos.begin();
1006  for (; mit != m_downloadInfos.end(); ++mit)
1007  {
1008  dlInfo = mit.value();
1009  if (dlInfo->m_caller == caller)
1010  {
1011  dlInfo->m_caller = NULL;
1012  dlInfo->m_outFile = QString();
1013  dlInfo->m_data = NULL;
1014  }
1015  }
1016 }
1017 
1021 void MythDownloadManager::downloadError(QNetworkReply::NetworkError errorCode)
1022 {
1023  QNetworkReply *reply = (QNetworkReply*)sender();
1024 
1025  LOG(VB_FILE, LOG_DEBUG, LOC + QString("downloadError %1 ")
1026  .arg(errorCode) + reply->errorString() );
1027 
1028  QMutexLocker locker(m_infoLock);
1029  if (!m_downloadReplies.contains(reply))
1030  {
1031  reply->deleteLater();
1032  return;
1033  }
1034 
1035  MythDownloadInfo *dlInfo = m_downloadReplies[reply];
1036 
1037  if (!dlInfo)
1038  return;
1039 
1040  dlInfo->m_errorCode = errorCode;
1041 }
1042 
1048 QUrl MythDownloadManager::redirectUrl(const QUrl& possibleRedirectUrl,
1049  const QUrl& oldRedirectUrl) const
1050 {
1051  LOG(VB_FILE, LOG_DEBUG, LOC + QString("redirectUrl()"));
1052  QUrl redirectUrl;
1053 
1054  if(!possibleRedirectUrl.isEmpty() && possibleRedirectUrl != oldRedirectUrl)
1055  redirectUrl = possibleRedirectUrl;
1056 
1057  return redirectUrl;
1058 }
1059 
1063 void MythDownloadManager::downloadFinished(QNetworkReply* reply)
1064 {
1065  LOG(VB_FILE, LOG_DEBUG, LOC + QString("downloadFinished(%1)")
1066  .arg((long long)reply));
1067 
1068  QMutexLocker locker(m_infoLock);
1069  if (!m_downloadReplies.contains(reply))
1070  {
1071  reply->deleteLater();
1072  return;
1073  }
1074 
1075  MythDownloadInfo *dlInfo = m_downloadReplies[reply];
1076 
1077  if (!dlInfo || !dlInfo->m_reply)
1078  return;
1079 
1080  downloadFinished(dlInfo);
1081 }
1082 
1087 {
1088  if (!dlInfo)
1089  return;
1090 
1091  int statusCode = -1;
1092  static const char dateFormat[] = "ddd, dd MMM yyyy hh:mm:ss 'GMT'";
1093  QNetworkReply *reply = dlInfo->m_reply;
1094 
1095  if (reply)
1096  {
1097  QUrl possibleRedirectUrl =
1098  reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
1099 
1100  if (!possibleRedirectUrl.isEmpty() &&
1101  possibleRedirectUrl.isValid() &&
1102  possibleRedirectUrl.isRelative()) // Turn relative Url to absolute
1103  possibleRedirectUrl = reply->url().resolved(possibleRedirectUrl);
1104 
1105  dlInfo->m_redirectedTo =
1106  redirectUrl(possibleRedirectUrl, dlInfo->m_redirectedTo);
1107 
1108  QVariant status =
1109  reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
1110  if (status.isValid())
1111  statusCode = status.toInt();
1112  }
1113 
1114  if(reply && !dlInfo->m_redirectedTo.isEmpty() &&
1115  ((dlInfo->m_requestType != kRequestPost) ||
1116  (statusCode == 301 || statusCode == 302 ||
1117  statusCode == 303)))
1118  {
1119  LOG(VB_FILE, LOG_DEBUG, LOC +
1120  QString("downloadFinished(%1): Redirect: %2 -> %3")
1121  .arg((long long)dlInfo)
1122  .arg(reply->url().toString())
1123  .arg(dlInfo->m_redirectedTo.toString()));
1124 
1125  if (dlInfo->m_data)
1126  dlInfo->m_data->clear();
1127 
1128  dlInfo->m_bytesReceived = 0;
1129  dlInfo->m_bytesTotal = 0;
1130 
1131  QNetworkRequest request(dlInfo->m_redirectedTo);
1132 
1133  if (dlInfo->m_reload)
1134  {
1135  request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
1136  QNetworkRequest::AlwaysNetwork);
1137  }
1138  else if (dlInfo->m_preferCache)
1139  {
1140  request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
1141  QNetworkRequest::PreferCache);
1142  }
1143 
1144  request.setRawHeader("User-Agent",
1145  "MythDownloadManager v" MYTH_BINARY_VERSION);
1146 
1147  switch (dlInfo->m_requestType)
1148  {
1149  case kRequestHead :
1150  dlInfo->m_reply = m_manager->head(request);
1151  break;
1152  case kRequestGet :
1153  default:
1154  dlInfo->m_reply = m_manager->get(request);
1155  break;
1156  }
1157 
1158  m_downloadReplies[dlInfo->m_reply] = dlInfo;
1159 
1160  connect(dlInfo->m_reply, SIGNAL(error(QNetworkReply::NetworkError)),
1161  this, SLOT(downloadError(QNetworkReply::NetworkError)));
1162  connect(dlInfo->m_reply, SIGNAL(downloadProgress(qint64, qint64)),
1163  this, SLOT(downloadProgress(qint64, qint64)));
1164 
1165  m_downloadReplies.remove(reply);
1166  reply->deleteLater();
1167  }
1168  else
1169  {
1170  LOG(VB_FILE, LOG_DEBUG, QString("downloadFinished(%1): COMPLETE: %2")
1171  .arg((long long)dlInfo).arg(dlInfo->m_url));
1172 
1173  // HACK Insert a Date header into the cached metadata if one doesn't
1174  // already exist
1175  QUrl fileUrl = dlInfo->m_url;
1176  QString redirectLoc;
1177  int limit = 0;
1178  while (!(redirectLoc = getHeader(fileUrl, "Location")).isNull())
1179  {
1180  if (limit == CACHE_REDIRECTION_LIMIT)
1181  {
1182  LOG(VB_GENERAL, LOG_WARNING, QString("Cache Redirection limit "
1183  "reached for %1")
1184  .arg(fileUrl.toString()));
1185  return;
1186  }
1187  fileUrl.setUrl(redirectLoc);
1188  limit++;
1189  }
1190 
1191  m_infoLock->lock();
1192  QNetworkCacheMetaData urlData = m_manager->cache()->metaData(fileUrl);
1193  m_infoLock->unlock();
1194  if (getHeader(urlData, "Date").isNull())
1195  {
1196  QNetworkCacheMetaData::RawHeaderList headers = urlData.rawHeaders();
1197  QNetworkCacheMetaData::RawHeader newheader;
1198  QDateTime now = MythDate::current();
1199  newheader = QNetworkCacheMetaData::RawHeader("Date",
1200  now.toString(dateFormat).toLatin1());
1201  headers.append(newheader);
1202  urlData.setRawHeaders(headers);
1203  m_infoLock->lock();
1204  m_manager->cache()->updateMetaData(urlData);
1205  m_infoLock->unlock();
1206  }
1207  // End HACK
1208 
1209  dlInfo->m_redirectedTo.clear();
1210 
1211  int dataSize = -1;
1212 
1213  // If we downloaded via the QNetworkAccessManager
1214  // AND the caller isn't handling the reply directly
1215  if (reply && dlInfo->m_processReply)
1216  {
1217  bool append = (!dlInfo->m_syncMode && dlInfo->m_caller);
1218  QByteArray data = reply->readAll();
1219  dataSize = data.size();
1220 
1221  if (append)
1222  dlInfo->m_bytesReceived += dataSize;
1223  else
1224  dlInfo->m_bytesReceived = dataSize;
1225 
1226  dlInfo->m_bytesTotal = dlInfo->m_bytesReceived;
1227 
1228  if (dlInfo->m_data)
1229  {
1230  if (append)
1231  dlInfo->m_data->append(data);
1232  else
1233  *dlInfo->m_data = data;
1234  }
1235  else if (!dlInfo->m_outFile.isEmpty())
1236  {
1237  saveFile(dlInfo->m_outFile, data, append);
1238  }
1239  }
1240  else if (!reply) // If we downloaded via RemoteFile
1241  {
1242  if (dlInfo->m_data)
1243  {
1244  (*dlInfo->m_data) = dlInfo->m_privData;
1245  }
1246  else if (!dlInfo->m_outFile.isEmpty())
1247  {
1248  saveFile(dlInfo->m_outFile, dlInfo->m_privData);
1249  }
1250  dlInfo->m_bytesReceived += dataSize;
1251  dlInfo->m_bytesTotal = dlInfo->m_bytesReceived;
1252  }
1253  // else we downloaded via QNetworkAccessManager
1254  // AND the caller is handling the reply
1255 
1256  m_downloadInfos.remove(dlInfo->m_url);
1257  if (reply)
1258  m_downloadReplies.remove(reply);
1259 
1260  dlInfo->SetDone(true);
1261 
1262  if (!dlInfo->m_syncMode)
1263  {
1264  if (dlInfo->m_caller)
1265  {
1266  LOG(VB_FILE, LOG_DEBUG, QString("downloadFinished(%1): "
1267  "COMPLETE: %2, sending event to caller")
1268  .arg((long long)dlInfo).arg(dlInfo->m_url));
1269 
1270  QStringList args;
1271  args << dlInfo->m_url;
1272  args << dlInfo->m_outFile;
1273  args << QString::number(dlInfo->m_bytesTotal);
1274  // placeholder for error string
1275  args << (reply ? reply->errorString() : QString());
1276  args << QString::number((int)(reply ? reply->error() :
1277  dlInfo->m_errorCode));
1278 
1279  QCoreApplication::postEvent(dlInfo->m_caller,
1280  new MythEvent("DOWNLOAD_FILE FINISHED", args));
1281  }
1282 
1283  delete dlInfo;
1284  }
1285 
1286  m_queueWaitCond.wakeAll();
1287  }
1288 }
1289 
1295 void MythDownloadManager::downloadProgress(qint64 bytesReceived,
1296  qint64 bytesTotal)
1297 {
1298  QNetworkReply *reply = (QNetworkReply*)sender();
1299 
1300  LOG(VB_FILE, LOG_DEBUG, LOC +
1301  QString("downloadProgress(%1, %2) (for reply %3)")
1302  .arg(bytesReceived).arg(bytesTotal).arg((long long)reply));
1303 
1304  QMutexLocker locker(m_infoLock);
1305  if (!m_downloadReplies.contains(reply))
1306  return;
1307 
1308  MythDownloadInfo *dlInfo = m_downloadReplies[reply];
1309 
1310  if (!dlInfo)
1311  return;
1312 
1313  dlInfo->m_lastStat = MythDate::current();
1314 
1315  LOG(VB_FILE, LOG_DEBUG, LOC +
1316  QString("downloadProgress: %1 to %2 is at %3 of %4 bytes downloaded")
1317  .arg(dlInfo->m_url).arg(dlInfo->m_outFile)
1318  .arg(bytesReceived).arg(bytesTotal));
1319 
1320  if (!dlInfo->m_syncMode && dlInfo->m_caller)
1321  {
1322  LOG(VB_FILE, LOG_DEBUG, QString("downloadProgress(%1): "
1323  "sending event to caller")
1324  .arg(reply->url().toString()));
1325 
1326  bool appendToFile = (dlInfo->m_bytesReceived != 0);
1327  QByteArray data = reply->readAll();
1328  if (!dlInfo->m_outFile.isEmpty())
1329  saveFile(dlInfo->m_outFile, data, appendToFile);
1330 
1331  if (dlInfo->m_data)
1332  dlInfo->m_data->append(data);
1333 
1334  dlInfo->m_bytesReceived = bytesReceived;
1335  dlInfo->m_bytesTotal = bytesTotal;
1336 
1337  QStringList args;
1338  args << dlInfo->m_url;
1339  args << dlInfo->m_outFile;
1340  args << QString::number(bytesReceived);
1341  args << QString::number(bytesTotal);
1342 
1343  QCoreApplication::postEvent(dlInfo->m_caller,
1344  new MythEvent("DOWNLOAD_FILE UPDATE", args));
1345  }
1346 }
1347 
1355 bool MythDownloadManager::saveFile(const QString &outFile,
1356  const QByteArray &data,
1357  const bool append)
1358 {
1359  if (outFile.isEmpty() || !data.size())
1360  return false;
1361 
1362  QFile file(outFile);
1363  QFileInfo fileInfo(outFile);
1364  QDir qdir(fileInfo.absolutePath());
1365 
1366  if (!qdir.exists() && !qdir.mkpath(fileInfo.absolutePath()))
1367  {
1368  LOG(VB_GENERAL, LOG_ERR, QString("Failed to create: '%1'")
1369  .arg(fileInfo.absolutePath()));
1370  return false;
1371  }
1372 
1373  QIODevice::OpenMode mode = QIODevice::Unbuffered|QIODevice::WriteOnly;
1374  if (append)
1375  mode |= QIODevice::Append;
1376 
1377  if (!file.open(mode))
1378  {
1379  LOG(VB_GENERAL, LOG_ERR, QString("Failed to open: '%1'") .arg(outFile));
1380  return false;
1381  }
1382 
1383  off_t offset = 0;
1384  size_t remaining = data.size();
1385  uint failure_cnt = 0;
1386  while ((remaining > 0) && (failure_cnt < 5))
1387  {
1388  ssize_t written = file.write(data.data() + offset, remaining);
1389  if (written < 0)
1390  {
1391  failure_cnt++;
1392  usleep(50000);
1393  continue;
1394  }
1395 
1396  failure_cnt = 0;
1397  offset += written;
1398  remaining -= written;
1399  }
1400 
1401  if (remaining > 0)
1402  return false;
1403 
1404  return true;
1405 }
1406 
1411 QDateTime MythDownloadManager::GetLastModified(const QString &url)
1412 {
1413  // If the header has not expired and
1414  // the last modification date is less than 1 hours old or if
1415  // the cache object is less than 20 minutes old,
1416  // then use the cached header otherwise redownload the header
1417 
1418  static const char dateFormat[] = "ddd, dd MMM yyyy hh:mm:ss 'GMT'";
1419  LOG(VB_FILE, LOG_DEBUG, LOC + QString("GetLastModified('%1')").arg(url));
1420  QDateTime result;
1421 
1422  QDateTime now = MythDate::current();
1423 
1424  QUrl cacheUrl = QUrl(url);
1425 
1426  // Deal with redirects, we want the cached data for the final url
1427  QString redirectLoc;
1428  int limit = 0;
1429  while (!(redirectLoc = getHeader(cacheUrl, "Location")).isNull())
1430  {
1431  if (limit == CACHE_REDIRECTION_LIMIT)
1432  {
1433  LOG(VB_GENERAL, LOG_WARNING, QString("Cache Redirection limit "
1434  "reached for %1")
1435  .arg(cacheUrl.toString()));
1436  return result;
1437  }
1438  cacheUrl.setUrl(redirectLoc);
1439  limit++;
1440  }
1441 
1442  m_infoLock->lock();
1443  QNetworkCacheMetaData urlData = m_manager->cache()->metaData(cacheUrl);
1444  m_infoLock->unlock();
1445 
1446  if (urlData.isValid() &&
1447  ((!urlData.expirationDate().isValid()) ||
1448  (urlData.expirationDate().secsTo(now) < 0)))
1449  {
1450  if (QDateTime(urlData.lastModified().toUTC()).secsTo(now) <= 3600) // 1 Hour
1451  {
1452  result = urlData.lastModified().toUTC();
1453  }
1454  else
1455  {
1456  QString date = getHeader(urlData, "Date");
1457  if (!date.isNull())
1458  {
1459  QDateTime loadDate =
1460  MythDate::fromString(date, dateFormat);
1461  loadDate.setTimeSpec(Qt::UTC);
1462  if (loadDate.secsTo(now) <= 1200) // 20 Minutes
1463  {
1464  result = urlData.lastModified().toUTC();
1465  }
1466  }
1467  }
1468  }
1469 
1470  if (!result.isValid())
1471  {
1472  MythDownloadInfo *dlInfo = new MythDownloadInfo;
1473  dlInfo->m_url = url;
1474  dlInfo->m_syncMode = true;
1475  // Head request, we only want to inspect the headers
1476  dlInfo->m_requestType = kRequestHead;
1477 
1478  if (downloadNow(dlInfo, false))
1479  {
1480  if (dlInfo->m_reply)
1481  {
1482  QVariant lastMod =
1483  dlInfo->m_reply->header(
1484  QNetworkRequest::LastModifiedHeader);
1485  if (lastMod.isValid())
1486  result = lastMod.toDateTime().toUTC();
1487  }
1488 
1489  // downloadNow() will set a flag to trigger downloadFinished()
1490  // to delete the dlInfo if the download times out
1491  delete dlInfo;
1492  }
1493  }
1494 
1495  LOG(VB_FILE, LOG_DEBUG, LOC + QString("GetLastModified('%1'): Result %2")
1496  .arg(url).arg(result.toString()));
1497 
1498  return result;
1499 }
1500 
1501 
1506 {
1507  QMutexLocker locker(&m_cookieLock);
1508 
1509  MythCookieJar *jar = new MythCookieJar;
1510  jar->load(filename);
1511  m_manager->setCookieJar(jar);
1512 }
1513 
1518 {
1519  QMutexLocker locker(&m_cookieLock);
1520 
1521  if (!m_manager->cookieJar())
1522  return;
1523 
1524  MythCookieJar *jar = static_cast<MythCookieJar *>(m_manager->cookieJar());
1525  jar->save(filename);
1526 }
1527 
1528 void MythDownloadManager::setCookieJar(QNetworkCookieJar *cookieJar)
1529 {
1530  QMutexLocker locker(&m_cookieLock);
1531  m_manager->setCookieJar(cookieJar);
1532 }
1533 
1537 QNetworkCookieJar *MythDownloadManager::copyCookieJar(void)
1538 {
1539  QMutexLocker locker(&m_cookieLock);
1540 
1541  if (!m_manager->cookieJar())
1542  return NULL;
1543 
1544  MythCookieJar *inJar = static_cast<MythCookieJar *>(m_manager->cookieJar());
1545  MythCookieJar *outJar = new MythCookieJar(*inJar);
1546 
1547  return static_cast<QNetworkCookieJar *>(outJar);
1548 }
1549 
1553 void MythDownloadManager::refreshCookieJar(QNetworkCookieJar *jar)
1554 {
1555  QMutexLocker locker(&m_cookieLock);
1556  if (m_inCookieJar)
1557  delete m_inCookieJar;
1558 
1559  MythCookieJar *inJar = static_cast<MythCookieJar *>(jar);
1560  MythCookieJar *outJar = new MythCookieJar(*inJar);
1561  m_inCookieJar = static_cast<QNetworkCookieJar *>(outJar);
1562 
1563  QMutexLocker locker2(&m_queueWaitLock);
1564  m_queueWaitCond.wakeAll();
1565 }
1566 
1570 {
1571  QMutexLocker locker(&m_cookieLock);
1572 
1573  MythCookieJar *inJar = static_cast<MythCookieJar *>(m_inCookieJar);
1574  MythCookieJar *outJar = new MythCookieJar(*inJar);
1575  m_manager->setCookieJar(static_cast<QNetworkCookieJar *>(outJar));
1576 
1577  delete m_inCookieJar;
1578  m_inCookieJar = NULL;
1579 }
1580 
1581 QString MythDownloadManager::getHeader(const QUrl& url, const QString& header)
1582 {
1583  if (!m_manager || !m_manager->cache())
1584  return QString::null;
1585 
1586  m_infoLock->lock();
1587  QNetworkCacheMetaData metadata = m_manager->cache()->metaData(url);
1588  m_infoLock->unlock();
1589 
1590  return getHeader(metadata, header);
1591 }
1592 
1598 QString MythDownloadManager::getHeader(const QNetworkCacheMetaData &cacheData,
1599  const QString& header)
1600 {
1601  QNetworkCacheMetaData::RawHeaderList headers = cacheData.rawHeaders();
1602  bool found = false;
1603  QNetworkCacheMetaData::RawHeaderList::iterator it = headers.begin();
1604  for (; !found && it != headers.end(); ++it)
1605  {
1606  if (QString((*it).first) == header)
1607  {
1608  found = true;
1609  return QString((*it).second);
1610  }
1611  }
1612 
1613  return QString::null;
1614 }
1615 
1616 
1621 {
1622  const QList<QNetworkCookie> cookieList = old.allCookies();
1623  setAllCookies(cookieList);
1624 }
1625 
1629 {
1630 }
1631 
1635 void MythCookieJar::load(const QString &filename)
1636 {
1637  LOG(VB_GENERAL, LOG_DEBUG, QString("MythCookieJar: loading cookies from: %1").arg(filename));
1638 
1639  QFile f(filename);
1640  if (!f.open(QIODevice::ReadOnly))
1641  {
1642  LOG(VB_GENERAL, LOG_WARNING, QString("MythCookieJar::load() failed to open file for reading: %1").arg(filename));
1643  return;
1644  }
1645 
1646  QList<QNetworkCookie> cookieList;
1647  QTextStream stream(&f);
1648  while (!stream.atEnd())
1649  {
1650  QString cookie = stream.readLine();
1651  cookieList << QNetworkCookie::parseCookies(cookie.toLocal8Bit());
1652  }
1653 
1654  setAllCookies(cookieList);
1655 }
1656 
1660 void MythCookieJar::save(const QString &filename)
1661 {
1662  LOG(VB_GENERAL, LOG_DEBUG, QString("MythCookieJar: saving cookies to: %1").arg(filename));
1663 
1664  QFile f(filename);
1665  if (!f.open(QIODevice::WriteOnly))
1666  {
1667  LOG(VB_GENERAL, LOG_ERR, QString("MythCookieJar::save() failed to open file for writing: %1").arg(filename));
1668  return;
1669  }
1670 
1671  QList<QNetworkCookie> cookieList = allCookies();
1672  QTextStream stream(&f);
1673 
1674  for (QList<QNetworkCookie>::iterator it = cookieList.begin();
1675  it != cookieList.end(); ++it)
1676  {
1677  stream << (*it).toRawForm() << endl;
1678  }
1679 }
1680 
1681 
1682 /* vim: set expandtab tabstop=4 shiftwidth=4: */
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:216
QNetworkCookieJar * copyCookieJar(void)
Copy from one cookie jar to another.
MYTH_GLsizeiptr const GLvoid * data
QNetworkReply::NetworkError m_errorCode
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:291
void save(const QString &filename)
Saves the cookie jar to a cookie file.
QList< MythDownloadInfo * > m_cancellationQueue
MythDownloadManager * m_parent
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
MRequestType
void(* AuthCallback)(QNetworkReply *, QAuthenticator *, void *)
void queueItem(const QString &url, QNetworkRequest *req, const QString &dest, QByteArray *data, QObject *caller, const MRequestType reqType=kRequestGet, const bool reload=false)
Adds a request to the download queue.
QMap< QString, MythDownloadInfo * > m_downloadInfos
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:83
MythDownloadManager * downloadManager
void start(QRunnable *runnable, QString debugName, int priority=0)
void removeListener(QObject *caller)
Disconnects the specified caller from any existing MythDownloadInfo instances.
QUrl redirectUrl(const QUrl &possibleRedirectUrl, const QUrl &oldRedirectUrl) const
Checks whether we were redirected to the given URL.
void run(void)
Runs a loop to process incoming download requests and triggers download events to be processed...
__int64 ssize_t
Definition: compat.h:72
QNetworkDiskCache * m_diskCache
void queueDownload(const QString &url, const QString &dest, QObject *caller, const bool reload=false)
Adds a url to the download queue.
~MythDownloadManager()
Destructor for MythDownloadManager.
void ShutdownMythDownloadManager(void)
Deletes the running MythDownloadManager at program exit.
A subclassed QNetworkCookieJar that allows for reading and writing cookie files that contain raw form...
void downloadQNetworkRequest(MythDownloadInfo *dlInfo)
Downloads a QNetworkRequest via the QNetworkAccessManager.
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:308
bool saveFile(const QString &outFile, const QByteArray &data, const bool append=false)
Saves a QByteArray of data to a given filename.
QNetworkReply * m_reply
AllMusic * parent
const char * filename
Definition: ioapi.h:135
QList< MythDownloadInfo * > m_downloadQueue
unsigned int uint
Definition: compat.h:136
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythDownloadManager()
Constructor for MythDownloadManager.
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
Slot to process download update events.
const QHash< QByteArray, QByteArray > * m_headers
friend class RemoteFileDownloadThread
MRequestType m_requestType
void downloadFinished(QNetworkReply *reply)
Slot to process download finished events.
bool downloadAuth(const QString &url, const QString &dest, const bool reload=false, AuthCallback authCallback=NULL, void *authArg=NULL, const QHash< QByteArray, QByteArray > *headers=NULL)
Downloads a URL to a file in blocking mode.
QString GetConfDir(void)
Definition: mythdirs.cpp:140
void authCallback(QNetworkReply *reply, QAuthenticator *authenticator)
Signal handler for authentication requests.
QNetworkAccessManager * m_manager
bool postAuth(const QString &url, QByteArray *data, AuthCallback authCallback, void *authArg, const QHash< QByteArray, QByteArray > *headers=NULL)
Posts data to a url via the QNetworkAccessManager.
voidpf uLong offset
Definition: ioapi.h:142
This class is used as a container for messages.
Definition: mythevent.h:15
void setCookieJar(QNetworkCookieJar *cookieJar)
RemoteFileDownloadThread(MythDownloadManager *parent, MythDownloadInfo *dlInfo)
QWaitCondition m_queueWaitCond
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
void queuePost(const QString &url, QByteArray *data, QObject *caller)
Queues a post to a URL via the QNetworkAccessManager.
bool processItem(const QString &url, QNetworkRequest *req, const QString &dest, QByteArray *data, const MRequestType reqType=kRequestGet, const bool reload=false, AuthCallback authCallback=NULL, void *authArg=NULL, const QHash< QByteArray, QByteArray > *headers=NULL)
Processes a network request immediately and waits for a response.
void refreshCookieJar(QNetworkCookieJar *jar)
Refresh the temporary cookie jar from another cookie jar.
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
bool download(const QString &url, const QString &dest, const bool reload=false)
Downloads a URL to a file in blocking mode.
then echo error
Definition: unittests.sh:42
QDateTime GetLastModified(const QString &url)
Gets the Last Modified timestamp for a URI.
QNetworkCookieJar * m_inCookieJar
void downloadError(QNetworkReply::NetworkError errorCode)
Slot to process download error events.
QString getHeader(const QUrl &url, const QString &header)
static MThreadPool * globalInstance(void)
VERBOSE_PREAMBLE Most Errors or other very important messages true
Definition: verbosedefs.h:91
bool SaveAs(QByteArray &data)
void downloadRemoteFile(MythDownloadInfo *dlInfo)
Triggers a myth:// URI download in the background via RemoteFile.
voidpf stream
Definition: ioapi.h:136
void SetDone(bool done)
QMap< QNetworkReply *, MythDownloadInfo * > m_downloadReplies
QNetworkRequest * m_request
void updateCookieJar(void)
Update the cookie jar from the temporary cookie jar.
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:203
bool downloadNow(MythDownloadInfo *dlInfo, bool deleteInfo=true)
Download helper for download() blocking methods.
void cancelDownload(const QString &url, bool block=true)
Cancel a queued or current download.
bool post(const QString &url, QByteArray *data)
Posts data to a url via the QNetworkAccessManager.
static const QString LOC
const char int mode
Definition: ioapi.h:135
static void usleep(unsigned long time)
Definition: mthread.cpp:345
QThread * getQueueThread(void)
QMutex dmCreateLock
QString GetHostName(void)
void saveCookieJar(const QString &filename)
Saves the cookie jar to a cookie file.
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:34
void loadCookieJar(const QString &filename)
Loads the cookie jar from a cookie file.
MythCookieJar()
Creates an empty MythCookieJar.
void load(const QString &filename)
Loads the cookie jar from a cookie file.
void preCache(const QString &url)
Downloads a URL but doesn't store the resulting data anywhere.