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  continue;
316 
317  QUrl qurl(dlInfo->m_url);
318  if (m_downloadInfos.contains(qurl.toString()))
319  {
320  // Push request to the end of the queue to let others process.
321  // If this is the only item in the queue, force the loop to
322  // wait a little.
323  if (m_downloadQueue.isEmpty())
324  waitAnyway = true;
325  m_downloadQueue.push_back(dlInfo);
326  m_infoLock->unlock();
327  continue;
328  }
329 
330  if (dlInfo->m_url.startsWith("myth://"))
331  downloadRemoteFile(dlInfo);
332  else
333  {
334  QMutexLocker cLock(&m_cookieLock);
335  downloadQNetworkRequest(dlInfo);
336  }
337 
338  m_downloadInfos[qurl.toString()] = dlInfo;
339  }
340  m_infoLock->unlock();
341  }
342  m_isRunning = false;
343 
344  RunEpilog();
345 }
346 
357 void MythDownloadManager::queueItem(const QString &url, QNetworkRequest *req,
358  const QString &dest, QByteArray *data,
359  QObject *caller, const MRequestType reqType,
360  const bool reload)
361 {
362  MythDownloadInfo *dlInfo = new MythDownloadInfo;
363 
364  dlInfo->m_url = url;
365  dlInfo->m_request = req;
366  dlInfo->m_outFile = dest;
367  dlInfo->m_data = data;
368  dlInfo->m_caller = caller;
369  dlInfo->m_requestType = reqType;
370  dlInfo->m_reload = reload;
371 
372  dlInfo->detach();
373 
374  QMutexLocker locker(m_infoLock);
375  m_downloadQueue.push_back(dlInfo);
376  m_queueWaitCond.wakeAll();
377 }
378 
391 bool MythDownloadManager::processItem(const QString &url, QNetworkRequest *req,
392  const QString &dest, QByteArray *data,
393  const MRequestType reqType,
394  const bool reload,
395  AuthCallback authCallback, void *authArg,
396  const QHash<QByteArray, QByteArray> *headers)
397 {
398  MythDownloadInfo *dlInfo = new MythDownloadInfo;
399 
400  dlInfo->m_url = url;
401  dlInfo->m_request = req;
402  dlInfo->m_outFile = dest;
403  dlInfo->m_data = data;
404  dlInfo->m_requestType = reqType;
405  dlInfo->m_reload = reload;
406  dlInfo->m_syncMode = true;
407  dlInfo->m_authCallback = authCallback;
408  dlInfo->m_authArg = authArg;
409  dlInfo->m_headers = headers;
410 
411  return downloadNow(dlInfo);
412 }
413 
417 void MythDownloadManager::preCache(const QString &url)
418 {
419  LOG(VB_FILE, LOG_DEBUG, LOC + QString("preCache('%1')").arg(url));
420  queueItem(url, NULL, QString(), NULL, NULL);
421 }
422 
429 void MythDownloadManager::queueDownload(const QString &url,
430  const QString &dest,
431  QObject *caller,
432  const bool reload)
433 {
434  LOG(VB_FILE, LOG_DEBUG, LOC + QString("queueDownload('%1', '%2', %3)")
435  .arg(url).arg(dest).arg((long long)caller));
436 
437  queueItem(url, NULL, dest, NULL, caller, kRequestGet, reload);
438 }
439 
445 void MythDownloadManager::queueDownload(QNetworkRequest *req,
446  QByteArray *data,
447  QObject *caller)
448 {
449  LOG(VB_FILE, LOG_DEBUG, LOC + QString("queueDownload('%1', '%2', %3)")
450  .arg(req->url().toString()).arg((long long)data)
451  .arg((long long)caller));
452 
453  queueItem(req->url().toString(), req, QString(), data, caller);
454 }
455 
462 bool MythDownloadManager::download(const QString &url, const QString &dest,
463  const bool reload)
464 {
465  return processItem(url, NULL, dest, NULL, kRequestGet, reload);
466 }
467 
474 bool MythDownloadManager::download(const QString &url, QByteArray *data,
475  const bool reload)
476 {
477  return processItem(url, NULL, QString(), data, kRequestGet, reload);
478 }
479 
486 QNetworkReply *MythDownloadManager::download(const QString &url,
487  const bool reload)
488 {
489  MythDownloadInfo *dlInfo = new MythDownloadInfo;
490  QNetworkReply *reply = NULL;
491 
492  dlInfo->m_url = url;
493  dlInfo->m_reload = reload;
494  dlInfo->m_syncMode = true;
495  dlInfo->m_processReply = false;
496 
497  if (downloadNow(dlInfo, false))
498  {
499  if (dlInfo->m_reply)
500  {
501  reply = dlInfo->m_reply;
502  // prevent dlInfo dtor from deleting the reply
503  dlInfo->m_reply = NULL;
504 
505  delete dlInfo;
506 
507  return reply;
508  }
509 
510  delete dlInfo;
511  }
512 
513  return NULL;
514 }
515 
521 bool MythDownloadManager::download(QNetworkRequest *req, QByteArray *data)
522 {
523  LOG(VB_FILE, LOG_DEBUG, LOC + QString("download('%1', '%2')")
524  .arg(req->url().toString()).arg((long long)data));
525  return processItem(req->url().toString(), req, QString(), data);
526 }
527 
537 bool MythDownloadManager::downloadAuth(const QString &url, const QString &dest,
538  const bool reload, AuthCallback authCallback, void *authArg,
539  const QHash<QByteArray, QByteArray> *headers)
540 {
541  return processItem(url, NULL, dest, NULL, kRequestGet, reload, authCallback,
542  authArg, headers);
543 }
544 
545 
551 void MythDownloadManager::queuePost(const QString &url,
552  QByteArray *data,
553  QObject *caller)
554 {
555  LOG(VB_FILE, LOG_DEBUG, LOC + QString("queuePost('%1', '%2')")
556  .arg(url).arg((long long)data));
557 
558  if (!data)
559  {
560  LOG(VB_GENERAL, LOG_ERR, LOC + "queuePost(), data is NULL!");
561  return;
562  }
563 
564  queueItem(url, NULL, QString(), data, caller, kRequestPost);
565 }
566 
572 void MythDownloadManager::queuePost(QNetworkRequest *req,
573  QByteArray *data,
574  QObject *caller)
575 {
576  LOG(VB_FILE, LOG_DEBUG, LOC + QString("queuePost('%1', '%2')")
577  .arg(req->url().toString()).arg((long long)data));
578 
579  if (!data)
580  {
581  LOG(VB_GENERAL, LOG_ERR, LOC + "queuePost(), data is NULL!");
582  return;
583  }
584 
585  queueItem(req->url().toString(), req, QString(), data, caller,
586  kRequestPost);
587 }
588 
594 bool MythDownloadManager::post(const QString &url, QByteArray *data)
595 {
596  LOG(VB_FILE, LOG_DEBUG, LOC + QString("post('%1', '%2')")
597  .arg(url).arg((long long)data));
598 
599  if (!data)
600  {
601  LOG(VB_GENERAL, LOG_ERR, LOC + "post(), data is NULL!");
602  return false;
603  }
604 
605  return processItem(url, NULL, QString(), data, kRequestPost);
606 }
607 
613 bool MythDownloadManager::post(QNetworkRequest *req, QByteArray *data)
614 {
615  LOG(VB_FILE, LOG_DEBUG, LOC + QString("post('%1', '%2')")
616  .arg(req->url().toString()).arg((long long)data));
617 
618  if (!data)
619  {
620  LOG(VB_GENERAL, LOG_ERR, LOC + "post(), data is NULL!");
621  return false;
622  }
623 
624  return processItem(req->url().toString(), req, QString(), data,
625  kRequestPost);
626 }
627 
636 bool MythDownloadManager::postAuth(const QString &url, QByteArray *data,
637  AuthCallback authCallback, void *authArg,
638  const QHash<QByteArray, QByteArray> *headers)
639 {
640  LOG(VB_FILE, LOG_DEBUG, LOC + QString("postAuth('%1', '%2')")
641  .arg(url).arg((long long)data));
642 
643  if (!data)
644  {
645  LOG(VB_GENERAL, LOG_ERR, LOC + "postAuth(), data is NULL!");
646  return false;
647  }
648 
649  return processItem(url, NULL, NULL, data, kRequestPost, false, authCallback,
650  authArg, headers);
651 }
652 
657 {
658  RemoteFileDownloadThread *dlThread =
659  new RemoteFileDownloadThread(this, dlInfo);
660  MThreadPool::globalInstance()->start(dlThread, "RemoteFileDownload");
661 }
662 
667 {
668  if (!dlInfo)
669  return;
670 
671  static const char dateFormat[] = "ddd, dd MMM yyyy hh:mm:ss 'GMT'";
672  QUrl qurl(dlInfo->m_url);
673  QNetworkRequest request;
674 
675  if (dlInfo->m_request)
676  {
677  request = *dlInfo->m_request;
678  delete dlInfo->m_request;
679  dlInfo->m_request = NULL;
680  }
681  else
682  request.setUrl(qurl);
683 
684  if (!dlInfo->m_reload)
685  {
686  // Prefer the in-cache item if one exists and it is less than 5 minutes
687  // old and it will not expire in the next 10 seconds
688  QDateTime now = MythDate::current();
689 
690  // Handle redirects, we want the metadata of the file headers
691  QString redirectLoc;
692  int limit = 0;
693  while (!(redirectLoc = getHeader(qurl, "Location")).isNull())
694  {
695  if (limit == CACHE_REDIRECTION_LIMIT)
696  {
697  LOG(VB_GENERAL, LOG_WARNING, QString("Cache Redirection limit "
698  "reached for %1")
699  .arg(qurl.toString()));
700  return;
701  }
702  qurl.setUrl(redirectLoc);
703  limit++;
704  }
705 
706  LOG(VB_NETWORK, LOG_DEBUG, QString("Checking cache for %1")
707  .arg(qurl.toString()));
708 
709  m_infoLock->lock();
710  QNetworkCacheMetaData urlData = m_manager->cache()->metaData(qurl);
711  m_infoLock->unlock();
712  if ((urlData.isValid()) &&
713  ((!urlData.expirationDate().isValid()) ||
714  (QDateTime(urlData.expirationDate().toUTC()).secsTo(now) < 10)))
715  {
716  QString dateString = getHeader(urlData, "Date");
717 
718  if (!dateString.isNull())
719  {
720  QDateTime loadDate =
721  MythDate::fromString(dateString, dateFormat);
722  loadDate.setTimeSpec(Qt::UTC);
723  if (loadDate.secsTo(now) <= 720)
724  {
725  dlInfo->m_preferCache = true;
726  LOG(VB_NETWORK, LOG_DEBUG, QString("Preferring cache for %1")
727  .arg(qurl.toString()));
728  }
729  }
730  }
731  }
732 
733  if (dlInfo->m_preferCache)
734  request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
735  QNetworkRequest::PreferCache);
736 
737  request.setRawHeader("User-Agent",
738  "MythTV v" MYTH_BINARY_VERSION " MythDownloadManager");
739 
740  if (dlInfo->m_headers)
741  {
742  QHash<QByteArray, QByteArray>::const_iterator it =
743  dlInfo->m_headers->constBegin();
744  for ( ; it != dlInfo->m_headers->constEnd(); ++it )
745  {
746  if (!it.key().isEmpty() && !it.value().isEmpty())
747  {
748  request.setRawHeader(it.key(), it.value());
749  }
750  }
751  }
752 
753  switch (dlInfo->m_requestType)
754  {
755  case kRequestPost :
756  dlInfo->m_reply = m_manager->post(request, *dlInfo->m_data);
757  break;
758  case kRequestHead :
759  dlInfo->m_reply = m_manager->head(request);
760  break;
761  case kRequestGet :
762  default:
763  dlInfo->m_reply = m_manager->get(request);
764  break;
765  }
766 
767  m_downloadReplies[dlInfo->m_reply] = dlInfo;
768 
769  if (dlInfo->m_authCallback)
770  {
771  connect(m_manager, SIGNAL(authenticationRequired(QNetworkReply *,
772  QAuthenticator *)),
773  this, SLOT(authCallback(QNetworkReply *, QAuthenticator *)));
774  }
775 
776  connect(dlInfo->m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
777  SLOT(downloadError(QNetworkReply::NetworkError)));
778  connect(dlInfo->m_reply, SIGNAL(downloadProgress(qint64, qint64)),
779  this, SLOT(downloadProgress(qint64, qint64)));
780 }
781 
786 void MythDownloadManager::authCallback(QNetworkReply *reply,
787  QAuthenticator *authenticator)
788 {
789  if (!reply)
790  return;
791 
792  MythDownloadInfo *dlInfo = m_downloadReplies[reply];
793 
794  if (!dlInfo)
795  return;
796 
797  if (dlInfo->m_authCallback)
798  {
799  LOG(VB_FILE, LOG_DEBUG, "Calling auth callback");
800  dlInfo->m_authCallback(reply, authenticator, dlInfo->m_authArg);
801  }
802 }
803 
811 {
812  if (!dlInfo)
813  return false;
814 
815  dlInfo->m_syncMode = true;
816 
817  m_infoLock->lock();
818  m_downloadQueue.push_back(dlInfo);
819  m_infoLock->unlock();
820  m_queueWaitCond.wakeAll();
821 
822  // timeout myth:// RemoteFile transfers 20 seconds from now
823  // timeout non-myth:// QNetworkAccessManager transfers 60 seconds after
824  // their last progress update
825  QDateTime startedAt = MythDate::current();
826  m_infoLock->lock();
827  while ((!dlInfo->IsDone()) &&
828  (dlInfo->m_errorCode == QNetworkReply::NoError) &&
829  (((!dlInfo->m_url.startsWith("myth://")) &&
830  (dlInfo->m_lastStat.secsTo(MythDate::current()) < 60)) ||
831  ((dlInfo->m_url.startsWith("myth://")) &&
832  (startedAt.secsTo(MythDate::current()) < 20))))
833  {
834  m_infoLock->unlock();
835  m_queueWaitLock.lock();
836  m_queueWaitCond.wait(&m_queueWaitLock, 200);
837  m_queueWaitLock.unlock();
838  m_infoLock->lock();
839  }
840  bool done = dlInfo->IsDone();
841  bool success =
842  done && (dlInfo->m_errorCode == QNetworkReply::NoError);
843 
844  if (!done)
845  {
846  dlInfo->m_data = NULL; // Prevent downloadFinished() from updating
847  dlInfo->m_syncMode = false; // Let downloadFinished() cleanup for us
848  if ((dlInfo->m_reply) &&
849  (dlInfo->m_errorCode == QNetworkReply::NoError))
850  {
851  LOG(VB_FILE, LOG_DEBUG,
852  LOC + QString("Aborting download - lack of data transfer"));
853  dlInfo->m_reply->abort();
854  }
855  }
856  else if (deleteInfo)
857  {
858  delete dlInfo;
859  dlInfo = NULL;
860  }
861 
862  m_infoLock->unlock();
863 
864  return success;
865 }
866 
870 void MythDownloadManager::cancelDownload(const QString &url, bool block)
871 {
872  cancelDownload(QStringList(url), block);
873 }
874 
878 void MythDownloadManager::cancelDownload(const QStringList &urls, bool block)
879 {
880  MythDownloadInfo *dlInfo;
881 
882  m_infoLock->lock();
883  foreach (QString url, urls)
884  {
885  QMutableListIterator<MythDownloadInfo*> lit(m_downloadQueue);
886  while (lit.hasNext())
887  {
888  lit.next();
889  dlInfo = lit.value();
890  if (dlInfo->m_url == url)
891  {
892  if (!m_cancellationQueue.contains(dlInfo))
893  m_cancellationQueue.append(dlInfo);
894  lit.remove();
895  }
896  }
897 
898  if (m_downloadInfos.contains(url))
899  {
900  dlInfo = m_downloadInfos[url];
901 
902  if (!m_cancellationQueue.contains(dlInfo))
903  m_cancellationQueue.append(dlInfo);
904 
905  if (dlInfo->m_reply)
906  m_downloadReplies.remove(dlInfo->m_reply);
907 
908  m_downloadInfos.remove(url);
909  }
910  }
911  m_infoLock->unlock();
912 
913  if (QThread::currentThread() == this->thread())
914  {
916  return;
917  }
918 
919  // wake-up running thread
920  m_queueWaitCond.wakeAll();
921 
922  if (!block)
923  return;
924 
925  while (!m_cancellationQueue.isEmpty())
926  {
927  usleep(50000); // re-test in another 50ms
928  }
929 }
930 
932 {
933  QMutexLocker locker(m_infoLock);
934  MythDownloadInfo *dlInfo;
935 
936  QMutableListIterator<MythDownloadInfo*> lit(m_cancellationQueue);
937  while (lit.hasNext())
938  {
939  lit.next();
940  dlInfo = lit.value();
941  dlInfo->m_lock.lock();
942 
943  if (dlInfo->m_reply)
944  {
945  LOG(VB_FILE, LOG_DEBUG,
946  LOC + QString("Aborting download - user request"));
947  dlInfo->m_reply->abort();
948  }
949  lit.remove();
950  if (dlInfo->m_done)
951  {
952  dlInfo->m_lock.unlock();
953  continue;
954  }
955  dlInfo->m_errorCode = QNetworkReply::OperationCanceledError;
956  dlInfo->m_done = true;
957  dlInfo->m_lock.unlock();
958  }
959 }
960 
966 {
967  QMutexLocker locker(m_infoLock);
968  MythDownloadInfo *dlInfo;
969 
970  QList <MythDownloadInfo*>::iterator lit = m_downloadQueue.begin();
971  for (; lit != m_downloadQueue.end(); ++lit)
972  {
973  dlInfo = *lit;
974  if (dlInfo->m_caller == caller)
975  {
976  dlInfo->m_caller = NULL;
977  dlInfo->m_outFile = QString();
978  dlInfo->m_data = NULL;
979  }
980  }
981 
982  QMap <QString, MythDownloadInfo*>::iterator mit = m_downloadInfos.begin();
983  for (; mit != m_downloadInfos.end(); ++mit)
984  {
985  dlInfo = mit.value();
986  if (dlInfo->m_caller == caller)
987  {
988  dlInfo->m_caller = NULL;
989  dlInfo->m_outFile = QString();
990  dlInfo->m_data = NULL;
991  }
992  }
993 }
994 
998 void MythDownloadManager::downloadError(QNetworkReply::NetworkError errorCode)
999 {
1000  QNetworkReply *reply = (QNetworkReply*)sender();
1001 
1002  LOG(VB_FILE, LOG_DEBUG, LOC + QString("downloadError %1 ")
1003  .arg(errorCode) + reply->errorString() );
1004 
1005  QMutexLocker locker(m_infoLock);
1006  if (!m_downloadReplies.contains(reply))
1007  {
1008  reply->deleteLater();
1009  return;
1010  }
1011 
1012  MythDownloadInfo *dlInfo = m_downloadReplies[reply];
1013 
1014  if (!dlInfo)
1015  return;
1016 
1017  dlInfo->m_errorCode = errorCode;
1018 }
1019 
1025 QUrl MythDownloadManager::redirectUrl(const QUrl& possibleRedirectUrl,
1026  const QUrl& oldRedirectUrl) const
1027 {
1028  LOG(VB_FILE, LOG_DEBUG, LOC + QString("redirectUrl()"));
1029  QUrl redirectUrl;
1030 
1031  if(!possibleRedirectUrl.isEmpty() && possibleRedirectUrl != oldRedirectUrl)
1032  redirectUrl = possibleRedirectUrl;
1033 
1034  return redirectUrl;
1035 }
1036 
1040 void MythDownloadManager::downloadFinished(QNetworkReply* reply)
1041 {
1042  LOG(VB_FILE, LOG_DEBUG, LOC + QString("downloadFinished(%1)")
1043  .arg((long long)reply));
1044 
1045  QMutexLocker locker(m_infoLock);
1046  if (!m_downloadReplies.contains(reply))
1047  {
1048  reply->deleteLater();
1049  return;
1050  }
1051 
1052  MythDownloadInfo *dlInfo = m_downloadReplies[reply];
1053 
1054  if (!dlInfo || !dlInfo->m_reply)
1055  return;
1056 
1057  downloadFinished(dlInfo);
1058 }
1059 
1064 {
1065  if (!dlInfo)
1066  return;
1067 
1068  static const char dateFormat[] = "ddd, dd MMM yyyy hh:mm:ss 'GMT'";
1069  QNetworkReply *reply = dlInfo->m_reply;
1070 
1071  if (reply)
1072  {
1073  QUrl possibleRedirectUrl =
1074  reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
1075 
1076  dlInfo->m_redirectedTo =
1077  redirectUrl(possibleRedirectUrl, dlInfo->m_redirectedTo);
1078  }
1079 
1080  if(!dlInfo->m_redirectedTo.isEmpty())
1081  {
1082  LOG(VB_FILE, LOG_DEBUG, LOC +
1083  QString("downloadFinished(%1): Redirect: %2 -> %3")
1084  .arg((long long)dlInfo)
1085  .arg(reply->url().toString())
1086  .arg(dlInfo->m_redirectedTo.toString()));
1087 
1088  if (dlInfo->m_data)
1089  dlInfo->m_data->clear();
1090 
1091  dlInfo->m_bytesReceived = 0;
1092  dlInfo->m_bytesTotal = 0;
1093 
1094  QNetworkRequest request(dlInfo->m_redirectedTo);
1095  if (dlInfo->m_preferCache)
1096  request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
1097  QNetworkRequest::PreferCache);
1098  request.setRawHeader("User-Agent",
1099  "MythDownloadManager v" MYTH_BINARY_VERSION);
1100 
1101  switch (dlInfo->m_requestType)
1102  {
1103  case kRequestPost :
1104  dlInfo->m_reply = m_manager->post(request, *dlInfo->m_data);
1105  break;
1106  case kRequestHead :
1107  dlInfo->m_reply = m_manager->head(request);
1108  break;
1109  case kRequestGet :
1110  default:
1111  dlInfo->m_reply = m_manager->get(request);
1112  break;
1113  }
1114 
1115  m_downloadReplies[dlInfo->m_reply] = dlInfo;
1116 
1117  connect(dlInfo->m_reply, SIGNAL(error(QNetworkReply::NetworkError)),
1118  this, SLOT(downloadError(QNetworkReply::NetworkError)));
1119  connect(dlInfo->m_reply, SIGNAL(downloadProgress(qint64, qint64)),
1120  this, SLOT(downloadProgress(qint64, qint64)));
1121 
1122  m_downloadReplies.remove(reply);
1123  reply->deleteLater();
1124  }
1125  else
1126  {
1127  LOG(VB_FILE, LOG_DEBUG, QString("downloadFinished(%1): COMPLETE: %2")
1128  .arg((long long)dlInfo).arg(dlInfo->m_url));
1129 
1130  // HACK Insert a Date header into the cached metadata if one doesn't
1131  // already exist
1132  QUrl fileUrl = dlInfo->m_url;
1133  QString redirectLoc;
1134  int limit = 0;
1135  while (!(redirectLoc = getHeader(fileUrl, "Location")).isNull())
1136  {
1137  if (limit == CACHE_REDIRECTION_LIMIT)
1138  {
1139  LOG(VB_GENERAL, LOG_WARNING, QString("Cache Redirection limit "
1140  "reached for %1")
1141  .arg(fileUrl.toString()));
1142  return;
1143  }
1144  fileUrl.setUrl(redirectLoc);
1145  limit++;
1146  }
1147 
1148  m_infoLock->lock();
1149  QNetworkCacheMetaData urlData = m_manager->cache()->metaData(fileUrl);
1150  m_infoLock->unlock();
1151  if (getHeader(urlData, "Date").isNull())
1152  {
1153  QNetworkCacheMetaData::RawHeaderList headers = urlData.rawHeaders();
1154  QNetworkCacheMetaData::RawHeader newheader;
1155  QDateTime now = MythDate::current();
1156  newheader = QNetworkCacheMetaData::RawHeader("Date",
1157  now.toString(dateFormat).toLatin1());
1158  headers.append(newheader);
1159  urlData.setRawHeaders(headers);
1160  m_infoLock->lock();
1161  m_manager->cache()->updateMetaData(urlData);
1162  m_infoLock->unlock();
1163  }
1164  // End HACK
1165 
1166  dlInfo->m_redirectedTo.clear();
1167 
1168  int dataSize = -1;
1169 
1170  // If we downloaded via the QNetworkAccessManager
1171  // AND the caller isn't handling the reply directly
1172  if (reply && dlInfo->m_processReply)
1173  {
1174  bool append = (!dlInfo->m_syncMode && dlInfo->m_caller);
1175  QByteArray data = reply->readAll();
1176  dataSize = data.size();
1177 
1178  if (append)
1179  dlInfo->m_bytesReceived += dataSize;
1180  else
1181  dlInfo->m_bytesReceived = dataSize;
1182 
1183  dlInfo->m_bytesTotal = dlInfo->m_bytesReceived;
1184 
1185  if (dlInfo->m_data)
1186  {
1187  if (append)
1188  dlInfo->m_data->append(data);
1189  else
1190  *dlInfo->m_data = data;
1191  }
1192  else if (!dlInfo->m_outFile.isEmpty())
1193  {
1194  saveFile(dlInfo->m_outFile, data, append);
1195  }
1196  }
1197  else if (!reply) // If we downloaded via RemoteFile
1198  {
1199  if (dlInfo->m_data)
1200  {
1201  (*dlInfo->m_data) = dlInfo->m_privData;
1202  }
1203  else if (!dlInfo->m_outFile.isEmpty())
1204  {
1205  saveFile(dlInfo->m_outFile, dlInfo->m_privData);
1206  }
1207  dlInfo->m_bytesReceived += dataSize;
1208  dlInfo->m_bytesTotal = dlInfo->m_bytesReceived;
1209  }
1210  // else we downloaded via QNetworkAccessManager
1211  // AND the caller is handling the reply
1212 
1213  m_downloadInfos.remove(dlInfo->m_url);
1214  if (reply)
1215  m_downloadReplies.remove(reply);
1216 
1217  dlInfo->SetDone(true);
1218 
1219  if (!dlInfo->m_syncMode)
1220  {
1221  if (dlInfo->m_caller)
1222  {
1223  LOG(VB_FILE, LOG_DEBUG, QString("downloadFinished(%1): "
1224  "COMPLETE: %2, sending event to caller")
1225  .arg((long long)dlInfo).arg(dlInfo->m_url));
1226 
1227  QStringList args;
1228  args << dlInfo->m_url;
1229  args << dlInfo->m_outFile;
1230  args << QString::number(dlInfo->m_bytesTotal);
1231  // placeholder for error string
1232  args << (reply ? reply->errorString() : QString());
1233  args << QString::number((int)(reply ? reply->error() :
1234  dlInfo->m_errorCode));
1235 
1236  QCoreApplication::postEvent(dlInfo->m_caller,
1237  new MythEvent("DOWNLOAD_FILE FINISHED", args));
1238  }
1239 
1240  delete dlInfo;
1241  dlInfo = NULL;
1242  }
1243 
1244  m_queueWaitCond.wakeAll();
1245  }
1246 }
1247 
1253 void MythDownloadManager::downloadProgress(qint64 bytesReceived,
1254  qint64 bytesTotal)
1255 {
1256  QNetworkReply *reply = (QNetworkReply*)sender();
1257 
1258  LOG(VB_FILE, LOG_DEBUG, LOC +
1259  QString("downloadProgress(%1, %2) (for reply %3)")
1260  .arg(bytesReceived).arg(bytesTotal).arg((long long)reply));
1261 
1262  QMutexLocker locker(m_infoLock);
1263  if (!m_downloadReplies.contains(reply))
1264  return;
1265 
1266  MythDownloadInfo *dlInfo = m_downloadReplies[reply];
1267 
1268  if (!dlInfo)
1269  return;
1270 
1271  dlInfo->m_lastStat = MythDate::current();
1272 
1273  LOG(VB_FILE, LOG_DEBUG, LOC +
1274  QString("downloadProgress: %1 to %2 is at %3 of %4 bytes downloaded")
1275  .arg(dlInfo->m_url).arg(dlInfo->m_outFile)
1276  .arg(bytesReceived).arg(bytesTotal));
1277 
1278  if (!dlInfo->m_syncMode && dlInfo->m_caller)
1279  {
1280  LOG(VB_FILE, LOG_DEBUG, QString("downloadProgress(%1): "
1281  "sending event to caller")
1282  .arg(reply->url().toString()));
1283 
1284  bool appendToFile = (dlInfo->m_bytesReceived != 0);
1285  QByteArray data = reply->readAll();
1286  if (!dlInfo->m_outFile.isEmpty())
1287  saveFile(dlInfo->m_outFile, data, appendToFile);
1288 
1289  if (dlInfo->m_data)
1290  dlInfo->m_data->append(data);
1291 
1292  dlInfo->m_bytesReceived = bytesReceived;
1293  dlInfo->m_bytesTotal = bytesTotal;
1294 
1295  QStringList args;
1296  args << dlInfo->m_url;
1297  args << dlInfo->m_outFile;
1298  args << QString::number(bytesReceived);
1299  args << QString::number(bytesTotal);
1300 
1301  QCoreApplication::postEvent(dlInfo->m_caller,
1302  new MythEvent("DOWNLOAD_FILE UPDATE", args));
1303  }
1304 }
1305 
1313 bool MythDownloadManager::saveFile(const QString &outFile,
1314  const QByteArray &data,
1315  const bool append)
1316 {
1317  if (outFile.isEmpty() || !data.size())
1318  return false;
1319 
1320  QFile file(outFile);
1321  QFileInfo fileInfo(outFile);
1322  QDir qdir(fileInfo.absolutePath());
1323 
1324  if (!qdir.exists() && !qdir.mkpath(fileInfo.absolutePath()))
1325  {
1326  LOG(VB_GENERAL, LOG_ERR, QString("Failed to create: '%1'")
1327  .arg(fileInfo.absolutePath()));
1328  return false;
1329  }
1330 
1331  QIODevice::OpenMode mode = QIODevice::Unbuffered|QIODevice::WriteOnly;
1332  if (append)
1333  mode |= QIODevice::Append;
1334 
1335  if (!file.open(mode))
1336  {
1337  LOG(VB_GENERAL, LOG_ERR, QString("Failed to open: '%1'") .arg(outFile));
1338  return false;
1339  }
1340 
1341  off_t offset = 0;
1342  size_t remaining = data.size();
1343  uint failure_cnt = 0;
1344  while ((remaining > 0) && (failure_cnt < 5))
1345  {
1346  ssize_t written = file.write(data.data() + offset, remaining);
1347  if (written < 0)
1348  {
1349  failure_cnt++;
1350  usleep(50000);
1351  continue;
1352  }
1353 
1354  failure_cnt = 0;
1355  offset += written;
1356  remaining -= written;
1357  }
1358 
1359  if (remaining > 0)
1360  return false;
1361 
1362  return true;
1363 }
1364 
1369 QDateTime MythDownloadManager::GetLastModified(const QString &url)
1370 {
1371  // If the header has not expired and
1372  // the last modification date is less than 1 hours old or if
1373  // the cache object is less than 20 minutes old,
1374  // then use the cached header otherwise redownload the header
1375 
1376  static const char dateFormat[] = "ddd, dd MMM yyyy hh:mm:ss 'GMT'";
1377  LOG(VB_FILE, LOG_DEBUG, LOC + QString("GetLastModified('%1')").arg(url));
1378  QDateTime result;
1379 
1380  QDateTime now = MythDate::current();
1381 
1382  QUrl cacheUrl = QUrl(url);
1383 
1384  // Deal with redirects, we want the cached data for the final url
1385  QString redirectLoc;
1386  int limit = 0;
1387  while (!(redirectLoc = getHeader(cacheUrl, "Location")).isNull())
1388  {
1389  if (limit == CACHE_REDIRECTION_LIMIT)
1390  {
1391  LOG(VB_GENERAL, LOG_WARNING, QString("Cache Redirection limit "
1392  "reached for %1")
1393  .arg(cacheUrl.toString()));
1394  return result;
1395  }
1396  cacheUrl.setUrl(redirectLoc);
1397  limit++;
1398  }
1399 
1400  m_infoLock->lock();
1401  QNetworkCacheMetaData urlData = m_manager->cache()->metaData(cacheUrl);
1402  m_infoLock->unlock();
1403 
1404  if (urlData.isValid() &&
1405  ((!urlData.expirationDate().isValid()) ||
1406  (urlData.expirationDate().secsTo(now) < 0)))
1407  {
1408  if (QDateTime(urlData.lastModified().toUTC()).secsTo(now) <= 3600) // 1 Hour
1409  {
1410  result = urlData.lastModified().toUTC();
1411  }
1412  else
1413  {
1414  QString date = getHeader(urlData, "Date");
1415  if (!date.isNull())
1416  {
1417  QDateTime loadDate =
1418  MythDate::fromString(date, dateFormat);
1419  loadDate.setTimeSpec(Qt::UTC);
1420  if (loadDate.secsTo(now) <= 1200) // 20 Minutes
1421  {
1422  result = urlData.lastModified().toUTC();
1423  }
1424  }
1425  }
1426  }
1427 
1428  if (!result.isValid())
1429  {
1430  MythDownloadInfo *dlInfo = new MythDownloadInfo;
1431  dlInfo->m_url = url;
1432  dlInfo->m_syncMode = true;
1433  // Head request, we only want to inspect the headers
1434  dlInfo->m_requestType = kRequestHead;
1435 
1436  if (downloadNow(dlInfo, false))
1437  {
1438  if (dlInfo->m_reply)
1439  {
1440  QVariant lastMod =
1441  dlInfo->m_reply->header(
1442  QNetworkRequest::LastModifiedHeader);
1443  if (lastMod.isValid())
1444  result = lastMod.toDateTime().toUTC();
1445  }
1446 
1447  // downloadNow() will set a flag to trigger downloadFinished()
1448  // to delete the dlInfo if the download times out
1449  delete dlInfo;
1450  }
1451  }
1452 
1453  LOG(VB_FILE, LOG_DEBUG, LOC + QString("GetLastModified('%1'): Result %2")
1454  .arg(url).arg(result.toString()));
1455 
1456  return result;
1457 }
1458 
1459 
1464 {
1465  QMutexLocker locker(&m_cookieLock);
1466 
1467  MythCookieJar *jar = new MythCookieJar;
1468  jar->load(filename);
1469  m_manager->setCookieJar(jar);
1470 }
1471 
1476 {
1477  QMutexLocker locker(&m_cookieLock);
1478 
1479  if (!m_manager->cookieJar())
1480  return;
1481 
1482  MythCookieJar *jar = static_cast<MythCookieJar *>(m_manager->cookieJar());
1483  jar->save(filename);
1484 }
1485 
1486 void MythDownloadManager::setCookieJar(QNetworkCookieJar *cookieJar)
1487 {
1488  QMutexLocker locker(&m_cookieLock);
1489  m_manager->setCookieJar(cookieJar);
1490 }
1491 
1495 QNetworkCookieJar *MythDownloadManager::copyCookieJar(void)
1496 {
1497  QMutexLocker locker(&m_cookieLock);
1498 
1499  if (!m_manager->cookieJar())
1500  return NULL;
1501 
1502  MythCookieJar *inJar = static_cast<MythCookieJar *>(m_manager->cookieJar());
1503  MythCookieJar *outJar = new MythCookieJar(*inJar);
1504 
1505  return static_cast<QNetworkCookieJar *>(outJar);
1506 }
1507 
1511 void MythDownloadManager::refreshCookieJar(QNetworkCookieJar *jar)
1512 {
1513  QMutexLocker locker(&m_cookieLock);
1514  if (m_inCookieJar)
1515  delete m_inCookieJar;
1516 
1517  MythCookieJar *inJar = static_cast<MythCookieJar *>(jar);
1518  MythCookieJar *outJar = new MythCookieJar(*inJar);
1519  m_inCookieJar = static_cast<QNetworkCookieJar *>(outJar);
1520 
1521  QMutexLocker locker2(&m_queueWaitLock);
1522  m_queueWaitCond.wakeAll();
1523 }
1524 
1528 {
1529  QMutexLocker locker(&m_cookieLock);
1530 
1531  MythCookieJar *inJar = static_cast<MythCookieJar *>(m_inCookieJar);
1532  MythCookieJar *outJar = new MythCookieJar(*inJar);
1533  m_manager->setCookieJar(static_cast<QNetworkCookieJar *>(outJar));
1534 
1535  delete m_inCookieJar;
1536  m_inCookieJar = NULL;
1537 }
1538 
1539 QString MythDownloadManager::getHeader(const QUrl& url, const QString& header)
1540 {
1541  if (!m_manager || !m_manager->cache())
1542  return QString::null;
1543 
1544  m_infoLock->lock();
1545  QNetworkCacheMetaData metadata = m_manager->cache()->metaData(url);
1546  m_infoLock->unlock();
1547 
1548  return getHeader(metadata, header);
1549 }
1550 
1556 QString MythDownloadManager::getHeader(const QNetworkCacheMetaData &cacheData,
1557  const QString& header)
1558 {
1559  QNetworkCacheMetaData::RawHeaderList headers = cacheData.rawHeaders();
1560  bool found = false;
1561  QNetworkCacheMetaData::RawHeaderList::iterator it = headers.begin();
1562  for (; !found && it != headers.end(); ++it)
1563  {
1564  if (QString((*it).first) == header)
1565  {
1566  found = true;
1567  return QString((*it).second);
1568  }
1569  }
1570 
1571  return QString::null;
1572 }
1573 
1574 
1579 {
1580  const QList<QNetworkCookie> cookieList = old.allCookies();
1581  setAllCookies(cookieList);
1582 }
1583 
1587 {
1588 }
1589 
1593 void MythCookieJar::load(const QString &filename)
1594 {
1595  LOG(VB_GENERAL, LOG_DEBUG, QString("MythCookieJar: loading cookies from: %1").arg(filename));
1596 
1597  QFile f(filename);
1598  if (!f.open(QIODevice::ReadOnly))
1599  {
1600  LOG(VB_GENERAL, LOG_WARNING, QString("MythCookieJar::load() failed to open file for reading: %1").arg(filename));
1601  return;
1602  }
1603 
1604  QList<QNetworkCookie> cookieList;
1605  QTextStream stream(&f);
1606  while (!stream.atEnd())
1607  {
1608  QString cookie = stream.readLine();
1609  cookieList << QNetworkCookie::parseCookies(cookie.toLocal8Bit());
1610  }
1611 
1612  setAllCookies(cookieList);
1613 }
1614 
1618 void MythCookieJar::save(const QString &filename)
1619 {
1620  LOG(VB_GENERAL, LOG_DEBUG, QString("MythCookieJar: saving cookies to: %1").arg(filename));
1621 
1622  QFile f(filename);
1623  if (!f.open(QIODevice::WriteOnly))
1624  {
1625  LOG(VB_GENERAL, LOG_ERR, QString("MythCookieJar::save() failed to open file for writing: %1").arg(filename));
1626  return;
1627  }
1628 
1629  QList<QNetworkCookie> cookieList = allCookies();
1630  QTextStream stream(&f);
1631 
1632  for (QList<QNetworkCookie>::iterator it = cookieList.begin();
1633  it != cookieList.end(); ++it)
1634  {
1635  stream << (*it).toRawForm() << endl;
1636  }
1637 }
1638 
1639 
1640 /* 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
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:135
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
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:83
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
dest
Definition: minilzo.cpp:2074
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.
VERBOSE_PREAMBLE Most Errors or other very important messages true
Definition: verbosedefs.h:91
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)
bool SaveAs(QByteArray &data)
Definition: remotefile.cpp:995
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.