MythTV  0.28pre
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  request.setRawHeader("User-Agent",
771  "MythTV v" MYTH_BINARY_VERSION " MythDownloadManager");
772 
773  if (dlInfo->m_headers)
774  {
775  QHash<QByteArray, QByteArray>::const_iterator it =
776  dlInfo->m_headers->constBegin();
777  for ( ; it != dlInfo->m_headers->constEnd(); ++it )
778  {
779  if (!it.key().isEmpty() && !it.value().isEmpty())
780  {
781  request.setRawHeader(it.key(), it.value());
782  }
783  }
784  }
785 
786  switch (dlInfo->m_requestType)
787  {
788  case kRequestPost :
789  dlInfo->m_reply = m_manager->post(request, *dlInfo->m_data);
790  break;
791  case kRequestHead :
792  dlInfo->m_reply = m_manager->head(request);
793  break;
794  case kRequestGet :
795  default:
796  dlInfo->m_reply = m_manager->get(request);
797  break;
798  }
799 
800  m_downloadReplies[dlInfo->m_reply] = dlInfo;
801 
802  if (dlInfo->m_authCallback)
803  {
804  connect(m_manager, SIGNAL(authenticationRequired(QNetworkReply *,
805  QAuthenticator *)),
806  this, SLOT(authCallback(QNetworkReply *, QAuthenticator *)));
807  }
808 
809  connect(dlInfo->m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
810  SLOT(downloadError(QNetworkReply::NetworkError)));
811  connect(dlInfo->m_reply, SIGNAL(downloadProgress(qint64, qint64)),
812  this, SLOT(downloadProgress(qint64, qint64)));
813 }
814 
819 void MythDownloadManager::authCallback(QNetworkReply *reply,
820  QAuthenticator *authenticator)
821 {
822  if (!reply)
823  return;
824 
825  MythDownloadInfo *dlInfo = m_downloadReplies[reply];
826 
827  if (!dlInfo)
828  return;
829 
830  if (dlInfo->m_authCallback)
831  {
832  LOG(VB_FILE, LOG_DEBUG, "Calling auth callback");
833  dlInfo->m_authCallback(reply, authenticator, dlInfo->m_authArg);
834  }
835 }
836 
844 {
845  if (!dlInfo)
846  return false;
847 
848  dlInfo->m_syncMode = true;
849 
850  m_infoLock->lock();
851  m_downloadQueue.push_back(dlInfo);
852  m_infoLock->unlock();
853  m_queueWaitCond.wakeAll();
854 
855  // timeout myth:// RemoteFile transfers 20 seconds from now
856  // timeout non-myth:// QNetworkAccessManager transfers 60 seconds after
857  // their last progress update
858  QDateTime startedAt = MythDate::current();
859  m_infoLock->lock();
860  while ((!dlInfo->IsDone()) &&
861  (dlInfo->m_errorCode == QNetworkReply::NoError) &&
862  (((!dlInfo->m_url.startsWith("myth://")) &&
863  (dlInfo->m_lastStat.secsTo(MythDate::current()) < 60)) ||
864  ((dlInfo->m_url.startsWith("myth://")) &&
865  (startedAt.secsTo(MythDate::current()) < 20))))
866  {
867  m_infoLock->unlock();
868  m_queueWaitLock.lock();
869  m_queueWaitCond.wait(&m_queueWaitLock, 200);
870  m_queueWaitLock.unlock();
871  m_infoLock->lock();
872  }
873  bool done = dlInfo->IsDone();
874  bool success =
875  done && (dlInfo->m_errorCode == QNetworkReply::NoError);
876 
877  if (!done)
878  {
879  dlInfo->m_data = NULL; // Prevent downloadFinished() from updating
880  dlInfo->m_syncMode = false; // Let downloadFinished() cleanup for us
881  if ((dlInfo->m_reply) &&
882  (dlInfo->m_errorCode == QNetworkReply::NoError))
883  {
884  LOG(VB_FILE, LOG_DEBUG,
885  LOC + QString("Aborting download - lack of data transfer"));
886  dlInfo->m_reply->abort();
887  }
888  }
889  else if (deleteInfo)
890  delete dlInfo;
891 
892  m_infoLock->unlock();
893 
894  return success;
895 }
896 
900 void MythDownloadManager::cancelDownload(const QString &url, bool block)
901 {
902  cancelDownload(QStringList(url), block);
903 }
904 
908 void MythDownloadManager::cancelDownload(const QStringList &urls, bool block)
909 {
910  MythDownloadInfo *dlInfo;
911 
912  m_infoLock->lock();
913  foreach (QString url, urls)
914  {
915  QMutableListIterator<MythDownloadInfo*> lit(m_downloadQueue);
916  while (lit.hasNext())
917  {
918  lit.next();
919  dlInfo = lit.value();
920  if (dlInfo->m_url == url)
921  {
922  if (!m_cancellationQueue.contains(dlInfo))
923  m_cancellationQueue.append(dlInfo);
924  lit.remove();
925  }
926  }
927 
928  if (m_downloadInfos.contains(url))
929  {
930  dlInfo = m_downloadInfos[url];
931 
932  if (!m_cancellationQueue.contains(dlInfo))
933  m_cancellationQueue.append(dlInfo);
934 
935  if (dlInfo->m_reply)
936  m_downloadReplies.remove(dlInfo->m_reply);
937 
938  m_downloadInfos.remove(url);
939  }
940  }
941  m_infoLock->unlock();
942 
943  if (QThread::currentThread() == this->thread())
944  {
946  return;
947  }
948 
949  // wake-up running thread
950  m_queueWaitCond.wakeAll();
951 
952  if (!block)
953  return;
954 
955  while (!m_cancellationQueue.isEmpty())
956  {
957  usleep(50000); // re-test in another 50ms
958  }
959 }
960 
962 {
963  QMutexLocker locker(m_infoLock);
964  MythDownloadInfo *dlInfo;
965 
966  QMutableListIterator<MythDownloadInfo*> lit(m_cancellationQueue);
967  while (lit.hasNext())
968  {
969  lit.next();
970  dlInfo = lit.value();
971  dlInfo->m_lock.lock();
972 
973  if (dlInfo->m_reply)
974  {
975  LOG(VB_FILE, LOG_DEBUG,
976  LOC + QString("Aborting download - user request"));
977  dlInfo->m_reply->abort();
978  }
979  lit.remove();
980  if (dlInfo->m_done)
981  {
982  dlInfo->m_lock.unlock();
983  continue;
984  }
985  dlInfo->m_errorCode = QNetworkReply::OperationCanceledError;
986  dlInfo->m_done = true;
987  dlInfo->m_lock.unlock();
988  }
989 }
990 
996 {
997  QMutexLocker locker(m_infoLock);
998  MythDownloadInfo *dlInfo;
999 
1000  QList <MythDownloadInfo*>::iterator lit = m_downloadQueue.begin();
1001  for (; lit != m_downloadQueue.end(); ++lit)
1002  {
1003  dlInfo = *lit;
1004  if (dlInfo->m_caller == caller)
1005  {
1006  dlInfo->m_caller = NULL;
1007  dlInfo->m_outFile = QString();
1008  dlInfo->m_data = NULL;
1009  }
1010  }
1011 
1012  QMap <QString, MythDownloadInfo*>::iterator mit = m_downloadInfos.begin();
1013  for (; mit != m_downloadInfos.end(); ++mit)
1014  {
1015  dlInfo = mit.value();
1016  if (dlInfo->m_caller == caller)
1017  {
1018  dlInfo->m_caller = NULL;
1019  dlInfo->m_outFile = QString();
1020  dlInfo->m_data = NULL;
1021  }
1022  }
1023 }
1024 
1028 void MythDownloadManager::downloadError(QNetworkReply::NetworkError errorCode)
1029 {
1030  QNetworkReply *reply = (QNetworkReply*)sender();
1031 
1032  LOG(VB_FILE, LOG_DEBUG, LOC + QString("downloadError %1 ")
1033  .arg(errorCode) + reply->errorString() );
1034 
1035  QMutexLocker locker(m_infoLock);
1036  if (!m_downloadReplies.contains(reply))
1037  {
1038  reply->deleteLater();
1039  return;
1040  }
1041 
1042  MythDownloadInfo *dlInfo = m_downloadReplies[reply];
1043 
1044  if (!dlInfo)
1045  return;
1046 
1047  dlInfo->m_errorCode = errorCode;
1048 }
1049 
1055 QUrl MythDownloadManager::redirectUrl(const QUrl& possibleRedirectUrl,
1056  const QUrl& oldRedirectUrl) const
1057 {
1058  LOG(VB_FILE, LOG_DEBUG, LOC + QString("redirectUrl()"));
1059  QUrl redirectUrl;
1060 
1061  if(!possibleRedirectUrl.isEmpty() && possibleRedirectUrl != oldRedirectUrl)
1062  redirectUrl = possibleRedirectUrl;
1063 
1064  return redirectUrl;
1065 }
1066 
1070 void MythDownloadManager::downloadFinished(QNetworkReply* reply)
1071 {
1072  LOG(VB_FILE, LOG_DEBUG, LOC + QString("downloadFinished(%1)")
1073  .arg((long long)reply));
1074 
1075  QMutexLocker locker(m_infoLock);
1076  if (!m_downloadReplies.contains(reply))
1077  {
1078  reply->deleteLater();
1079  return;
1080  }
1081 
1082  MythDownloadInfo *dlInfo = m_downloadReplies[reply];
1083 
1084  if (!dlInfo || !dlInfo->m_reply)
1085  return;
1086 
1087  downloadFinished(dlInfo);
1088 }
1089 
1094 {
1095  if (!dlInfo)
1096  return;
1097 
1098  int statusCode = -1;
1099  static const char dateFormat[] = "ddd, dd MMM yyyy hh:mm:ss 'GMT'";
1100  QNetworkReply *reply = dlInfo->m_reply;
1101 
1102  if (reply)
1103  {
1104  QUrl possibleRedirectUrl =
1105  reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
1106 
1107  if (!possibleRedirectUrl.isEmpty() &&
1108  possibleRedirectUrl.isValid() &&
1109  possibleRedirectUrl.isRelative()) // Turn relative Url to absolute
1110  possibleRedirectUrl = reply->url().resolved(possibleRedirectUrl);
1111 
1112  dlInfo->m_redirectedTo =
1113  redirectUrl(possibleRedirectUrl, dlInfo->m_redirectedTo);
1114 
1115  QVariant status =
1116  reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
1117  if (status.isValid())
1118  statusCode = status.toInt();
1119  }
1120 
1121  if(reply && !dlInfo->m_redirectedTo.isEmpty() &&
1122  ((dlInfo->m_requestType != kRequestPost) ||
1123  (statusCode == 301 || statusCode == 302 ||
1124  statusCode == 303)))
1125  {
1126  LOG(VB_FILE, LOG_DEBUG, LOC +
1127  QString("downloadFinished(%1): Redirect: %2 -> %3")
1128  .arg((long long)dlInfo)
1129  .arg(reply->url().toString())
1130  .arg(dlInfo->m_redirectedTo.toString()));
1131 
1132  if (dlInfo->m_data)
1133  dlInfo->m_data->clear();
1134 
1135  dlInfo->m_bytesReceived = 0;
1136  dlInfo->m_bytesTotal = 0;
1137 
1138  QNetworkRequest request(dlInfo->m_redirectedTo);
1139 
1140  if (dlInfo->m_reload)
1141  {
1142  request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
1143  QNetworkRequest::AlwaysNetwork);
1144  }
1145  else if (dlInfo->m_preferCache)
1146  {
1147  request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
1148  QNetworkRequest::PreferCache);
1149  }
1150 
1151  request.setRawHeader("User-Agent",
1152  "MythDownloadManager v" MYTH_BINARY_VERSION);
1153 
1154  switch (dlInfo->m_requestType)
1155  {
1156  case kRequestHead :
1157  dlInfo->m_reply = m_manager->head(request);
1158  break;
1159  case kRequestGet :
1160  default:
1161  dlInfo->m_reply = m_manager->get(request);
1162  break;
1163  }
1164 
1165  m_downloadReplies[dlInfo->m_reply] = dlInfo;
1166 
1167  connect(dlInfo->m_reply, SIGNAL(error(QNetworkReply::NetworkError)),
1168  this, SLOT(downloadError(QNetworkReply::NetworkError)));
1169  connect(dlInfo->m_reply, SIGNAL(downloadProgress(qint64, qint64)),
1170  this, SLOT(downloadProgress(qint64, qint64)));
1171 
1172  m_downloadReplies.remove(reply);
1173  reply->deleteLater();
1174  }
1175  else
1176  {
1177  LOG(VB_FILE, LOG_DEBUG, QString("downloadFinished(%1): COMPLETE: %2")
1178  .arg((long long)dlInfo).arg(dlInfo->m_url));
1179 
1180  // HACK Insert a Date header into the cached metadata if one doesn't
1181  // already exist
1182  QUrl fileUrl = dlInfo->m_url;
1183  QString redirectLoc;
1184  int limit = 0;
1185  while (!(redirectLoc = getHeader(fileUrl, "Location")).isNull())
1186  {
1187  if (limit == CACHE_REDIRECTION_LIMIT)
1188  {
1189  LOG(VB_GENERAL, LOG_WARNING, QString("Cache Redirection limit "
1190  "reached for %1")
1191  .arg(fileUrl.toString()));
1192  return;
1193  }
1194  fileUrl.setUrl(redirectLoc);
1195  limit++;
1196  }
1197 
1198  m_infoLock->lock();
1199  QNetworkCacheMetaData urlData = m_manager->cache()->metaData(fileUrl);
1200  m_infoLock->unlock();
1201  if (getHeader(urlData, "Date").isNull())
1202  {
1203  QNetworkCacheMetaData::RawHeaderList headers = urlData.rawHeaders();
1204  QNetworkCacheMetaData::RawHeader newheader;
1205  QDateTime now = MythDate::current();
1206  newheader = QNetworkCacheMetaData::RawHeader("Date",
1207  now.toString(dateFormat).toLatin1());
1208  headers.append(newheader);
1209  urlData.setRawHeaders(headers);
1210  m_infoLock->lock();
1211  m_manager->cache()->updateMetaData(urlData);
1212  m_infoLock->unlock();
1213  }
1214  // End HACK
1215 
1216  dlInfo->m_redirectedTo.clear();
1217 
1218  int dataSize = -1;
1219 
1220  // If we downloaded via the QNetworkAccessManager
1221  // AND the caller isn't handling the reply directly
1222  if (reply && dlInfo->m_processReply)
1223  {
1224  bool append = (!dlInfo->m_syncMode && dlInfo->m_caller);
1225  QByteArray data = reply->readAll();
1226  dataSize = data.size();
1227 
1228  if (append)
1229  dlInfo->m_bytesReceived += dataSize;
1230  else
1231  dlInfo->m_bytesReceived = dataSize;
1232 
1233  dlInfo->m_bytesTotal = dlInfo->m_bytesReceived;
1234 
1235  if (dlInfo->m_data)
1236  {
1237  if (append)
1238  dlInfo->m_data->append(data);
1239  else
1240  *dlInfo->m_data = data;
1241  }
1242  else if (!dlInfo->m_outFile.isEmpty())
1243  {
1244  saveFile(dlInfo->m_outFile, data, append);
1245  }
1246  }
1247  else if (!reply) // If we downloaded via RemoteFile
1248  {
1249  if (dlInfo->m_data)
1250  {
1251  (*dlInfo->m_data) = dlInfo->m_privData;
1252  }
1253  else if (!dlInfo->m_outFile.isEmpty())
1254  {
1255  saveFile(dlInfo->m_outFile, dlInfo->m_privData);
1256  }
1257  dlInfo->m_bytesReceived += dataSize;
1258  dlInfo->m_bytesTotal = dlInfo->m_bytesReceived;
1259  }
1260  // else we downloaded via QNetworkAccessManager
1261  // AND the caller is handling the reply
1262 
1263  m_infoLock->lock();
1264  if (!m_downloadInfos.remove(dlInfo->m_url))
1265  LOG(VB_GENERAL, LOG_ERR, LOC +
1266  QString("ERROR download finished but failed to remove url: %1")
1267  .arg(dlInfo->m_url));
1268 
1269  if (reply)
1270  m_downloadReplies.remove(reply);
1271  m_infoLock->unlock();
1272 
1273  dlInfo->SetDone(true);
1274 
1275  if (!dlInfo->m_syncMode)
1276  {
1277  if (dlInfo->m_caller)
1278  {
1279  LOG(VB_FILE, LOG_DEBUG, QString("downloadFinished(%1): "
1280  "COMPLETE: %2, sending event to caller")
1281  .arg((long long)dlInfo).arg(dlInfo->m_url));
1282 
1283  QStringList args;
1284  args << dlInfo->m_url;
1285  args << dlInfo->m_outFile;
1286  args << QString::number(dlInfo->m_bytesTotal);
1287  // placeholder for error string
1288  args << (reply ? reply->errorString() : QString());
1289  args << QString::number((int)(reply ? reply->error() :
1290  dlInfo->m_errorCode));
1291 
1292  QCoreApplication::postEvent(dlInfo->m_caller,
1293  new MythEvent("DOWNLOAD_FILE FINISHED", args));
1294  }
1295 
1296  delete dlInfo;
1297  }
1298 
1299  m_queueWaitCond.wakeAll();
1300  }
1301 }
1302 
1308 void MythDownloadManager::downloadProgress(qint64 bytesReceived,
1309  qint64 bytesTotal)
1310 {
1311  QNetworkReply *reply = (QNetworkReply*)sender();
1312 
1313  LOG(VB_FILE, LOG_DEBUG, LOC +
1314  QString("downloadProgress(%1, %2) (for reply %3)")
1315  .arg(bytesReceived).arg(bytesTotal).arg((long long)reply));
1316 
1317  QMutexLocker locker(m_infoLock);
1318  if (!m_downloadReplies.contains(reply))
1319  return;
1320 
1321  MythDownloadInfo *dlInfo = m_downloadReplies[reply];
1322 
1323  if (!dlInfo)
1324  return;
1325 
1326  dlInfo->m_lastStat = MythDate::current();
1327 
1328  LOG(VB_FILE, LOG_DEBUG, LOC +
1329  QString("downloadProgress: %1 to %2 is at %3 of %4 bytes downloaded")
1330  .arg(dlInfo->m_url).arg(dlInfo->m_outFile)
1331  .arg(bytesReceived).arg(bytesTotal));
1332 
1333  if (!dlInfo->m_syncMode && dlInfo->m_caller)
1334  {
1335  LOG(VB_FILE, LOG_DEBUG, QString("downloadProgress(%1): "
1336  "sending event to caller")
1337  .arg(reply->url().toString()));
1338 
1339  bool appendToFile = (dlInfo->m_bytesReceived != 0);
1340  QByteArray data = reply->readAll();
1341  if (!dlInfo->m_outFile.isEmpty())
1342  saveFile(dlInfo->m_outFile, data, appendToFile);
1343 
1344  if (dlInfo->m_data)
1345  dlInfo->m_data->append(data);
1346 
1347  dlInfo->m_bytesReceived = bytesReceived;
1348  dlInfo->m_bytesTotal = bytesTotal;
1349 
1350  QStringList args;
1351  args << dlInfo->m_url;
1352  args << dlInfo->m_outFile;
1353  args << QString::number(bytesReceived);
1354  args << QString::number(bytesTotal);
1355 
1356  QCoreApplication::postEvent(dlInfo->m_caller,
1357  new MythEvent("DOWNLOAD_FILE UPDATE", args));
1358  }
1359 }
1360 
1368 bool MythDownloadManager::saveFile(const QString &outFile,
1369  const QByteArray &data,
1370  const bool append)
1371 {
1372  if (outFile.isEmpty() || !data.size())
1373  return false;
1374 
1375  QFile file(outFile);
1376  QFileInfo fileInfo(outFile);
1377  QDir qdir(fileInfo.absolutePath());
1378 
1379  if (!qdir.exists() && !qdir.mkpath(fileInfo.absolutePath()))
1380  {
1381  LOG(VB_GENERAL, LOG_ERR, QString("Failed to create: '%1'")
1382  .arg(fileInfo.absolutePath()));
1383  return false;
1384  }
1385 
1386  QIODevice::OpenMode mode = QIODevice::Unbuffered|QIODevice::WriteOnly;
1387  if (append)
1388  mode |= QIODevice::Append;
1389 
1390  if (!file.open(mode))
1391  {
1392  LOG(VB_GENERAL, LOG_ERR, QString("Failed to open: '%1'") .arg(outFile));
1393  return false;
1394  }
1395 
1396  off_t offset = 0;
1397  size_t remaining = data.size();
1398  uint failure_cnt = 0;
1399  while ((remaining > 0) && (failure_cnt < 5))
1400  {
1401  ssize_t written = file.write(data.data() + offset, remaining);
1402  if (written < 0)
1403  {
1404  failure_cnt++;
1405  usleep(50000);
1406  continue;
1407  }
1408 
1409  failure_cnt = 0;
1410  offset += written;
1411  remaining -= written;
1412  }
1413 
1414  if (remaining > 0)
1415  return false;
1416 
1417  return true;
1418 }
1419 
1424 QDateTime MythDownloadManager::GetLastModified(const QString &url)
1425 {
1426  // If the header has not expired and
1427  // the last modification date is less than 1 hours old or if
1428  // the cache object is less than 20 minutes old,
1429  // then use the cached header otherwise redownload the header
1430 
1431  static const char dateFormat[] = "ddd, dd MMM yyyy hh:mm:ss 'GMT'";
1432  LOG(VB_FILE, LOG_DEBUG, LOC + QString("GetLastModified('%1')").arg(url));
1433  QDateTime result;
1434 
1435  QDateTime now = MythDate::current();
1436 
1437  QUrl cacheUrl = QUrl(url);
1438 
1439  // Deal with redirects, we want the cached data for the final url
1440  QString redirectLoc;
1441  int limit = 0;
1442  while (!(redirectLoc = getHeader(cacheUrl, "Location")).isNull())
1443  {
1444  if (limit == CACHE_REDIRECTION_LIMIT)
1445  {
1446  LOG(VB_GENERAL, LOG_WARNING, QString("Cache Redirection limit "
1447  "reached for %1")
1448  .arg(cacheUrl.toString()));
1449  return result;
1450  }
1451  cacheUrl.setUrl(redirectLoc);
1452  limit++;
1453  }
1454 
1455  m_infoLock->lock();
1456  QNetworkCacheMetaData urlData = m_manager->cache()->metaData(cacheUrl);
1457  m_infoLock->unlock();
1458 
1459  if (urlData.isValid() &&
1460  ((!urlData.expirationDate().isValid()) ||
1461  (urlData.expirationDate().secsTo(now) < 0)))
1462  {
1463  if (QDateTime(urlData.lastModified().toUTC()).secsTo(now) <= 3600) // 1 Hour
1464  {
1465  result = urlData.lastModified().toUTC();
1466  }
1467  else
1468  {
1469  QString date = getHeader(urlData, "Date");
1470  if (!date.isNull())
1471  {
1472  QDateTime loadDate =
1473  MythDate::fromString(date, dateFormat);
1474  loadDate.setTimeSpec(Qt::UTC);
1475  if (loadDate.secsTo(now) <= 1200) // 20 Minutes
1476  {
1477  result = urlData.lastModified().toUTC();
1478  }
1479  }
1480  }
1481  }
1482 
1483  if (!result.isValid())
1484  {
1485  MythDownloadInfo *dlInfo = new MythDownloadInfo;
1486  dlInfo->m_url = url;
1487  dlInfo->m_syncMode = true;
1488  // Head request, we only want to inspect the headers
1489  dlInfo->m_requestType = kRequestHead;
1490 
1491  if (downloadNow(dlInfo, false))
1492  {
1493  if (dlInfo->m_reply)
1494  {
1495  QVariant lastMod =
1496  dlInfo->m_reply->header(
1497  QNetworkRequest::LastModifiedHeader);
1498  if (lastMod.isValid())
1499  result = lastMod.toDateTime().toUTC();
1500  }
1501 
1502  // downloadNow() will set a flag to trigger downloadFinished()
1503  // to delete the dlInfo if the download times out
1504  delete dlInfo;
1505  }
1506  }
1507 
1508  LOG(VB_FILE, LOG_DEBUG, LOC + QString("GetLastModified('%1'): Result %2")
1509  .arg(url).arg(result.toString()));
1510 
1511  return result;
1512 }
1513 
1514 
1519 {
1520  QMutexLocker locker(&m_cookieLock);
1521 
1522  MythCookieJar *jar = new MythCookieJar;
1523  jar->load(filename);
1524  m_manager->setCookieJar(jar);
1525 }
1526 
1531 {
1532  QMutexLocker locker(&m_cookieLock);
1533 
1534  if (!m_manager->cookieJar())
1535  return;
1536 
1537  MythCookieJar *jar = static_cast<MythCookieJar *>(m_manager->cookieJar());
1538  jar->save(filename);
1539 }
1540 
1541 void MythDownloadManager::setCookieJar(QNetworkCookieJar *cookieJar)
1542 {
1543  QMutexLocker locker(&m_cookieLock);
1544  m_manager->setCookieJar(cookieJar);
1545 }
1546 
1550 QNetworkCookieJar *MythDownloadManager::copyCookieJar(void)
1551 {
1552  QMutexLocker locker(&m_cookieLock);
1553 
1554  if (!m_manager->cookieJar())
1555  return NULL;
1556 
1557  MythCookieJar *inJar = static_cast<MythCookieJar *>(m_manager->cookieJar());
1558  MythCookieJar *outJar = new MythCookieJar(*inJar);
1559 
1560  return static_cast<QNetworkCookieJar *>(outJar);
1561 }
1562 
1566 void MythDownloadManager::refreshCookieJar(QNetworkCookieJar *jar)
1567 {
1568  QMutexLocker locker(&m_cookieLock);
1569  if (m_inCookieJar)
1570  delete m_inCookieJar;
1571 
1572  MythCookieJar *inJar = static_cast<MythCookieJar *>(jar);
1573  MythCookieJar *outJar = new MythCookieJar(*inJar);
1574  m_inCookieJar = static_cast<QNetworkCookieJar *>(outJar);
1575 
1576  QMutexLocker locker2(&m_queueWaitLock);
1577  m_queueWaitCond.wakeAll();
1578 }
1579 
1583 {
1584  QMutexLocker locker(&m_cookieLock);
1585 
1586  MythCookieJar *inJar = static_cast<MythCookieJar *>(m_inCookieJar);
1587  MythCookieJar *outJar = new MythCookieJar(*inJar);
1588  m_manager->setCookieJar(static_cast<QNetworkCookieJar *>(outJar));
1589 
1590  delete m_inCookieJar;
1591  m_inCookieJar = NULL;
1592 }
1593 
1594 QString MythDownloadManager::getHeader(const QUrl& url, const QString& header)
1595 {
1596  if (!m_manager || !m_manager->cache())
1597  return QString::null;
1598 
1599  m_infoLock->lock();
1600  QNetworkCacheMetaData metadata = m_manager->cache()->metaData(url);
1601  m_infoLock->unlock();
1602 
1603  return getHeader(metadata, header);
1604 }
1605 
1611 QString MythDownloadManager::getHeader(const QNetworkCacheMetaData &cacheData,
1612  const QString& header)
1613 {
1614  QNetworkCacheMetaData::RawHeaderList headers = cacheData.rawHeaders();
1615  bool found = false;
1616  QNetworkCacheMetaData::RawHeaderList::iterator it = headers.begin();
1617  for (; !found && it != headers.end(); ++it)
1618  {
1619  if (QString((*it).first) == header)
1620  {
1621  found = true;
1622  return QString((*it).second);
1623  }
1624  }
1625 
1626  return QString::null;
1627 }
1628 
1629 
1634 {
1635  const QList<QNetworkCookie> cookieList = old.allCookies();
1636  setAllCookies(cookieList);
1637 }
1638 
1642 {
1643 }
1644 
1648 void MythCookieJar::load(const QString &filename)
1649 {
1650  LOG(VB_GENERAL, LOG_DEBUG, QString("MythCookieJar: loading cookies from: %1").arg(filename));
1651 
1652  QFile f(filename);
1653  if (!f.open(QIODevice::ReadOnly))
1654  {
1655  LOG(VB_GENERAL, LOG_WARNING, QString("MythCookieJar::load() failed to open file for reading: %1").arg(filename));
1656  return;
1657  }
1658 
1659  QList<QNetworkCookie> cookieList;
1660  QTextStream stream(&f);
1661  while (!stream.atEnd())
1662  {
1663  QString cookie = stream.readLine();
1664  cookieList << QNetworkCookie::parseCookies(cookie.toLocal8Bit());
1665  }
1666 
1667  setAllCookies(cookieList);
1668 }
1669 
1673 void MythCookieJar::save(const QString &filename)
1674 {
1675  LOG(VB_GENERAL, LOG_DEBUG, QString("MythCookieJar: saving cookies to: %1").arg(filename));
1676 
1677  QFile f(filename);
1678  if (!f.open(QIODevice::WriteOnly))
1679  {
1680  LOG(VB_GENERAL, LOG_ERR, QString("MythCookieJar::save() failed to open file for writing: %1").arg(filename));
1681  return;
1682  }
1683 
1684  QList<QNetworkCookie> cookieList = allCookies();
1685  QTextStream stream(&f);
1686 
1687  for (QList<QNetworkCookie>::iterator it = cookieList.begin();
1688  it != cookieList.end(); ++it)
1689  {
1690  stream << (*it).toRawForm() << endl;
1691  }
1692 }
1693 
1694 
1695 /* 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 queueItem(const QString &url, QNetworkRequest *req, const QString &dest, QByteArray *data, QObject *caller, const MRequestType reqType=kRequestGet, const bool reload=false)
Adds a request to the download queue.
QMap< QString, MythDownloadInfo * > m_downloadInfos
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:83
MythDownloadManager * downloadManager
void start(QRunnable *runnable, QString debugName, int priority=0)
void removeListener(QObject *caller)
Disconnects the specified caller from any existing MythDownloadInfo instances.
QUrl redirectUrl(const QUrl &possibleRedirectUrl, const QUrl &oldRedirectUrl) const
Checks whether we were redirected to the given URL.
void run(void)
Runs a loop to process incoming download requests and triggers download events to be processed...
__int64 ssize_t
Definition: compat.h:72
QNetworkDiskCache * m_diskCache
void queueDownload(const QString &url, const QString &dest, QObject *caller, const bool reload=false)
Adds a url to the download queue.
~MythDownloadManager()
Destructor for MythDownloadManager.
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
AllMusic * parent
const char * filename
Definition: ioapi.h:135
QList< MythDownloadInfo * > m_downloadQueue
unsigned int uint
Definition: compat.h:136
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythDownloadManager()
Constructor for MythDownloadManager.
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
Slot to process download update events.
const QHash< QByteArray, QByteArray > * m_headers
friend class RemoteFileDownloadThread
MRequestType m_requestType
void downloadFinished(QNetworkReply *reply)
Slot to process download finished events.
bool downloadAuth(const QString &url, const QString &dest, const bool reload=false, AuthCallback authCallback=NULL, void *authArg=NULL, const QHash< QByteArray, QByteArray > *headers=NULL)
Downloads a URL to a file in blocking mode.
QString GetConfDir(void)
Definition: mythdirs.cpp:140
void authCallback(QNetworkReply *reply, QAuthenticator *authenticator)
Signal handler for authentication requests.
QNetworkAccessManager * m_manager
bool postAuth(const QString &url, QByteArray *data, AuthCallback authCallback, void *authArg, const QHash< QByteArray, QByteArray > *headers=NULL)
Posts data to a url via the QNetworkAccessManager.
voidpf uLong offset
Definition: ioapi.h:142
This class is used as a container for messages.
Definition: mythevent.h:15
void setCookieJar(QNetworkCookieJar *cookieJar)
RemoteFileDownloadThread(MythDownloadManager *parent, MythDownloadInfo *dlInfo)
QWaitCondition m_queueWaitCond
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
void queuePost(const QString &url, QByteArray *data, QObject *caller)
Queues a post to a URL via the QNetworkAccessManager.
bool processItem(const QString &url, QNetworkRequest *req, const QString &dest, QByteArray *data, const MRequestType reqType=kRequestGet, const bool reload=false, AuthCallback authCallback=NULL, void *authArg=NULL, const QHash< QByteArray, QByteArray > *headers=NULL)
Processes a network request immediately and waits for a response.
void refreshCookieJar(QNetworkCookieJar *jar)
Refresh the temporary cookie jar from another cookie jar.
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
bool download(const QString &url, const QString &dest, const bool reload=false)
Downloads a URL to a file in blocking mode.
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't store the resulting data anywhere.