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