MythTV  master
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 #include <QUrl>
15 
16 #include "stdlib.h"
17 
18 #include <unistd.h> // for usleep()
19 
20 // libmythbase
21 #include "compat.h"
22 #include "mythcorecontext.h"
23 #include "mythcoreutil.h"
24 #include "mthreadpool.h"
25 #include "mythdirs.h"
26 #include "mythevent.h"
27 #include "mythversion.h"
28 #include "remotefile.h"
29 #include "mythdate.h"
30 
31 #include "mythdownloadmanager.h"
32 #include "mythlogging.h"
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  LOG(VB_FILE, LOG_DEBUG, LOC + QString("items downloading %1").arg(m_downloadInfos.count()));
282  LOG(VB_FILE, LOG_DEBUG, LOC + QString("items queued %1").arg(m_downloadQueue.count()));
283  downloading = !m_downloadInfos.isEmpty();
284  itemsInCancellationQueue = !m_cancellationQueue.isEmpty();
285  m_infoLock->unlock();
286 
287  if (itemsInCancellationQueue)
288  {
290  }
291  if (downloading)
292  QCoreApplication::processEvents();
293 
294  m_infoLock->lock();
295  itemsInQueue = !m_downloadQueue.isEmpty();
296  m_infoLock->unlock();
297 
298  if (!itemsInQueue || waitAnyway)
299  {
300  waitAnyway = false;
301  m_queueWaitLock.lock();
302 
303  if (downloading)
304  {
305  LOG(VB_FILE, LOG_DEBUG, LOC + QString("waiting 200ms"));
306  m_queueWaitCond.wait(&m_queueWaitLock, 200);
307  }
308  else
309  {
310  LOG(VB_FILE, LOG_DEBUG, LOC + QString("waiting for more items to download"));
312  }
313 
314  m_queueWaitLock.unlock();
315  }
316 
317  m_infoLock->lock();
318  if (!m_downloadQueue.isEmpty())
319  {
320  MythDownloadInfo *dlInfo = m_downloadQueue.front();
321 
322  m_downloadQueue.pop_front();
323 
324  if (!dlInfo)
325  {
326  m_infoLock->unlock();
327  continue;
328  }
329 
330  if (m_downloadInfos.contains(dlInfo->m_url))
331  {
332  // Push request to the end of the queue to let others process.
333  // If this is the only item in the queue, force the loop to
334  // wait a little.
335  if (m_downloadQueue.isEmpty())
336  waitAnyway = true;
337  m_downloadQueue.push_back(dlInfo);
338  m_infoLock->unlock();
339  continue;
340  }
341 
342  if (dlInfo->m_url.startsWith("myth://"))
343  downloadRemoteFile(dlInfo);
344  else
345  {
346  QMutexLocker cLock(&m_cookieLock);
347  downloadQNetworkRequest(dlInfo);
348  }
349 
350  m_downloadInfos[dlInfo->m_url] = dlInfo;
351  }
352  m_infoLock->unlock();
353  }
354  m_isRunning = false;
355 
356  RunEpilog();
357 }
358 
369 void MythDownloadManager::queueItem(const QString &url, QNetworkRequest *req,
370  const QString &dest, QByteArray *data,
371  QObject *caller, const MRequestType reqType,
372  const bool reload)
373 {
374  MythDownloadInfo *dlInfo = new MythDownloadInfo;
375 
376  dlInfo->m_url = url;
377  dlInfo->m_request = req;
378  dlInfo->m_outFile = dest;
379  dlInfo->m_data = data;
380  dlInfo->m_caller = caller;
381  dlInfo->m_requestType = reqType;
382  dlInfo->m_reload = reload;
383 
384  dlInfo->detach();
385 
386  QMutexLocker locker(m_infoLock);
387  m_downloadQueue.push_back(dlInfo);
388  m_queueWaitCond.wakeAll();
389 }
390 
403 bool MythDownloadManager::processItem(const QString &url, QNetworkRequest *req,
404  const QString &dest, QByteArray *data,
405  const MRequestType reqType,
406  const bool reload,
407  AuthCallback authCallback, void *authArg,
408  const QHash<QByteArray, QByteArray> *headers)
409 {
410  MythDownloadInfo *dlInfo = new MythDownloadInfo;
411 
412  dlInfo->m_url = url;
413  dlInfo->m_request = req;
414  dlInfo->m_outFile = dest;
415  dlInfo->m_data = data;
416  dlInfo->m_requestType = reqType;
417  dlInfo->m_reload = reload;
418  dlInfo->m_syncMode = true;
419  dlInfo->m_authCallback = authCallback;
420  dlInfo->m_authArg = authArg;
421  dlInfo->m_headers = headers;
422 
423  return downloadNow(dlInfo, true);
424 }
425 
429 void MythDownloadManager::preCache(const QString &url)
430 {
431  LOG(VB_FILE, LOG_DEBUG, LOC + QString("preCache('%1')").arg(url));
432  queueItem(url, NULL, QString(), NULL, NULL);
433 }
434 
441 void MythDownloadManager::queueDownload(const QString &url,
442  const QString &dest,
443  QObject *caller,
444  const bool reload)
445 {
446  LOG(VB_FILE, LOG_DEBUG, LOC + QString("queueDownload('%1', '%2', %3)")
447  .arg(url).arg(dest).arg((long long)caller));
448 
449  queueItem(url, NULL, dest, NULL, caller, kRequestGet, reload);
450 }
451 
457 void MythDownloadManager::queueDownload(QNetworkRequest *req,
458  QByteArray *data,
459  QObject *caller)
460 {
461  LOG(VB_FILE, LOG_DEBUG, LOC + QString("queueDownload('%1', '%2', %3)")
462  .arg(req->url().toString()).arg((long long)data)
463  .arg((long long)caller));
464 
465  queueItem(req->url().toString(), req, QString(), data, caller,
466  kRequestGet,
467  (QNetworkRequest::AlwaysNetwork == req->attribute(
468  QNetworkRequest::CacheLoadControlAttribute,
469  QNetworkRequest::PreferNetwork).toInt()));
470 }
471 
478 bool MythDownloadManager::download(const QString &url, const QString &dest,
479  const bool reload)
480 {
481  return processItem(url, NULL, dest, NULL, kRequestGet, reload);
482 }
483 
490 bool MythDownloadManager::download(const QString &url, QByteArray *data,
491  const bool reload)
492 {
493  return processItem(url, NULL, QString(), data, kRequestGet, reload);
494 }
495 
502 QNetworkReply *MythDownloadManager::download(const QString &url,
503  const bool reload)
504 {
505  MythDownloadInfo *dlInfo = new MythDownloadInfo;
506  QNetworkReply *reply = NULL;
507 
508  dlInfo->m_url = url;
509  dlInfo->m_reload = reload;
510  dlInfo->m_syncMode = true;
511  dlInfo->m_processReply = false;
512 
513  if (downloadNow(dlInfo, false))
514  {
515  if (dlInfo->m_reply)
516  {
517  reply = dlInfo->m_reply;
518  // prevent dlInfo dtor from deleting the reply
519  dlInfo->m_reply = NULL;
520 
521  delete dlInfo;
522 
523  return reply;
524  }
525 
526  delete dlInfo;
527  }
528 
529  return NULL;
530 }
531 
537 bool MythDownloadManager::download(QNetworkRequest *req, QByteArray *data)
538 {
539  LOG(VB_FILE, LOG_DEBUG, LOC + QString("download('%1', '%2')")
540  .arg(req->url().toString()).arg((long long)data));
541  return processItem(req->url().toString(), req, QString(), data,
542  kRequestGet,
543  (QNetworkRequest::AlwaysNetwork == req->attribute(
544  QNetworkRequest::CacheLoadControlAttribute,
545  QNetworkRequest::PreferNetwork).toInt()));
546 }
547 
557 bool MythDownloadManager::downloadAuth(const QString &url, const QString &dest,
558  const bool reload, AuthCallback authCallback, void *authArg,
559  const QHash<QByteArray, QByteArray> *headers)
560 {
561  return processItem(url, NULL, dest, NULL, kRequestGet, reload, authCallback,
562  authArg, headers);
563 }
564 
565 
571 void MythDownloadManager::queuePost(const QString &url,
572  QByteArray *data,
573  QObject *caller)
574 {
575  LOG(VB_FILE, LOG_DEBUG, LOC + QString("queuePost('%1', '%2')")
576  .arg(url).arg((long long)data));
577 
578  if (!data)
579  {
580  LOG(VB_GENERAL, LOG_ERR, LOC + "queuePost(), data is NULL!");
581  return;
582  }
583 
584  queueItem(url, NULL, QString(), data, caller, kRequestPost);
585 }
586 
592 void MythDownloadManager::queuePost(QNetworkRequest *req,
593  QByteArray *data,
594  QObject *caller)
595 {
596  LOG(VB_FILE, LOG_DEBUG, LOC + QString("queuePost('%1', '%2')")
597  .arg(req->url().toString()).arg((long long)data));
598 
599  if (!data)
600  {
601  LOG(VB_GENERAL, LOG_ERR, LOC + "queuePost(), data is NULL!");
602  return;
603  }
604 
605  queueItem(req->url().toString(), req, QString(), data, caller,
606  kRequestPost,
607  (QNetworkRequest::AlwaysNetwork == req->attribute(
608  QNetworkRequest::CacheLoadControlAttribute,
609  QNetworkRequest::PreferNetwork).toInt()));
610 
611 }
612 
618 bool MythDownloadManager::post(const QString &url, QByteArray *data)
619 {
620  LOG(VB_FILE, LOG_DEBUG, LOC + QString("post('%1', '%2')")
621  .arg(url).arg((long long)data));
622 
623  if (!data)
624  {
625  LOG(VB_GENERAL, LOG_ERR, LOC + "post(), data is NULL!");
626  return false;
627  }
628 
629  return processItem(url, NULL, QString(), data, kRequestPost);
630 }
631 
637 bool MythDownloadManager::post(QNetworkRequest *req, QByteArray *data)
638 {
639  LOG(VB_FILE, LOG_DEBUG, LOC + QString("post('%1', '%2')")
640  .arg(req->url().toString()).arg((long long)data));
641 
642  if (!data)
643  {
644  LOG(VB_GENERAL, LOG_ERR, LOC + "post(), data is NULL!");
645  return false;
646  }
647 
648  return processItem(req->url().toString(), req, QString(), data,
649  kRequestPost,
650  (QNetworkRequest::AlwaysNetwork == req->attribute(
651  QNetworkRequest::CacheLoadControlAttribute,
652  QNetworkRequest::PreferNetwork).toInt()));
653 
654 }
655 
664 bool MythDownloadManager::postAuth(const QString &url, QByteArray *data,
665  AuthCallback authCallback, void *authArg,
666  const QHash<QByteArray, QByteArray> *headers)
667 {
668  LOG(VB_FILE, LOG_DEBUG, LOC + QString("postAuth('%1', '%2')")
669  .arg(url).arg((long long)data));
670 
671  if (!data)
672  {
673  LOG(VB_GENERAL, LOG_ERR, LOC + "postAuth(), data is NULL!");
674  return false;
675  }
676 
677  return processItem(url, NULL, NULL, data, kRequestPost, false, authCallback,
678  authArg, headers);
679 }
680 
685 {
686  RemoteFileDownloadThread *dlThread =
687  new RemoteFileDownloadThread(this, dlInfo);
688  MThreadPool::globalInstance()->start(dlThread, "RemoteFileDownload");
689 }
690 
695 {
696  if (!dlInfo)
697  return;
698 
699  static const char dateFormat[] = "ddd, dd MMM yyyy hh:mm:ss 'GMT'";
700  QUrl qurl(dlInfo->m_url);
701  QNetworkRequest request;
702 
703  if (dlInfo->m_request)
704  {
705  request = *dlInfo->m_request;
706  delete dlInfo->m_request;
707  dlInfo->m_request = NULL;
708  }
709  else
710  request.setUrl(qurl);
711 
712  if (dlInfo->m_reload)
713  {
714  request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
715  QNetworkRequest::AlwaysNetwork);
716  }
717  else
718  {
719  // Prefer the in-cache item if one exists and it is less than 5 minutes
720  // old and it will not expire in the next 10 seconds
721  QDateTime now = MythDate::current();
722 
723  // Handle redirects, we want the metadata of the file headers
724  QString redirectLoc;
725  int limit = 0;
726  while (!(redirectLoc = getHeader(qurl, "Location")).isNull())
727  {
728  if (limit == CACHE_REDIRECTION_LIMIT)
729  {
730  LOG(VB_GENERAL, LOG_WARNING, QString("Cache Redirection limit "
731  "reached for %1")
732  .arg(qurl.toString()));
733  return;
734  }
735  qurl.setUrl(redirectLoc);
736  limit++;
737  }
738 
739  LOG(VB_NETWORK, LOG_DEBUG, QString("Checking cache for %1")
740  .arg(qurl.toString()));
741 
742  m_infoLock->lock();
743  QNetworkCacheMetaData urlData = m_manager->cache()->metaData(qurl);
744  m_infoLock->unlock();
745  if ((urlData.isValid()) &&
746  ((!urlData.expirationDate().isValid()) ||
747  (QDateTime(urlData.expirationDate().toUTC()).secsTo(now) < 10)))
748  {
749  QString dateString = getHeader(urlData, "Date");
750 
751  if (!dateString.isNull())
752  {
753  QDateTime loadDate =
754  MythDate::fromString(dateString, dateFormat);
755  loadDate.setTimeSpec(Qt::UTC);
756  if (loadDate.secsTo(now) <= 720)
757  {
758  dlInfo->m_preferCache = true;
759  LOG(VB_NETWORK, LOG_DEBUG, QString("Preferring cache for %1")
760  .arg(qurl.toString()));
761  }
762  }
763  }
764  }
765 
766  if (dlInfo->m_preferCache)
767  request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
768  QNetworkRequest::PreferCache);
769 
770  if (!request.hasRawHeader("User-Agent"))
771  {
772  request.setRawHeader("User-Agent",
773  "MythTV v" MYTH_BINARY_VERSION
774  " MythDownloadManager");
775  }
776 
777  if (dlInfo->m_headers)
778  {
779  QHash<QByteArray, QByteArray>::const_iterator it =
780  dlInfo->m_headers->constBegin();
781  for ( ; it != dlInfo->m_headers->constEnd(); ++it )
782  {
783  if (!it.key().isEmpty() && !it.value().isEmpty())
784  {
785  request.setRawHeader(it.key(), it.value());
786  }
787  }
788  }
789 
790  switch (dlInfo->m_requestType)
791  {
792  case kRequestPost :
793  dlInfo->m_reply = m_manager->post(request, *dlInfo->m_data);
794  break;
795  case kRequestHead :
796  dlInfo->m_reply = m_manager->head(request);
797  break;
798  case kRequestGet :
799  default:
800  dlInfo->m_reply = m_manager->get(request);
801  break;
802  }
803 
804  m_downloadReplies[dlInfo->m_reply] = dlInfo;
805 
806  if (dlInfo->m_authCallback)
807  {
808  connect(m_manager, SIGNAL(authenticationRequired(QNetworkReply *,
809  QAuthenticator *)),
810  this, SLOT(authCallback(QNetworkReply *, QAuthenticator *)));
811  }
812 
813  connect(dlInfo->m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
814  SLOT(downloadError(QNetworkReply::NetworkError)));
815  connect(dlInfo->m_reply, SIGNAL(downloadProgress(qint64, qint64)),
816  this, SLOT(downloadProgress(qint64, qint64)));
817 }
818 
823 void MythDownloadManager::authCallback(QNetworkReply *reply,
824  QAuthenticator *authenticator)
825 {
826  if (!reply)
827  return;
828 
829  MythDownloadInfo *dlInfo = m_downloadReplies[reply];
830 
831  if (!dlInfo)
832  return;
833 
834  if (dlInfo->m_authCallback)
835  {
836  LOG(VB_FILE, LOG_DEBUG, "Calling auth callback");
837  dlInfo->m_authCallback(reply, authenticator, dlInfo->m_authArg);
838  }
839 }
840 
848 {
849  if (!dlInfo)
850  return false;
851 
852  dlInfo->m_syncMode = true;
853 
854  m_infoLock->lock();
855  m_downloadQueue.push_back(dlInfo);
856  m_infoLock->unlock();
857  m_queueWaitCond.wakeAll();
858 
859  // timeout myth:// RemoteFile transfers 20 seconds from now
860  // timeout non-myth:// QNetworkAccessManager transfers 60 seconds after
861  // their last progress update
862  QDateTime startedAt = MythDate::current();
863  m_infoLock->lock();
864  while ((!dlInfo->IsDone()) &&
865  (dlInfo->m_errorCode == QNetworkReply::NoError) &&
866  (((!dlInfo->m_url.startsWith("myth://")) &&
867  (dlInfo->m_lastStat.secsTo(MythDate::current()) < 60)) ||
868  ((dlInfo->m_url.startsWith("myth://")) &&
869  (startedAt.secsTo(MythDate::current()) < 20))))
870  {
871  m_infoLock->unlock();
872  m_queueWaitLock.lock();
873  m_queueWaitCond.wait(&m_queueWaitLock, 200);
874  m_queueWaitLock.unlock();
875  m_infoLock->lock();
876  }
877  bool done = dlInfo->IsDone();
878  bool success =
879  done && (dlInfo->m_errorCode == QNetworkReply::NoError);
880 
881  if (!done)
882  {
883  dlInfo->m_data = NULL; // Prevent downloadFinished() from updating
884  dlInfo->m_syncMode = false; // Let downloadFinished() cleanup for us
885  if ((dlInfo->m_reply) &&
886  (dlInfo->m_errorCode == QNetworkReply::NoError))
887  {
888  LOG(VB_FILE, LOG_DEBUG,
889  LOC + QString("Aborting download - lack of data transfer"));
890  dlInfo->m_reply->abort();
891  }
892  }
893  else if (deleteInfo)
894  delete dlInfo;
895 
896  m_infoLock->unlock();
897 
898  return success;
899 }
900 
904 void MythDownloadManager::cancelDownload(const QString &url, bool block)
905 {
906  cancelDownload(QStringList(url), block);
907 }
908 
912 void MythDownloadManager::cancelDownload(const QStringList &urls, bool block)
913 {
914  MythDownloadInfo *dlInfo;
915 
916  m_infoLock->lock();
917  foreach (QString url, urls)
918  {
919  QMutableListIterator<MythDownloadInfo*> lit(m_downloadQueue);
920  while (lit.hasNext())
921  {
922  lit.next();
923  dlInfo = lit.value();
924  if (dlInfo->m_url == url)
925  {
926  if (!m_cancellationQueue.contains(dlInfo))
927  m_cancellationQueue.append(dlInfo);
928  lit.remove();
929  }
930  }
931 
932  if (m_downloadInfos.contains(url))
933  {
934  dlInfo = m_downloadInfos[url];
935 
936  if (!m_cancellationQueue.contains(dlInfo))
937  m_cancellationQueue.append(dlInfo);
938 
939  if (dlInfo->m_reply)
940  m_downloadReplies.remove(dlInfo->m_reply);
941 
942  m_downloadInfos.remove(url);
943  }
944  }
945  m_infoLock->unlock();
946 
947  if (QThread::currentThread() == this->thread())
948  {
950  return;
951  }
952 
953  // wake-up running thread
954  m_queueWaitCond.wakeAll();
955 
956  if (!block)
957  return;
958 
959  while (!m_cancellationQueue.isEmpty())
960  {
961  usleep(50000); // re-test in another 50ms
962  }
963 }
964 
966 {
967  QMutexLocker locker(m_infoLock);
968  MythDownloadInfo *dlInfo;
969 
970  QMutableListIterator<MythDownloadInfo*> lit(m_cancellationQueue);
971  while (lit.hasNext())
972  {
973  lit.next();
974  dlInfo = lit.value();
975  dlInfo->m_lock.lock();
976 
977  if (dlInfo->m_reply)
978  {
979  LOG(VB_FILE, LOG_DEBUG,
980  LOC + QString("Aborting download - user request"));
981  dlInfo->m_reply->abort();
982  }
983  lit.remove();
984  if (dlInfo->m_done)
985  {
986  dlInfo->m_lock.unlock();
987  continue;
988  }
989  dlInfo->m_errorCode = QNetworkReply::OperationCanceledError;
990  dlInfo->m_done = true;
991  dlInfo->m_lock.unlock();
992  }
993 }
994 
1000 {
1001  QMutexLocker locker(m_infoLock);
1002  MythDownloadInfo *dlInfo;
1003 
1004  QList <MythDownloadInfo*>::iterator lit = m_downloadQueue.begin();
1005  for (; lit != m_downloadQueue.end(); ++lit)
1006  {
1007  dlInfo = *lit;
1008  if (dlInfo->m_caller == caller)
1009  {
1010  dlInfo->m_caller = NULL;
1011  dlInfo->m_outFile = QString();
1012  dlInfo->m_data = NULL;
1013  }
1014  }
1015 
1016  QMap <QString, MythDownloadInfo*>::iterator mit = m_downloadInfos.begin();
1017  for (; mit != m_downloadInfos.end(); ++mit)
1018  {
1019  dlInfo = mit.value();
1020  if (dlInfo->m_caller == caller)
1021  {
1022  dlInfo->m_caller = NULL;
1023  dlInfo->m_outFile = QString();
1024  dlInfo->m_data = NULL;
1025  }
1026  }
1027 }
1028 
1032 void MythDownloadManager::downloadError(QNetworkReply::NetworkError errorCode)
1033 {
1034  QNetworkReply *reply = (QNetworkReply*)sender();
1035 
1036  LOG(VB_FILE, LOG_DEBUG, LOC + QString("downloadError %1 ")
1037  .arg(errorCode) + reply->errorString() );
1038 
1039  QMutexLocker locker(m_infoLock);
1040  if (!m_downloadReplies.contains(reply))
1041  {
1042  reply->deleteLater();
1043  return;
1044  }
1045 
1046  MythDownloadInfo *dlInfo = m_downloadReplies[reply];
1047 
1048  if (!dlInfo)
1049  return;
1050 
1051  dlInfo->m_errorCode = errorCode;
1052 }
1053 
1059 QUrl MythDownloadManager::redirectUrl(const QUrl& possibleRedirectUrl,
1060  const QUrl& oldRedirectUrl) const
1061 {
1062  LOG(VB_FILE, LOG_DEBUG, LOC + QString("redirectUrl()"));
1063  QUrl redirectUrl;
1064 
1065  if(!possibleRedirectUrl.isEmpty() && possibleRedirectUrl != oldRedirectUrl)
1066  redirectUrl = possibleRedirectUrl;
1067 
1068  return redirectUrl;
1069 }
1070 
1074 void MythDownloadManager::downloadFinished(QNetworkReply* reply)
1075 {
1076  LOG(VB_FILE, LOG_DEBUG, LOC + QString("downloadFinished(%1)")
1077  .arg((long long)reply));
1078 
1079  QMutexLocker locker(m_infoLock);
1080  if (!m_downloadReplies.contains(reply))
1081  {
1082  reply->deleteLater();
1083  return;
1084  }
1085 
1086  MythDownloadInfo *dlInfo = m_downloadReplies[reply];
1087 
1088  if (!dlInfo || !dlInfo->m_reply)
1089  return;
1090 
1091  downloadFinished(dlInfo);
1092 }
1093 
1098 {
1099  if (!dlInfo)
1100  return;
1101 
1102  int statusCode = -1;
1103  static const char dateFormat[] = "ddd, dd MMM yyyy hh:mm:ss 'GMT'";
1104  QNetworkReply *reply = dlInfo->m_reply;
1105 
1106  if (reply)
1107  {
1108  QUrl possibleRedirectUrl =
1109  reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
1110 
1111  if (!possibleRedirectUrl.isEmpty() &&
1112  possibleRedirectUrl.isValid() &&
1113  possibleRedirectUrl.isRelative()) // Turn relative Url to absolute
1114  possibleRedirectUrl = reply->url().resolved(possibleRedirectUrl);
1115 
1116  dlInfo->m_redirectedTo =
1117  redirectUrl(possibleRedirectUrl, dlInfo->m_redirectedTo);
1118 
1119  QVariant status =
1120  reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
1121  if (status.isValid())
1122  statusCode = status.toInt();
1123  }
1124 
1125  if(reply && !dlInfo->m_redirectedTo.isEmpty() &&
1126  ((dlInfo->m_requestType != kRequestPost) ||
1127  (statusCode == 301 || statusCode == 302 ||
1128  statusCode == 303)))
1129  {
1130  LOG(VB_FILE, LOG_DEBUG, LOC +
1131  QString("downloadFinished(%1): Redirect: %2 -> %3")
1132  .arg((long long)dlInfo)
1133  .arg(reply->url().toString())
1134  .arg(dlInfo->m_redirectedTo.toString()));
1135 
1136  if (dlInfo->m_data)
1137  dlInfo->m_data->clear();
1138 
1139  dlInfo->m_bytesReceived = 0;
1140  dlInfo->m_bytesTotal = 0;
1141 
1142  QNetworkRequest request(dlInfo->m_redirectedTo);
1143 
1144  if (dlInfo->m_reload)
1145  {
1146  request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
1147  QNetworkRequest::AlwaysNetwork);
1148  }
1149  else if (dlInfo->m_preferCache)
1150  {
1151  request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
1152  QNetworkRequest::PreferCache);
1153  }
1154 
1155  request.setRawHeader("User-Agent",
1156  "MythDownloadManager v" MYTH_BINARY_VERSION);
1157 
1158  switch (dlInfo->m_requestType)
1159  {
1160  case kRequestHead :
1161  dlInfo->m_reply = m_manager->head(request);
1162  break;
1163  case kRequestGet :
1164  default:
1165  dlInfo->m_reply = m_manager->get(request);
1166  break;
1167  }
1168 
1169  m_downloadReplies[dlInfo->m_reply] = dlInfo;
1170 
1171  connect(dlInfo->m_reply, SIGNAL(error(QNetworkReply::NetworkError)),
1172  this, SLOT(downloadError(QNetworkReply::NetworkError)));
1173  connect(dlInfo->m_reply, SIGNAL(downloadProgress(qint64, qint64)),
1174  this, SLOT(downloadProgress(qint64, qint64)));
1175 
1176  m_downloadReplies.remove(reply);
1177  reply->deleteLater();
1178  }
1179  else
1180  {
1181  LOG(VB_FILE, LOG_DEBUG, QString("downloadFinished(%1): COMPLETE: %2")
1182  .arg((long long)dlInfo).arg(dlInfo->m_url));
1183 
1184  // HACK Insert a Date header into the cached metadata if one doesn't
1185  // already exist
1186  QUrl fileUrl = dlInfo->m_url;
1187  QString redirectLoc;
1188  int limit = 0;
1189  while (!(redirectLoc = getHeader(fileUrl, "Location")).isNull())
1190  {
1191  if (limit == CACHE_REDIRECTION_LIMIT)
1192  {
1193  LOG(VB_GENERAL, LOG_WARNING, QString("Cache Redirection limit "
1194  "reached for %1")
1195  .arg(fileUrl.toString()));
1196  return;
1197  }
1198  fileUrl.setUrl(redirectLoc);
1199  limit++;
1200  }
1201 
1202  m_infoLock->lock();
1203  QNetworkCacheMetaData urlData = m_manager->cache()->metaData(fileUrl);
1204  m_infoLock->unlock();
1205  if (getHeader(urlData, "Date").isNull())
1206  {
1207  QNetworkCacheMetaData::RawHeaderList headers = urlData.rawHeaders();
1208  QNetworkCacheMetaData::RawHeader newheader;
1209  QDateTime now = MythDate::current();
1210  newheader = QNetworkCacheMetaData::RawHeader("Date",
1211  now.toString(dateFormat).toLatin1());
1212  headers.append(newheader);
1213  urlData.setRawHeaders(headers);
1214  m_infoLock->lock();
1215  m_manager->cache()->updateMetaData(urlData);
1216  m_infoLock->unlock();
1217  }
1218  // End HACK
1219 
1220  dlInfo->m_redirectedTo.clear();
1221 
1222  int dataSize = -1;
1223 
1224  // If we downloaded via the QNetworkAccessManager
1225  // AND the caller isn't handling the reply directly
1226  if (reply && dlInfo->m_processReply)
1227  {
1228  bool append = (!dlInfo->m_syncMode && dlInfo->m_caller);
1229  QByteArray data = reply->readAll();
1230  dataSize = data.size();
1231 
1232  if (append)
1233  dlInfo->m_bytesReceived += dataSize;
1234  else
1235  dlInfo->m_bytesReceived = dataSize;
1236 
1237  dlInfo->m_bytesTotal = dlInfo->m_bytesReceived;
1238 
1239  if (dlInfo->m_data)
1240  {
1241  if (append)
1242  dlInfo->m_data->append(data);
1243  else
1244  *dlInfo->m_data = data;
1245  }
1246  else if (!dlInfo->m_outFile.isEmpty())
1247  {
1248  saveFile(dlInfo->m_outFile, data, append);
1249  }
1250  }
1251  else if (!reply) // If we downloaded via RemoteFile
1252  {
1253  if (dlInfo->m_data)
1254  {
1255  (*dlInfo->m_data) = dlInfo->m_privData;
1256  }
1257  else if (!dlInfo->m_outFile.isEmpty())
1258  {
1259  saveFile(dlInfo->m_outFile, dlInfo->m_privData);
1260  }
1261  dlInfo->m_bytesReceived += dataSize;
1262  dlInfo->m_bytesTotal = dlInfo->m_bytesReceived;
1263  }
1264  // else we downloaded via QNetworkAccessManager
1265  // AND the caller is handling the reply
1266 
1267  m_infoLock->lock();
1268  if (!m_downloadInfos.remove(dlInfo->m_url))
1269  LOG(VB_GENERAL, LOG_ERR, LOC +
1270  QString("ERROR download finished but failed to remove url: %1")
1271  .arg(dlInfo->m_url));
1272 
1273  if (reply)
1274  m_downloadReplies.remove(reply);
1275  m_infoLock->unlock();
1276 
1277  dlInfo->SetDone(true);
1278 
1279  if (!dlInfo->m_syncMode)
1280  {
1281  if (dlInfo->m_caller)
1282  {
1283  LOG(VB_FILE, LOG_DEBUG, QString("downloadFinished(%1): "
1284  "COMPLETE: %2, sending event to caller")
1285  .arg((long long)dlInfo).arg(dlInfo->m_url));
1286 
1287  QStringList args;
1288  args << dlInfo->m_url;
1289  args << dlInfo->m_outFile;
1290  args << QString::number(dlInfo->m_bytesTotal);
1291  // placeholder for error string
1292  args << (reply ? reply->errorString() : QString());
1293  args << QString::number((int)(reply ? reply->error() :
1294  dlInfo->m_errorCode));
1295 
1296  QCoreApplication::postEvent(dlInfo->m_caller,
1297  new MythEvent("DOWNLOAD_FILE FINISHED", args));
1298  }
1299 
1300  delete dlInfo;
1301  }
1302 
1303  m_queueWaitCond.wakeAll();
1304  }
1305 }
1306 
1312 void MythDownloadManager::downloadProgress(qint64 bytesReceived,
1313  qint64 bytesTotal)
1314 {
1315  QNetworkReply *reply = (QNetworkReply*)sender();
1316 
1317  LOG(VB_FILE, LOG_DEBUG, LOC +
1318  QString("downloadProgress(%1, %2) (for reply %3)")
1319  .arg(bytesReceived).arg(bytesTotal).arg((long long)reply));
1320 
1321  QMutexLocker locker(m_infoLock);
1322  if (!m_downloadReplies.contains(reply))
1323  return;
1324 
1325  MythDownloadInfo *dlInfo = m_downloadReplies[reply];
1326 
1327  if (!dlInfo)
1328  return;
1329 
1330  dlInfo->m_lastStat = MythDate::current();
1331 
1332  LOG(VB_FILE, LOG_DEBUG, LOC +
1333  QString("downloadProgress: %1 to %2 is at %3 of %4 bytes downloaded")
1334  .arg(dlInfo->m_url).arg(dlInfo->m_outFile)
1335  .arg(bytesReceived).arg(bytesTotal));
1336 
1337  if (!dlInfo->m_syncMode && dlInfo->m_caller)
1338  {
1339  LOG(VB_FILE, LOG_DEBUG, QString("downloadProgress(%1): "
1340  "sending event to caller")
1341  .arg(reply->url().toString()));
1342 
1343  bool appendToFile = (dlInfo->m_bytesReceived != 0);
1344  QByteArray data = reply->readAll();
1345  if (!dlInfo->m_outFile.isEmpty())
1346  saveFile(dlInfo->m_outFile, data, appendToFile);
1347 
1348  if (dlInfo->m_data)
1349  dlInfo->m_data->append(data);
1350 
1351  dlInfo->m_bytesReceived = bytesReceived;
1352  dlInfo->m_bytesTotal = bytesTotal;
1353 
1354  QStringList args;
1355  args << dlInfo->m_url;
1356  args << dlInfo->m_outFile;
1357  args << QString::number(bytesReceived);
1358  args << QString::number(bytesTotal);
1359 
1360  QCoreApplication::postEvent(dlInfo->m_caller,
1361  new MythEvent("DOWNLOAD_FILE UPDATE", args));
1362  }
1363 }
1364 
1372 bool MythDownloadManager::saveFile(const QString &outFile,
1373  const QByteArray &data,
1374  const bool append)
1375 {
1376  if (outFile.isEmpty() || !data.size())
1377  return false;
1378 
1379  QFile file(outFile);
1380  QFileInfo fileInfo(outFile);
1381  QDir qdir(fileInfo.absolutePath());
1382 
1383  if (!qdir.exists() && !qdir.mkpath(fileInfo.absolutePath()))
1384  {
1385  LOG(VB_GENERAL, LOG_ERR, QString("Failed to create: '%1'")
1386  .arg(fileInfo.absolutePath()));
1387  return false;
1388  }
1389 
1390  QIODevice::OpenMode mode = QIODevice::Unbuffered|QIODevice::WriteOnly;
1391  if (append)
1392  mode |= QIODevice::Append;
1393 
1394  if (!file.open(mode))
1395  {
1396  LOG(VB_GENERAL, LOG_ERR, QString("Failed to open: '%1'") .arg(outFile));
1397  return false;
1398  }
1399 
1400  off_t offset = 0;
1401  size_t remaining = data.size();
1402  uint failure_cnt = 0;
1403  while ((remaining > 0) && (failure_cnt < 5))
1404  {
1405  ssize_t written = file.write(data.data() + offset, remaining);
1406  if (written < 0)
1407  {
1408  failure_cnt++;
1409  usleep(50000);
1410  continue;
1411  }
1412 
1413  failure_cnt = 0;
1414  offset += written;
1415  remaining -= written;
1416  }
1417 
1418  if (remaining > 0)
1419  return false;
1420 
1421  return true;
1422 }
1423 
1428 QDateTime MythDownloadManager::GetLastModified(const QString &url)
1429 {
1430  // If the header has not expired and
1431  // the last modification date is less than 1 hours old or if
1432  // the cache object is less than 20 minutes old,
1433  // then use the cached header otherwise redownload the header
1434 
1435  static const char dateFormat[] = "ddd, dd MMM yyyy hh:mm:ss 'GMT'";
1436  LOG(VB_FILE, LOG_DEBUG, LOC + QString("GetLastModified('%1')").arg(url));
1437  QDateTime result;
1438 
1439  QDateTime now = MythDate::current();
1440 
1441  QUrl cacheUrl = QUrl(url);
1442 
1443  // Deal with redirects, we want the cached data for the final url
1444  QString redirectLoc;
1445  int limit = 0;
1446  while (!(redirectLoc = getHeader(cacheUrl, "Location")).isNull())
1447  {
1448  if (limit == CACHE_REDIRECTION_LIMIT)
1449  {
1450  LOG(VB_GENERAL, LOG_WARNING, QString("Cache Redirection limit "
1451  "reached for %1")
1452  .arg(cacheUrl.toString()));
1453  return result;
1454  }
1455  cacheUrl.setUrl(redirectLoc);
1456  limit++;
1457  }
1458 
1459  m_infoLock->lock();
1460  QNetworkCacheMetaData urlData = m_manager->cache()->metaData(cacheUrl);
1461  m_infoLock->unlock();
1462 
1463  if (urlData.isValid() &&
1464  ((!urlData.expirationDate().isValid()) ||
1465  (urlData.expirationDate().secsTo(now) < 0)))
1466  {
1467  if (QDateTime(urlData.lastModified().toUTC()).secsTo(now) <= 3600) // 1 Hour
1468  {
1469  result = urlData.lastModified().toUTC();
1470  }
1471  else
1472  {
1473  QString date = getHeader(urlData, "Date");
1474  if (!date.isNull())
1475  {
1476  QDateTime loadDate =
1477  MythDate::fromString(date, dateFormat);
1478  loadDate.setTimeSpec(Qt::UTC);
1479  if (loadDate.secsTo(now) <= 1200) // 20 Minutes
1480  {
1481  result = urlData.lastModified().toUTC();
1482  }
1483  }
1484  }
1485  }
1486 
1487  if (!result.isValid())
1488  {
1489  MythDownloadInfo *dlInfo = new MythDownloadInfo;
1490  dlInfo->m_url = url;
1491  dlInfo->m_syncMode = true;
1492  // Head request, we only want to inspect the headers
1493  dlInfo->m_requestType = kRequestHead;
1494 
1495  if (downloadNow(dlInfo, false))
1496  {
1497  if (dlInfo->m_reply)
1498  {
1499  QVariant lastMod =
1500  dlInfo->m_reply->header(
1501  QNetworkRequest::LastModifiedHeader);
1502  if (lastMod.isValid())
1503  result = lastMod.toDateTime().toUTC();
1504  }
1505 
1506  // downloadNow() will set a flag to trigger downloadFinished()
1507  // to delete the dlInfo if the download times out
1508  delete dlInfo;
1509  }
1510  }
1511 
1512  LOG(VB_FILE, LOG_DEBUG, LOC + QString("GetLastModified('%1'): Result %2")
1513  .arg(url).arg(result.toString()));
1514 
1515  return result;
1516 }
1517 
1518 
1523 {
1524  QMutexLocker locker(&m_cookieLock);
1525 
1526  MythCookieJar *jar = new MythCookieJar;
1527  jar->load(filename);
1528  m_manager->setCookieJar(jar);
1529 }
1530 
1535 {
1536  QMutexLocker locker(&m_cookieLock);
1537 
1538  if (!m_manager->cookieJar())
1539  return;
1540 
1541  MythCookieJar *jar = static_cast<MythCookieJar *>(m_manager->cookieJar());
1542  jar->save(filename);
1543 }
1544 
1545 void MythDownloadManager::setCookieJar(QNetworkCookieJar *cookieJar)
1546 {
1547  QMutexLocker locker(&m_cookieLock);
1548  m_manager->setCookieJar(cookieJar);
1549 }
1550 
1554 QNetworkCookieJar *MythDownloadManager::copyCookieJar(void)
1555 {
1556  QMutexLocker locker(&m_cookieLock);
1557 
1558  if (!m_manager->cookieJar())
1559  return NULL;
1560 
1561  MythCookieJar *inJar = static_cast<MythCookieJar *>(m_manager->cookieJar());
1562  MythCookieJar *outJar = new MythCookieJar(*inJar);
1563 
1564  return static_cast<QNetworkCookieJar *>(outJar);
1565 }
1566 
1570 void MythDownloadManager::refreshCookieJar(QNetworkCookieJar *jar)
1571 {
1572  QMutexLocker locker(&m_cookieLock);
1573  if (m_inCookieJar)
1574  delete m_inCookieJar;
1575 
1576  MythCookieJar *inJar = static_cast<MythCookieJar *>(jar);
1577  MythCookieJar *outJar = new MythCookieJar(*inJar);
1578  m_inCookieJar = static_cast<QNetworkCookieJar *>(outJar);
1579 
1580  QMutexLocker locker2(&m_queueWaitLock);
1581  m_queueWaitCond.wakeAll();
1582 }
1583 
1587 {
1588  QMutexLocker locker(&m_cookieLock);
1589 
1590  MythCookieJar *inJar = static_cast<MythCookieJar *>(m_inCookieJar);
1591  MythCookieJar *outJar = new MythCookieJar(*inJar);
1592  m_manager->setCookieJar(static_cast<QNetworkCookieJar *>(outJar));
1593 
1594  delete m_inCookieJar;
1595  m_inCookieJar = NULL;
1596 }
1597 
1598 QString MythDownloadManager::getHeader(const QUrl& url, const QString& header)
1599 {
1600  if (!m_manager || !m_manager->cache())
1601  return QString::null;
1602 
1603  m_infoLock->lock();
1604  QNetworkCacheMetaData metadata = m_manager->cache()->metaData(url);
1605  m_infoLock->unlock();
1606 
1607  return getHeader(metadata, header);
1608 }
1609 
1615 QString MythDownloadManager::getHeader(const QNetworkCacheMetaData &cacheData,
1616  const QString& header)
1617 {
1618  QNetworkCacheMetaData::RawHeaderList headers = cacheData.rawHeaders();
1619  bool found = false;
1620  QNetworkCacheMetaData::RawHeaderList::iterator it = headers.begin();
1621  for (; !found && it != headers.end(); ++it)
1622  {
1623  if (QString((*it).first) == header)
1624  {
1625  found = true;
1626  return QString((*it).second);
1627  }
1628  }
1629 
1630  return QString::null;
1631 }
1632 
1633 
1638 {
1639  const QList<QNetworkCookie> cookieList = old.allCookies();
1640  setAllCookies(cookieList);
1641 }
1642 
1646 {
1647 }
1648 
1652 void MythCookieJar::load(const QString &filename)
1653 {
1654  LOG(VB_GENERAL, LOG_DEBUG, QString("MythCookieJar: loading cookies from: %1").arg(filename));
1655 
1656  QFile f(filename);
1657  if (!f.open(QIODevice::ReadOnly))
1658  {
1659  LOG(VB_GENERAL, LOG_WARNING, QString("MythCookieJar::load() failed to open file for reading: %1").arg(filename));
1660  return;
1661  }
1662 
1663  QList<QNetworkCookie> cookieList;
1664  QTextStream stream(&f);
1665  while (!stream.atEnd())
1666  {
1667  QString cookie = stream.readLine();
1668  cookieList << QNetworkCookie::parseCookies(cookie.toLocal8Bit());
1669  }
1670 
1671  setAllCookies(cookieList);
1672 }
1673 
1677 void MythCookieJar::save(const QString &filename)
1678 {
1679  LOG(VB_GENERAL, LOG_DEBUG, QString("MythCookieJar: saving cookies to: %1").arg(filename));
1680 
1681  QFile f(filename);
1682  if (!f.open(QIODevice::WriteOnly))
1683  {
1684  LOG(VB_GENERAL, LOG_ERR, QString("MythCookieJar::save() failed to open file for writing: %1").arg(filename));
1685  return;
1686  }
1687 
1688  QList<QNetworkCookie> cookieList = allCookies();
1689  QTextStream stream(&f);
1690 
1691  for (QList<QNetworkCookie>::iterator it = cookieList.begin();
1692  it != cookieList.end(); ++it)
1693  {
1694  stream << (*it).toRawForm() << endl;
1695  }
1696 }
1697 
1698 
1699 /* vim: set expandtab tabstop=4 shiftwidth=4: */
void RunEpilog(void)
Cleans up a thread&#39;s resources, call this if you reimplement run().
Definition: mthread.cpp:216
QNetworkCookieJar * copyCookieJar(void)
Copy from one cookie jar to another.
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 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.
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.
static void error(const char *str,...)
Definition: vbi.c:41
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
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.
__u8 data[42]
Definition: videodev2.h:1039
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:212
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
__u8 block
Definition: videodev2.h:1040
QUrl redirectUrl(const QUrl &possibleRedirectUrl, const QUrl &oldRedirectUrl) const
Checks whether we were redirected to the given URL.
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.
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.
void(* AuthCallback)(QNetworkReply *, QAuthenticator *, void *)
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&#39;t store the resulting data anywhere.