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 #include "portchecker.h"
34 
35 using namespace std;
36 
37 #define LOC QString("DownloadManager: ")
38 #define CACHE_REDIRECTION_LIMIT 10
39 
41 QMutex dmCreateLock;
42 
47 {
48  public:
50  m_request(NULL), m_reply(NULL), m_data(NULL),
51  m_caller(NULL), m_requestType(kRequestGet),
52  m_reload(false), m_preferCache(false), m_syncMode(false),
53  m_processReply(true), m_done(false), m_bytesReceived(0),
54  m_bytesTotal(0), m_lastStat(MythDate::current()),
55  m_authCallback(NULL), m_authArg(NULL),
56  m_headers(NULL), m_errorCode(QNetworkReply::NoError)
57  {
58  qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
59  }
60 
62  {
63  if (m_request)
64  delete m_request;
65  if (m_reply && m_processReply)
66  m_reply->deleteLater();
67  }
68 
69  void detach(void)
70  {
71  m_url.detach();
72  m_outFile.detach();
73  }
74 
75  bool IsDone(void)
76  {
77  QMutexLocker lock(&m_lock);
78  return m_done;
79  }
80 
81  void SetDone(bool done)
82  {
83  QMutexLocker lock(&m_lock);
84  m_done = done;
85  }
86 
87  QString m_url;
89  QNetworkRequest *m_request;
90  QNetworkReply *m_reply;
91  QString m_outFile;
92  QByteArray *m_data;
93  QByteArray m_privData;
94  QObject *m_caller;
96  bool m_reload;
98  bool m_syncMode;
100  bool m_done;
102  qint64 m_bytesTotal;
103  QDateTime m_lastStat;
105  void *m_authArg;
106  const QHash<QByteArray, QByteArray> *m_headers;
107 
108  QNetworkReply::NetworkError m_errorCode;
109  QMutex m_lock;
110 };
111 
112 
117 class MythCookieJar : public QNetworkCookieJar
118 {
119  public:
120  MythCookieJar();
122  void load(const QString &filename);
123  void save(const QString &filename);
124 };
125 
129 class RemoteFileDownloadThread : public QRunnable
130 {
131  public:
133  MythDownloadInfo *dlInfo) :
134  m_parent(parent),
135  m_dlInfo(dlInfo)
136  {
137  m_dlInfo->detach();
138  }
139 
140  void run()
141  {
142  bool ok = false;
143 
144  RemoteFile *rf = new RemoteFile(m_dlInfo->m_url, false, false, 0);
145  ok = rf->SaveAs(m_dlInfo->m_privData);
146  delete rf;
147 
148  if (!ok)
149  m_dlInfo->m_errorCode = QNetworkReply::UnknownNetworkError;
150 
151  m_dlInfo->m_bytesReceived = m_dlInfo->m_privData.size();
152  m_dlInfo->m_bytesTotal = m_dlInfo->m_bytesReceived;
153 
154  m_parent->downloadFinished(m_dlInfo);
155  }
156 
157  private:
160 };
161 
165 {
166  if (downloadManager)
167  {
168  delete downloadManager;
169  downloadManager = NULL;
170  }
171 }
172 
177 {
178  if (downloadManager)
179  return downloadManager;
180 
181  QMutexLocker locker(&dmCreateLock);
182 
183  // Check once more in case the download manager was created
184  // while we were securing the lock.
185  if (downloadManager)
186  return downloadManager;
187 
189  tmpDLM->start();
190  while (!tmpDLM->getQueueThread())
191  usleep(10000);
192 
193  tmpDLM->moveToThread(tmpDLM->getQueueThread());
194  tmpDLM->setRunThread();
195 
196  while (!tmpDLM->isRunning())
197  usleep(10000);
198 
199  downloadManager = tmpDLM;
200 
202 
203  return downloadManager;
204 }
205 
210  MThread("DownloadManager"),
211  m_manager(NULL),
212  m_diskCache(NULL),
213  m_proxy(NULL),
214  m_infoLock(new QMutex(QMutex::Recursive)),
215  m_queueThread(NULL),
216  m_runThread(false),
217  m_isRunning(false),
218  m_inCookieJar(NULL)
219 {
220 }
221 
225 {
226  m_runThread = false;
227  m_queueWaitCond.wakeAll();
228 
229  wait();
230 
231  delete m_infoLock;
232 
233  if (m_inCookieJar)
234  delete m_inCookieJar;
235 }
236 
241 {
242  RunProlog();
243 
244  bool downloading = false;
245  bool itemsInQueue = false;
246  bool itemsInCancellationQueue = false;
247  bool waitAnyway = false;
248 
249  m_queueThread = QThread::currentThread();
250 
251  while (!m_runThread)
252  usleep(50000);
253 
254  m_manager = new QNetworkAccessManager(this);
255  m_diskCache = new QNetworkDiskCache(this);
256  m_proxy = new QNetworkProxy();
257  m_diskCache->setCacheDirectory(GetConfDir() + "/cache/" +
258  QCoreApplication::applicationName() + "-" +
260  m_manager->setCache(m_diskCache);
261 
262  // Set the proxy for the manager to be the application default proxy,
263  // which has already been setup
264  m_manager->setProxy(*m_proxy);
265 
266  // make sure the cookieJar is created in the same thread as the manager
267  // and set its parent to NULL so it can be shared between managers
268  m_manager->cookieJar()->setParent(NULL);
269 
270  QObject::connect(m_manager, SIGNAL(finished(QNetworkReply*)), this,
271  SLOT(downloadFinished(QNetworkReply*)));
272 
273  m_isRunning = true;
274  while (m_runThread)
275  {
276  if (m_inCookieJar)
277  {
278  LOG(VB_GENERAL, LOG_DEBUG, "Updating DLManager's Cookie Jar");
279  updateCookieJar();
280  }
281  m_infoLock->lock();
282  LOG(VB_FILE, LOG_DEBUG, LOC + QString("items downloading %1").arg(m_downloadInfos.count()));
283  LOG(VB_FILE, LOG_DEBUG, LOC + QString("items queued %1").arg(m_downloadQueue.count()));
284  downloading = !m_downloadInfos.isEmpty();
285  itemsInCancellationQueue = !m_cancellationQueue.isEmpty();
286  m_infoLock->unlock();
287 
288  if (itemsInCancellationQueue)
289  {
291  }
292  if (downloading)
293  QCoreApplication::processEvents();
294 
295  m_infoLock->lock();
296  itemsInQueue = !m_downloadQueue.isEmpty();
297  m_infoLock->unlock();
298 
299  if (!itemsInQueue || waitAnyway)
300  {
301  waitAnyway = false;
302  m_queueWaitLock.lock();
303 
304  if (downloading)
305  {
306  LOG(VB_FILE, LOG_DEBUG, LOC + QString("waiting 200ms"));
307  m_queueWaitCond.wait(&m_queueWaitLock, 200);
308  }
309  else
310  {
311  LOG(VB_FILE, LOG_DEBUG, LOC + QString("waiting for more items to download"));
313  }
314 
315  m_queueWaitLock.unlock();
316  }
317 
318  m_infoLock->lock();
319  if (!m_downloadQueue.isEmpty())
320  {
321  MythDownloadInfo *dlInfo = m_downloadQueue.front();
322 
323  m_downloadQueue.pop_front();
324 
325  if (!dlInfo)
326  {
327  m_infoLock->unlock();
328  continue;
329  }
330 
331  if (m_downloadInfos.contains(dlInfo->m_url))
332  {
333  // Push request to the end of the queue to let others process.
334  // If this is the only item in the queue, force the loop to
335  // wait a little.
336  if (m_downloadQueue.isEmpty())
337  waitAnyway = true;
338  m_downloadQueue.push_back(dlInfo);
339  m_infoLock->unlock();
340  continue;
341  }
342 
343  if (dlInfo->m_url.startsWith("myth://"))
344  downloadRemoteFile(dlInfo);
345  else
346  {
347  QMutexLocker cLock(&m_cookieLock);
348  downloadQNetworkRequest(dlInfo);
349  }
350 
351  m_downloadInfos[dlInfo->m_url] = dlInfo;
352  }
353  m_infoLock->unlock();
354  }
355  m_isRunning = false;
356 
357  RunEpilog();
358 }
359 
370 void MythDownloadManager::queueItem(const QString &url, QNetworkRequest *req,
371  const QString &dest, QByteArray *data,
372  QObject *caller, const MRequestType reqType,
373  const bool reload)
374 {
375  MythDownloadInfo *dlInfo = new MythDownloadInfo;
376 
377  dlInfo->m_url = url;
378  dlInfo->m_request = req;
379  dlInfo->m_outFile = dest;
380  dlInfo->m_data = data;
381  dlInfo->m_caller = caller;
382  dlInfo->m_requestType = reqType;
383  dlInfo->m_reload = reload;
384 
385  dlInfo->detach();
386 
387  QMutexLocker locker(m_infoLock);
388  m_downloadQueue.push_back(dlInfo);
389  m_queueWaitCond.wakeAll();
390 }
391 
404 bool MythDownloadManager::processItem(const QString &url, QNetworkRequest *req,
405  const QString &dest, QByteArray *data,
406  const MRequestType reqType,
407  const bool reload,
408  AuthCallback authCallback, void *authArg,
409  const QHash<QByteArray, QByteArray> *headers)
410 {
411  MythDownloadInfo *dlInfo = new MythDownloadInfo;
412 
413  dlInfo->m_url = url;
414  dlInfo->m_request = req;
415  dlInfo->m_outFile = dest;
416  dlInfo->m_data = data;
417  dlInfo->m_requestType = reqType;
418  dlInfo->m_reload = reload;
419  dlInfo->m_syncMode = true;
420  dlInfo->m_authCallback = authCallback;
421  dlInfo->m_authArg = authArg;
422  dlInfo->m_headers = headers;
423 
424  return downloadNow(dlInfo, true);
425 }
426 
430 void MythDownloadManager::preCache(const QString &url)
431 {
432  LOG(VB_FILE, LOG_DEBUG, LOC + QString("preCache('%1')").arg(url));
433  queueItem(url, NULL, QString(), NULL, NULL);
434 }
435 
442 void MythDownloadManager::queueDownload(const QString &url,
443  const QString &dest,
444  QObject *caller,
445  const bool reload)
446 {
447  LOG(VB_FILE, LOG_DEBUG, LOC + QString("queueDownload('%1', '%2', %3)")
448  .arg(url).arg(dest).arg((long long)caller));
449 
450  queueItem(url, NULL, dest, NULL, caller, kRequestGet, reload);
451 }
452 
458 void MythDownloadManager::queueDownload(QNetworkRequest *req,
459  QByteArray *data,
460  QObject *caller)
461 {
462  LOG(VB_FILE, LOG_DEBUG, LOC + QString("queueDownload('%1', '%2', %3)")
463  .arg(req->url().toString()).arg((long long)data)
464  .arg((long long)caller));
465 
466  queueItem(req->url().toString(), req, QString(), data, caller,
467  kRequestGet,
468  (QNetworkRequest::AlwaysNetwork == req->attribute(
469  QNetworkRequest::CacheLoadControlAttribute,
470  QNetworkRequest::PreferNetwork).toInt()));
471 }
472 
479 bool MythDownloadManager::download(const QString &url, const QString &dest,
480  const bool reload)
481 {
482  return processItem(url, NULL, dest, NULL, kRequestGet, reload);
483 }
484 
491 bool MythDownloadManager::download(const QString &url, QByteArray *data,
492  const bool reload)
493 {
494  return processItem(url, NULL, QString(), data, kRequestGet, reload);
495 }
496 
503 QNetworkReply *MythDownloadManager::download(const QString &url,
504  const bool reload)
505 {
506  MythDownloadInfo *dlInfo = new MythDownloadInfo;
507  QNetworkReply *reply = NULL;
508 
509  dlInfo->m_url = url;
510  dlInfo->m_reload = reload;
511  dlInfo->m_syncMode = true;
512  dlInfo->m_processReply = false;
513 
514  if (downloadNow(dlInfo, false))
515  {
516  if (dlInfo->m_reply)
517  {
518  reply = dlInfo->m_reply;
519  // prevent dlInfo dtor from deleting the reply
520  dlInfo->m_reply = NULL;
521 
522  delete dlInfo;
523 
524  return reply;
525  }
526 
527  delete dlInfo;
528  }
529 
530  return NULL;
531 }
532 
538 bool MythDownloadManager::download(QNetworkRequest *req, QByteArray *data)
539 {
540  LOG(VB_FILE, LOG_DEBUG, LOC + QString("download('%1', '%2')")
541  .arg(req->url().toString()).arg((long long)data));
542  return processItem(req->url().toString(), req, QString(), data,
543  kRequestGet,
544  (QNetworkRequest::AlwaysNetwork == req->attribute(
545  QNetworkRequest::CacheLoadControlAttribute,
546  QNetworkRequest::PreferNetwork).toInt()));
547 }
548 
558 bool MythDownloadManager::downloadAuth(const QString &url, const QString &dest,
559  const bool reload, AuthCallback authCallback, void *authArg,
560  const QHash<QByteArray, QByteArray> *headers)
561 {
562  return processItem(url, NULL, dest, NULL, kRequestGet, reload, authCallback,
563  authArg, headers);
564 }
565 
566 
572 void MythDownloadManager::queuePost(const QString &url,
573  QByteArray *data,
574  QObject *caller)
575 {
576  LOG(VB_FILE, LOG_DEBUG, LOC + QString("queuePost('%1', '%2')")
577  .arg(url).arg((long long)data));
578 
579  if (!data)
580  {
581  LOG(VB_GENERAL, LOG_ERR, LOC + "queuePost(), data is NULL!");
582  return;
583  }
584 
585  queueItem(url, NULL, QString(), data, caller, kRequestPost);
586 }
587 
593 void MythDownloadManager::queuePost(QNetworkRequest *req,
594  QByteArray *data,
595  QObject *caller)
596 {
597  LOG(VB_FILE, LOG_DEBUG, LOC + QString("queuePost('%1', '%2')")
598  .arg(req->url().toString()).arg((long long)data));
599 
600  if (!data)
601  {
602  LOG(VB_GENERAL, LOG_ERR, LOC + "queuePost(), data is NULL!");
603  return;
604  }
605 
606  queueItem(req->url().toString(), req, QString(), data, caller,
607  kRequestPost,
608  (QNetworkRequest::AlwaysNetwork == req->attribute(
609  QNetworkRequest::CacheLoadControlAttribute,
610  QNetworkRequest::PreferNetwork).toInt()));
611 
612 }
613 
619 bool MythDownloadManager::post(const QString &url, QByteArray *data)
620 {
621  LOG(VB_FILE, LOG_DEBUG, LOC + QString("post('%1', '%2')")
622  .arg(url).arg((long long)data));
623 
624  if (!data)
625  {
626  LOG(VB_GENERAL, LOG_ERR, LOC + "post(), data is NULL!");
627  return false;
628  }
629 
630  return processItem(url, NULL, QString(), data, kRequestPost);
631 }
632 
638 bool MythDownloadManager::post(QNetworkRequest *req, QByteArray *data)
639 {
640  LOG(VB_FILE, LOG_DEBUG, LOC + QString("post('%1', '%2')")
641  .arg(req->url().toString()).arg((long long)data));
642 
643  if (!data)
644  {
645  LOG(VB_GENERAL, LOG_ERR, LOC + "post(), data is NULL!");
646  return false;
647  }
648 
649  return processItem(req->url().toString(), req, QString(), data,
650  kRequestPost,
651  (QNetworkRequest::AlwaysNetwork == req->attribute(
652  QNetworkRequest::CacheLoadControlAttribute,
653  QNetworkRequest::PreferNetwork).toInt()));
654 
655 }
656 
665 bool MythDownloadManager::postAuth(const QString &url, QByteArray *data,
666  AuthCallback authCallback, void *authArg,
667  const QHash<QByteArray, QByteArray> *headers)
668 {
669  LOG(VB_FILE, LOG_DEBUG, LOC + QString("postAuth('%1', '%2')")
670  .arg(url).arg((long long)data));
671 
672  if (!data)
673  {
674  LOG(VB_GENERAL, LOG_ERR, LOC + "postAuth(), data is NULL!");
675  return false;
676  }
677 
678  return processItem(url, NULL, NULL, data, kRequestPost, false, authCallback,
679  authArg, headers);
680 }
681 
686 {
687  RemoteFileDownloadThread *dlThread =
688  new RemoteFileDownloadThread(this, dlInfo);
689  MThreadPool::globalInstance()->start(dlThread, "RemoteFileDownload");
690 }
691 
696 {
697  if (!dlInfo)
698  return;
699 
700  static const char dateFormat[] = "ddd, dd MMM yyyy hh:mm:ss 'GMT'";
701  QUrl qurl(dlInfo->m_url);
702  QNetworkRequest request;
703 
704  if (dlInfo->m_request)
705  {
706  request = *dlInfo->m_request;
707  delete dlInfo->m_request;
708  dlInfo->m_request = NULL;
709  }
710  else
711  request.setUrl(qurl);
712 
713  if (dlInfo->m_reload)
714  {
715  request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
716  QNetworkRequest::AlwaysNetwork);
717  }
718  else
719  {
720  // Prefer the in-cache item if one exists and it is less than 5 minutes
721  // old and it will not expire in the next 10 seconds
722  QDateTime now = MythDate::current();
723 
724  // Handle redirects, we want the metadata of the file headers
725  QString redirectLoc;
726  int limit = 0;
727  while (!(redirectLoc = getHeader(qurl, "Location")).isNull())
728  {
729  if (limit == CACHE_REDIRECTION_LIMIT)
730  {
731  LOG(VB_GENERAL, LOG_WARNING, QString("Cache Redirection limit "
732  "reached for %1")
733  .arg(qurl.toString()));
734  return;
735  }
736  qurl.setUrl(redirectLoc);
737  limit++;
738  }
739 
740  LOG(VB_NETWORK, LOG_DEBUG, QString("Checking cache for %1")
741  .arg(qurl.toString()));
742 
743  m_infoLock->lock();
744  QNetworkCacheMetaData urlData = m_manager->cache()->metaData(qurl);
745  m_infoLock->unlock();
746  if ((urlData.isValid()) &&
747  ((!urlData.expirationDate().isValid()) ||
748  (QDateTime(urlData.expirationDate().toUTC()).secsTo(now) < 10)))
749  {
750  QString dateString = getHeader(urlData, "Date");
751 
752  if (!dateString.isNull())
753  {
754  QDateTime loadDate =
755  MythDate::fromString(dateString, dateFormat);
756  loadDate.setTimeSpec(Qt::UTC);
757  if (loadDate.secsTo(now) <= 720)
758  {
759  dlInfo->m_preferCache = true;
760  LOG(VB_NETWORK, LOG_DEBUG, QString("Preferring cache for %1")
761  .arg(qurl.toString()));
762  }
763  }
764  }
765  }
766 
767  if (dlInfo->m_preferCache)
768  request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
769  QNetworkRequest::PreferCache);
770 
771  if (!request.hasRawHeader("User-Agent"))
772  {
773  request.setRawHeader("User-Agent",
774  "MythTV v" MYTH_BINARY_VERSION
775  " MythDownloadManager");
776  }
777 
778  if (dlInfo->m_headers)
779  {
780  QHash<QByteArray, QByteArray>::const_iterator it =
781  dlInfo->m_headers->constBegin();
782  for ( ; it != dlInfo->m_headers->constEnd(); ++it )
783  {
784  if (!it.key().isEmpty() && !it.value().isEmpty())
785  {
786  request.setRawHeader(it.key(), it.value());
787  }
788  }
789  }
790 
791  switch (dlInfo->m_requestType)
792  {
793  case kRequestPost :
794  dlInfo->m_reply = m_manager->post(request, *dlInfo->m_data);
795  break;
796  case kRequestHead :
797  dlInfo->m_reply = m_manager->head(request);
798  break;
799  case kRequestGet :
800  default:
801  dlInfo->m_reply = m_manager->get(request);
802  break;
803  }
804 
805  m_downloadReplies[dlInfo->m_reply] = dlInfo;
806 
807  if (dlInfo->m_authCallback)
808  {
809  connect(m_manager, SIGNAL(authenticationRequired(QNetworkReply *,
810  QAuthenticator *)),
811  this, SLOT(authCallback(QNetworkReply *, QAuthenticator *)));
812  }
813 
814  connect(dlInfo->m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
815  SLOT(downloadError(QNetworkReply::NetworkError)));
816  connect(dlInfo->m_reply, SIGNAL(downloadProgress(qint64, qint64)),
817  this, SLOT(downloadProgress(qint64, qint64)));
818 }
819 
824 void MythDownloadManager::authCallback(QNetworkReply *reply,
825  QAuthenticator *authenticator)
826 {
827  if (!reply)
828  return;
829 
830  MythDownloadInfo *dlInfo = m_downloadReplies[reply];
831 
832  if (!dlInfo)
833  return;
834 
835  if (dlInfo->m_authCallback)
836  {
837  LOG(VB_FILE, LOG_DEBUG, "Calling auth callback");
838  dlInfo->m_authCallback(reply, authenticator, dlInfo->m_authArg);
839  }
840 }
841 
849 {
850  if (!dlInfo)
851  return false;
852 
853  dlInfo->m_syncMode = true;
854 
855  // Special handling for link-local
856  // Not needed for Windows because windows does not need
857  // the scope id.
858 #ifndef _WIN32
859  if (dlInfo->m_url.startsWith("http://[fe80::",Qt::CaseInsensitive))
860  return downloadNowLinkLocal(dlInfo,deleteInfo);
861 #endif
862  m_infoLock->lock();
863  m_downloadQueue.push_back(dlInfo);
864  m_infoLock->unlock();
865  m_queueWaitCond.wakeAll();
866 
867  // timeout myth:// RemoteFile transfers 20 seconds from now
868  // timeout non-myth:// QNetworkAccessManager transfers 60 seconds after
869  // their last progress update
870  QDateTime startedAt = MythDate::current();
871  m_infoLock->lock();
872  while ((!dlInfo->IsDone()) &&
873  (dlInfo->m_errorCode == QNetworkReply::NoError) &&
874  (((!dlInfo->m_url.startsWith("myth://")) &&
875  (dlInfo->m_lastStat.secsTo(MythDate::current()) < 60)) ||
876  ((dlInfo->m_url.startsWith("myth://")) &&
877  (startedAt.secsTo(MythDate::current()) < 20))))
878  {
879  m_infoLock->unlock();
880  m_queueWaitLock.lock();
881  m_queueWaitCond.wait(&m_queueWaitLock, 200);
882  m_queueWaitLock.unlock();
883  m_infoLock->lock();
884  }
885  bool done = dlInfo->IsDone();
886  bool success =
887  done && (dlInfo->m_errorCode == QNetworkReply::NoError);
888 
889  if (!done)
890  {
891  dlInfo->m_data = NULL; // Prevent downloadFinished() from updating
892  dlInfo->m_syncMode = false; // Let downloadFinished() cleanup for us
893  if ((dlInfo->m_reply) &&
894  (dlInfo->m_errorCode == QNetworkReply::NoError))
895  {
896  LOG(VB_FILE, LOG_DEBUG,
897  LOC + QString("Aborting download - lack of data transfer"));
898  dlInfo->m_reply->abort();
899  }
900  }
901  else if (deleteInfo)
902  delete dlInfo;
903 
904  m_infoLock->unlock();
905 
906  return success;
907 }
908 
929  #ifndef _WIN32
931 {
932  bool isOK = true;
933 
934  // Only certain features are supported here
935  if (dlInfo->m_authCallback || dlInfo->m_authArg)
936  {
937  LOG(VB_GENERAL, LOG_ERR, LOC +
938  QString("Unsupported authentication for %1").arg(dlInfo->m_url));
939  isOK = false;
940  }
941  if (!dlInfo->m_outFile.isEmpty())
942  {
943  LOG(VB_GENERAL, LOG_ERR, LOC +
944  QString("Unsupported File output %1 for %2")
945  .arg(dlInfo->m_outFile).arg(dlInfo->m_url));
946  isOK = false;
947  }
948 
949  if (!deleteInfo || dlInfo->m_requestType == kRequestHead)
950  {
951  // We do not have the ability to return a network reply in dlInfo
952  // so if we are asked to do that, return an error.
953  LOG(VB_GENERAL, LOG_ERR, LOC +
954  QString("Unsupported link-local operation %1")
955  .arg(dlInfo->m_url));
956  isOK = false;
957  }
958 
959  QUrl url(dlInfo->m_url);
960  QString host(url.host());
961  int port(url.port(80));
962  if (isOK && PortChecker::resolveLinkLocal(host, port))
963  {
964  QString reqType;
965  switch (dlInfo->m_requestType)
966  {
967  case kRequestPost :
968  reqType = "POST";
969  break;
970  case kRequestGet :
971  default:
972  reqType = "GET";
973  break;
974  }
975  QByteArray *aBuffer = dlInfo->m_data;
976  QHash<QByteArray, QByteArray> headers;
977  if (dlInfo->m_headers)
978  headers = *dlInfo->m_headers;
979  if (!headers.contains("User-Agent"))
980  headers.insert("User-Agent",
981  "MythDownloadManager v" MYTH_BINARY_VERSION);
982  headers.insert("Connection", "close");
983  headers.insert("Accept-Encoding", "identity");
984  if (aBuffer && !aBuffer->isEmpty())
985  headers.insert("Content-Length",
986  (QString::number(aBuffer->size())).toUtf8());
987  headers.insert("Host",
988  (url.host()+":"+QString::number(port)).toUtf8());
989 
990  QByteArray requestMessage;
991  QString path (url.path());
992  requestMessage.append("POST ");
993  requestMessage.append(path);
994  requestMessage.append(" HTTP/1.1\r\n");
995  QHashIterator<QByteArray, QByteArray> it(headers);
996  while (it.hasNext())
997  {
998  it.next();
999  requestMessage.append(it.key());
1000  requestMessage.append(": ");
1001  requestMessage.append(it.value());
1002  requestMessage.append("\r\n");
1003  }
1004  requestMessage.append("\r\n");
1005  if (aBuffer && !aBuffer->isEmpty())
1006  {
1007  requestMessage.append(*aBuffer);
1008  }
1009  QTcpSocket socket;
1010  socket.connectToHost(host, port);
1011  // QT Warning - this may not work on Windows
1012  if (!socket.waitForConnected(5000))
1013  isOK = false;
1014  if (isOK)
1015  isOK = (socket.write(requestMessage) > 0);
1016  if (isOK)
1017  // QT Warning - this may not work on Windows
1018  isOK = socket.waitForDisconnected(5000);
1019  if (isOK)
1020  {
1021  *aBuffer = socket.readAll();
1022  // Find the start of the content
1023  QByteArray delim("\r\n\r\n");
1024  int delimLoc=aBuffer->indexOf(delim);
1025  if (delimLoc > -1)
1026  *aBuffer = aBuffer->right
1027  (aBuffer->size()-delimLoc-4);
1028  else
1029  isOK=false;
1030  }
1031  socket.close();
1032  }
1033  else
1034  isOK = false;
1035 
1036  if (deleteInfo)
1037  delete dlInfo;
1038 
1039  if (isOK)
1040  return true;
1041  else
1042  {
1043  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Link Local request failed: %1")
1044  .arg(url.toString()));
1045  return false;
1046  }
1047 
1048 }
1049 #endif
1050 
1054 void MythDownloadManager::cancelDownload(const QString &url, bool block)
1055 {
1056  cancelDownload(QStringList(url), block);
1057 }
1058 
1062 void MythDownloadManager::cancelDownload(const QStringList &urls, bool block)
1063 {
1064  MythDownloadInfo *dlInfo;
1065 
1066  m_infoLock->lock();
1067  foreach (QString url, urls)
1068  {
1069  QMutableListIterator<MythDownloadInfo*> lit(m_downloadQueue);
1070  while (lit.hasNext())
1071  {
1072  lit.next();
1073  dlInfo = lit.value();
1074  if (dlInfo->m_url == url)
1075  {
1076  if (!m_cancellationQueue.contains(dlInfo))
1077  m_cancellationQueue.append(dlInfo);
1078  lit.remove();
1079  }
1080  }
1081 
1082  if (m_downloadInfos.contains(url))
1083  {
1084  dlInfo = m_downloadInfos[url];
1085 
1086  if (!m_cancellationQueue.contains(dlInfo))
1087  m_cancellationQueue.append(dlInfo);
1088 
1089  if (dlInfo->m_reply)
1090  m_downloadReplies.remove(dlInfo->m_reply);
1091 
1092  m_downloadInfos.remove(url);
1093  }
1094  }
1095  m_infoLock->unlock();
1096 
1097  if (QThread::currentThread() == this->thread())
1098  {
1099  downloadCanceled();
1100  return;
1101  }
1102 
1103  // wake-up running thread
1104  m_queueWaitCond.wakeAll();
1105 
1106  if (!block)
1107  return;
1108 
1109  while (!m_cancellationQueue.isEmpty())
1110  {
1111  usleep(50000); // re-test in another 50ms
1112  }
1113 }
1114 
1116 {
1117  QMutexLocker locker(m_infoLock);
1118  MythDownloadInfo *dlInfo;
1119 
1120  QMutableListIterator<MythDownloadInfo*> lit(m_cancellationQueue);
1121  while (lit.hasNext())
1122  {
1123  lit.next();
1124  dlInfo = lit.value();
1125  dlInfo->m_lock.lock();
1126 
1127  if (dlInfo->m_reply)
1128  {
1129  LOG(VB_FILE, LOG_DEBUG,
1130  LOC + QString("Aborting download - user request"));
1131  dlInfo->m_reply->abort();
1132  }
1133  lit.remove();
1134  if (dlInfo->m_done)
1135  {
1136  dlInfo->m_lock.unlock();
1137  continue;
1138  }
1139  dlInfo->m_errorCode = QNetworkReply::OperationCanceledError;
1140  dlInfo->m_done = true;
1141  dlInfo->m_lock.unlock();
1142  }
1143 }
1144 
1150 {
1151  QMutexLocker locker(m_infoLock);
1152  MythDownloadInfo *dlInfo;
1153 
1154  QList <MythDownloadInfo*>::iterator lit = m_downloadQueue.begin();
1155  for (; lit != m_downloadQueue.end(); ++lit)
1156  {
1157  dlInfo = *lit;
1158  if (dlInfo->m_caller == caller)
1159  {
1160  dlInfo->m_caller = NULL;
1161  dlInfo->m_outFile = QString();
1162  dlInfo->m_data = NULL;
1163  }
1164  }
1165 
1166  QMap <QString, MythDownloadInfo*>::iterator mit = m_downloadInfos.begin();
1167  for (; mit != m_downloadInfos.end(); ++mit)
1168  {
1169  dlInfo = mit.value();
1170  if (dlInfo->m_caller == caller)
1171  {
1172  dlInfo->m_caller = NULL;
1173  dlInfo->m_outFile = QString();
1174  dlInfo->m_data = NULL;
1175  }
1176  }
1177 }
1178 
1182 void MythDownloadManager::downloadError(QNetworkReply::NetworkError errorCode)
1183 {
1184  QNetworkReply *reply = (QNetworkReply*)sender();
1185 
1186  LOG(VB_FILE, LOG_DEBUG, LOC + QString("downloadError %1 ")
1187  .arg(errorCode) + reply->errorString() );
1188 
1189  QMutexLocker locker(m_infoLock);
1190  if (!m_downloadReplies.contains(reply))
1191  {
1192  reply->deleteLater();
1193  return;
1194  }
1195 
1196  MythDownloadInfo *dlInfo = m_downloadReplies[reply];
1197 
1198  if (!dlInfo)
1199  return;
1200 
1201  dlInfo->m_errorCode = errorCode;
1202 }
1203 
1209 QUrl MythDownloadManager::redirectUrl(const QUrl& possibleRedirectUrl,
1210  const QUrl& oldRedirectUrl) const
1211 {
1212  LOG(VB_FILE, LOG_DEBUG, LOC + QString("redirectUrl()"));
1213  QUrl redirectUrl;
1214 
1215  if(!possibleRedirectUrl.isEmpty() && possibleRedirectUrl != oldRedirectUrl)
1216  redirectUrl = possibleRedirectUrl;
1217 
1218  return redirectUrl;
1219 }
1220 
1224 void MythDownloadManager::downloadFinished(QNetworkReply* reply)
1225 {
1226  LOG(VB_FILE, LOG_DEBUG, LOC + QString("downloadFinished(%1)")
1227  .arg((long long)reply));
1228 
1229  QMutexLocker locker(m_infoLock);
1230  if (!m_downloadReplies.contains(reply))
1231  {
1232  reply->deleteLater();
1233  return;
1234  }
1235 
1236  MythDownloadInfo *dlInfo = m_downloadReplies[reply];
1237 
1238  if (!dlInfo || !dlInfo->m_reply)
1239  return;
1240 
1241  downloadFinished(dlInfo);
1242 }
1243 
1248 {
1249  if (!dlInfo)
1250  return;
1251 
1252  int statusCode = -1;
1253  static const char dateFormat[] = "ddd, dd MMM yyyy hh:mm:ss 'GMT'";
1254  QNetworkReply *reply = dlInfo->m_reply;
1255 
1256  if (reply)
1257  {
1258  QUrl possibleRedirectUrl =
1259  reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
1260 
1261  if (!possibleRedirectUrl.isEmpty() &&
1262  possibleRedirectUrl.isValid() &&
1263  possibleRedirectUrl.isRelative()) // Turn relative Url to absolute
1264  possibleRedirectUrl = reply->url().resolved(possibleRedirectUrl);
1265 
1266  dlInfo->m_redirectedTo =
1267  redirectUrl(possibleRedirectUrl, dlInfo->m_redirectedTo);
1268 
1269  QVariant status =
1270  reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
1271  if (status.isValid())
1272  statusCode = status.toInt();
1273  }
1274 
1275  if(reply && !dlInfo->m_redirectedTo.isEmpty() &&
1276  ((dlInfo->m_requestType != kRequestPost) ||
1277  (statusCode == 301 || statusCode == 302 ||
1278  statusCode == 303)))
1279  {
1280  LOG(VB_FILE, LOG_DEBUG, LOC +
1281  QString("downloadFinished(%1): Redirect: %2 -> %3")
1282  .arg((long long)dlInfo)
1283  .arg(reply->url().toString())
1284  .arg(dlInfo->m_redirectedTo.toString()));
1285 
1286  if (dlInfo->m_data)
1287  dlInfo->m_data->clear();
1288 
1289  dlInfo->m_bytesReceived = 0;
1290  dlInfo->m_bytesTotal = 0;
1291 
1292  QNetworkRequest request(dlInfo->m_redirectedTo);
1293 
1294  if (dlInfo->m_reload)
1295  {
1296  request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
1297  QNetworkRequest::AlwaysNetwork);
1298  }
1299  else if (dlInfo->m_preferCache)
1300  {
1301  request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
1302  QNetworkRequest::PreferCache);
1303  }
1304 
1305  request.setRawHeader("User-Agent",
1306  "MythDownloadManager v" MYTH_BINARY_VERSION);
1307 
1308  switch (dlInfo->m_requestType)
1309  {
1310  case kRequestHead :
1311  dlInfo->m_reply = m_manager->head(request);
1312  break;
1313  case kRequestGet :
1314  default:
1315  dlInfo->m_reply = m_manager->get(request);
1316  break;
1317  }
1318 
1319  m_downloadReplies[dlInfo->m_reply] = dlInfo;
1320 
1321  connect(dlInfo->m_reply, SIGNAL(error(QNetworkReply::NetworkError)),
1322  this, SLOT(downloadError(QNetworkReply::NetworkError)));
1323  connect(dlInfo->m_reply, SIGNAL(downloadProgress(qint64, qint64)),
1324  this, SLOT(downloadProgress(qint64, qint64)));
1325 
1326  m_downloadReplies.remove(reply);
1327  reply->deleteLater();
1328  }
1329  else
1330  {
1331  LOG(VB_FILE, LOG_DEBUG, QString("downloadFinished(%1): COMPLETE: %2")
1332  .arg((long long)dlInfo).arg(dlInfo->m_url));
1333 
1334  // HACK Insert a Date header into the cached metadata if one doesn't
1335  // already exist
1336  QUrl fileUrl = dlInfo->m_url;
1337  QString redirectLoc;
1338  int limit = 0;
1339  while (!(redirectLoc = getHeader(fileUrl, "Location")).isNull())
1340  {
1341  if (limit == CACHE_REDIRECTION_LIMIT)
1342  {
1343  LOG(VB_GENERAL, LOG_WARNING, QString("Cache Redirection limit "
1344  "reached for %1")
1345  .arg(fileUrl.toString()));
1346  return;
1347  }
1348  fileUrl.setUrl(redirectLoc);
1349  limit++;
1350  }
1351 
1352  m_infoLock->lock();
1353  QNetworkCacheMetaData urlData = m_manager->cache()->metaData(fileUrl);
1354  m_infoLock->unlock();
1355  if (getHeader(urlData, "Date").isNull())
1356  {
1357  QNetworkCacheMetaData::RawHeaderList headers = urlData.rawHeaders();
1358  QNetworkCacheMetaData::RawHeader newheader;
1359  QDateTime now = MythDate::current();
1360  newheader = QNetworkCacheMetaData::RawHeader("Date",
1361  now.toString(dateFormat).toLatin1());
1362  headers.append(newheader);
1363  urlData.setRawHeaders(headers);
1364  m_infoLock->lock();
1365  m_manager->cache()->updateMetaData(urlData);
1366  m_infoLock->unlock();
1367  }
1368  // End HACK
1369 
1370  dlInfo->m_redirectedTo.clear();
1371 
1372  int dataSize = -1;
1373 
1374  // If we downloaded via the QNetworkAccessManager
1375  // AND the caller isn't handling the reply directly
1376  if (reply && dlInfo->m_processReply)
1377  {
1378  bool append = (!dlInfo->m_syncMode && dlInfo->m_caller);
1379  QByteArray data = reply->readAll();
1380  dataSize = data.size();
1381 
1382  if (append)
1383  dlInfo->m_bytesReceived += dataSize;
1384  else
1385  dlInfo->m_bytesReceived = dataSize;
1386 
1387  dlInfo->m_bytesTotal = dlInfo->m_bytesReceived;
1388 
1389  if (dlInfo->m_data)
1390  {
1391  if (append)
1392  dlInfo->m_data->append(data);
1393  else
1394  *dlInfo->m_data = data;
1395  }
1396  else if (!dlInfo->m_outFile.isEmpty())
1397  {
1398  saveFile(dlInfo->m_outFile, data, append);
1399  }
1400  }
1401  else if (!reply) // If we downloaded via RemoteFile
1402  {
1403  if (dlInfo->m_data)
1404  {
1405  (*dlInfo->m_data) = dlInfo->m_privData;
1406  }
1407  else if (!dlInfo->m_outFile.isEmpty())
1408  {
1409  saveFile(dlInfo->m_outFile, dlInfo->m_privData);
1410  }
1411  dlInfo->m_bytesReceived += dataSize;
1412  dlInfo->m_bytesTotal = dlInfo->m_bytesReceived;
1413  }
1414  // else we downloaded via QNetworkAccessManager
1415  // AND the caller is handling the reply
1416 
1417  m_infoLock->lock();
1418  if (!m_downloadInfos.remove(dlInfo->m_url))
1419  LOG(VB_GENERAL, LOG_ERR, LOC +
1420  QString("ERROR download finished but failed to remove url: %1")
1421  .arg(dlInfo->m_url));
1422 
1423  if (reply)
1424  m_downloadReplies.remove(reply);
1425  m_infoLock->unlock();
1426 
1427  dlInfo->SetDone(true);
1428 
1429  if (!dlInfo->m_syncMode)
1430  {
1431  if (dlInfo->m_caller)
1432  {
1433  LOG(VB_FILE, LOG_DEBUG, QString("downloadFinished(%1): "
1434  "COMPLETE: %2, sending event to caller")
1435  .arg((long long)dlInfo).arg(dlInfo->m_url));
1436 
1437  QStringList args;
1438  args << dlInfo->m_url;
1439  args << dlInfo->m_outFile;
1440  args << QString::number(dlInfo->m_bytesTotal);
1441  // placeholder for error string
1442  args << (reply ? reply->errorString() : QString());
1443  args << QString::number((int)(reply ? reply->error() :
1444  dlInfo->m_errorCode));
1445 
1446  QCoreApplication::postEvent(dlInfo->m_caller,
1447  new MythEvent("DOWNLOAD_FILE FINISHED", args));
1448  }
1449 
1450  delete dlInfo;
1451  }
1452 
1453  m_queueWaitCond.wakeAll();
1454  }
1455 }
1456 
1462 void MythDownloadManager::downloadProgress(qint64 bytesReceived,
1463  qint64 bytesTotal)
1464 {
1465  QNetworkReply *reply = (QNetworkReply*)sender();
1466 
1467  LOG(VB_FILE, LOG_DEBUG, LOC +
1468  QString("downloadProgress(%1, %2) (for reply %3)")
1469  .arg(bytesReceived).arg(bytesTotal).arg((long long)reply));
1470 
1471  QMutexLocker locker(m_infoLock);
1472  if (!m_downloadReplies.contains(reply))
1473  return;
1474 
1475  MythDownloadInfo *dlInfo = m_downloadReplies[reply];
1476 
1477  if (!dlInfo)
1478  return;
1479 
1480  dlInfo->m_lastStat = MythDate::current();
1481 
1482  LOG(VB_FILE, LOG_DEBUG, LOC +
1483  QString("downloadProgress: %1 to %2 is at %3 of %4 bytes downloaded")
1484  .arg(dlInfo->m_url).arg(dlInfo->m_outFile)
1485  .arg(bytesReceived).arg(bytesTotal));
1486 
1487  if (!dlInfo->m_syncMode && dlInfo->m_caller)
1488  {
1489  LOG(VB_FILE, LOG_DEBUG, QString("downloadProgress(%1): "
1490  "sending event to caller")
1491  .arg(reply->url().toString()));
1492 
1493  bool appendToFile = (dlInfo->m_bytesReceived != 0);
1494  QByteArray data = reply->readAll();
1495  if (!dlInfo->m_outFile.isEmpty())
1496  saveFile(dlInfo->m_outFile, data, appendToFile);
1497 
1498  if (dlInfo->m_data)
1499  dlInfo->m_data->append(data);
1500 
1501  dlInfo->m_bytesReceived = bytesReceived;
1502  dlInfo->m_bytesTotal = bytesTotal;
1503 
1504  QStringList args;
1505  args << dlInfo->m_url;
1506  args << dlInfo->m_outFile;
1507  args << QString::number(bytesReceived);
1508  args << QString::number(bytesTotal);
1509 
1510  QCoreApplication::postEvent(dlInfo->m_caller,
1511  new MythEvent("DOWNLOAD_FILE UPDATE", args));
1512  }
1513 }
1514 
1522 bool MythDownloadManager::saveFile(const QString &outFile,
1523  const QByteArray &data,
1524  const bool append)
1525 {
1526  if (outFile.isEmpty() || !data.size())
1527  return false;
1528 
1529  QFile file(outFile);
1530  QFileInfo fileInfo(outFile);
1531  QDir qdir(fileInfo.absolutePath());
1532 
1533  if (!qdir.exists() && !qdir.mkpath(fileInfo.absolutePath()))
1534  {
1535  LOG(VB_GENERAL, LOG_ERR, QString("Failed to create: '%1'")
1536  .arg(fileInfo.absolutePath()));
1537  return false;
1538  }
1539 
1540  QIODevice::OpenMode mode = QIODevice::Unbuffered|QIODevice::WriteOnly;
1541  if (append)
1542  mode |= QIODevice::Append;
1543 
1544  if (!file.open(mode))
1545  {
1546  LOG(VB_GENERAL, LOG_ERR, QString("Failed to open: '%1'") .arg(outFile));
1547  return false;
1548  }
1549 
1550  off_t offset = 0;
1551  size_t remaining = data.size();
1552  uint failure_cnt = 0;
1553  while ((remaining > 0) && (failure_cnt < 5))
1554  {
1555  ssize_t written = file.write(data.data() + offset, remaining);
1556  if (written < 0)
1557  {
1558  failure_cnt++;
1559  usleep(50000);
1560  continue;
1561  }
1562 
1563  failure_cnt = 0;
1564  offset += written;
1565  remaining -= written;
1566  }
1567 
1568  if (remaining > 0)
1569  return false;
1570 
1571  return true;
1572 }
1573 
1578 QDateTime MythDownloadManager::GetLastModified(const QString &url)
1579 {
1580  // If the header has not expired and
1581  // the last modification date is less than 1 hours old or if
1582  // the cache object is less than 20 minutes old,
1583  // then use the cached header otherwise redownload the header
1584 
1585  static const char dateFormat[] = "ddd, dd MMM yyyy hh:mm:ss 'GMT'";
1586  LOG(VB_FILE, LOG_DEBUG, LOC + QString("GetLastModified('%1')").arg(url));
1587  QDateTime result;
1588 
1589  QDateTime now = MythDate::current();
1590 
1591  QUrl cacheUrl = QUrl(url);
1592 
1593  // Deal with redirects, we want the cached data for the final url
1594  QString redirectLoc;
1595  int limit = 0;
1596  while (!(redirectLoc = getHeader(cacheUrl, "Location")).isNull())
1597  {
1598  if (limit == CACHE_REDIRECTION_LIMIT)
1599  {
1600  LOG(VB_GENERAL, LOG_WARNING, QString("Cache Redirection limit "
1601  "reached for %1")
1602  .arg(cacheUrl.toString()));
1603  return result;
1604  }
1605  cacheUrl.setUrl(redirectLoc);
1606  limit++;
1607  }
1608 
1609  m_infoLock->lock();
1610  QNetworkCacheMetaData urlData = m_manager->cache()->metaData(cacheUrl);
1611  m_infoLock->unlock();
1612 
1613  if (urlData.isValid() &&
1614  ((!urlData.expirationDate().isValid()) ||
1615  (urlData.expirationDate().secsTo(now) < 0)))
1616  {
1617  if (QDateTime(urlData.lastModified().toUTC()).secsTo(now) <= 3600) // 1 Hour
1618  {
1619  result = urlData.lastModified().toUTC();
1620  }
1621  else
1622  {
1623  QString date = getHeader(urlData, "Date");
1624  if (!date.isNull())
1625  {
1626  QDateTime loadDate =
1627  MythDate::fromString(date, dateFormat);
1628  loadDate.setTimeSpec(Qt::UTC);
1629  if (loadDate.secsTo(now) <= 1200) // 20 Minutes
1630  {
1631  result = urlData.lastModified().toUTC();
1632  }
1633  }
1634  }
1635  }
1636 
1637  if (!result.isValid())
1638  {
1639  MythDownloadInfo *dlInfo = new MythDownloadInfo;
1640  dlInfo->m_url = url;
1641  dlInfo->m_syncMode = true;
1642  // Head request, we only want to inspect the headers
1643  dlInfo->m_requestType = kRequestHead;
1644 
1645  if (downloadNow(dlInfo, false))
1646  {
1647  if (dlInfo->m_reply)
1648  {
1649  QVariant lastMod =
1650  dlInfo->m_reply->header(
1651  QNetworkRequest::LastModifiedHeader);
1652  if (lastMod.isValid())
1653  result = lastMod.toDateTime().toUTC();
1654  }
1655 
1656  // downloadNow() will set a flag to trigger downloadFinished()
1657  // to delete the dlInfo if the download times out
1658  delete dlInfo;
1659  }
1660  }
1661 
1662  LOG(VB_FILE, LOG_DEBUG, LOC + QString("GetLastModified('%1'): Result %2")
1663  .arg(url).arg(result.toString()));
1664 
1665  return result;
1666 }
1667 
1668 
1673 {
1674  QMutexLocker locker(&m_cookieLock);
1675 
1676  MythCookieJar *jar = new MythCookieJar;
1677  jar->load(filename);
1678  m_manager->setCookieJar(jar);
1679 }
1680 
1685 {
1686  QMutexLocker locker(&m_cookieLock);
1687 
1688  if (!m_manager->cookieJar())
1689  return;
1690 
1691  MythCookieJar *jar = static_cast<MythCookieJar *>(m_manager->cookieJar());
1692  jar->save(filename);
1693 }
1694 
1695 void MythDownloadManager::setCookieJar(QNetworkCookieJar *cookieJar)
1696 {
1697  QMutexLocker locker(&m_cookieLock);
1698  m_manager->setCookieJar(cookieJar);
1699 }
1700 
1704 QNetworkCookieJar *MythDownloadManager::copyCookieJar(void)
1705 {
1706  QMutexLocker locker(&m_cookieLock);
1707 
1708  if (!m_manager->cookieJar())
1709  return NULL;
1710 
1711  MythCookieJar *inJar = static_cast<MythCookieJar *>(m_manager->cookieJar());
1712  MythCookieJar *outJar = new MythCookieJar(*inJar);
1713 
1714  return static_cast<QNetworkCookieJar *>(outJar);
1715 }
1716 
1720 void MythDownloadManager::refreshCookieJar(QNetworkCookieJar *jar)
1721 {
1722  QMutexLocker locker(&m_cookieLock);
1723  if (m_inCookieJar)
1724  delete m_inCookieJar;
1725 
1726  MythCookieJar *inJar = static_cast<MythCookieJar *>(jar);
1727  MythCookieJar *outJar = new MythCookieJar(*inJar);
1728  m_inCookieJar = static_cast<QNetworkCookieJar *>(outJar);
1729 
1730  QMutexLocker locker2(&m_queueWaitLock);
1731  m_queueWaitCond.wakeAll();
1732 }
1733 
1737 {
1738  QMutexLocker locker(&m_cookieLock);
1739 
1740  MythCookieJar *inJar = static_cast<MythCookieJar *>(m_inCookieJar);
1741  MythCookieJar *outJar = new MythCookieJar(*inJar);
1742  m_manager->setCookieJar(static_cast<QNetworkCookieJar *>(outJar));
1743 
1744  delete m_inCookieJar;
1745  m_inCookieJar = NULL;
1746 }
1747 
1748 QString MythDownloadManager::getHeader(const QUrl& url, const QString& header)
1749 {
1750  if (!m_manager || !m_manager->cache())
1751  return QString::null;
1752 
1753  m_infoLock->lock();
1754  QNetworkCacheMetaData metadata = m_manager->cache()->metaData(url);
1755  m_infoLock->unlock();
1756 
1757  return getHeader(metadata, header);
1758 }
1759 
1765 QString MythDownloadManager::getHeader(const QNetworkCacheMetaData &cacheData,
1766  const QString& header)
1767 {
1768  QNetworkCacheMetaData::RawHeaderList headers = cacheData.rawHeaders();
1769  bool found = false;
1770  QNetworkCacheMetaData::RawHeaderList::iterator it = headers.begin();
1771  for (; !found && it != headers.end(); ++it)
1772  {
1773  if (QString((*it).first) == header)
1774  {
1775  found = true;
1776  return QString((*it).second);
1777  }
1778  }
1779 
1780  return QString::null;
1781 }
1782 
1783 
1788 {
1789  const QList<QNetworkCookie> cookieList = old.allCookies();
1790  setAllCookies(cookieList);
1791 }
1792 
1796 {
1797 }
1798 
1802 void MythCookieJar::load(const QString &filename)
1803 {
1804  LOG(VB_GENERAL, LOG_DEBUG, QString("MythCookieJar: loading cookies from: %1").arg(filename));
1805 
1806  QFile f(filename);
1807  if (!f.open(QIODevice::ReadOnly))
1808  {
1809  LOG(VB_GENERAL, LOG_WARNING, QString("MythCookieJar::load() failed to open file for reading: %1").arg(filename));
1810  return;
1811  }
1812 
1813  QList<QNetworkCookie> cookieList;
1814  QTextStream stream(&f);
1815  while (!stream.atEnd())
1816  {
1817  QString cookie = stream.readLine();
1818  cookieList << QNetworkCookie::parseCookies(cookie.toLocal8Bit());
1819  }
1820 
1821  setAllCookies(cookieList);
1822 }
1823 
1827 void MythCookieJar::save(const QString &filename)
1828 {
1829  LOG(VB_GENERAL, LOG_DEBUG, QString("MythCookieJar: saving cookies to: %1").arg(filename));
1830 
1831  QFile f(filename);
1832  if (!f.open(QIODevice::WriteOnly))
1833  {
1834  LOG(VB_GENERAL, LOG_ERR, QString("MythCookieJar::save() failed to open file for writing: %1").arg(filename));
1835  return;
1836  }
1837 
1838  QList<QNetworkCookie> cookieList = allCookies();
1839  QTextStream stream(&f);
1840 
1841  for (QList<QNetworkCookie>::iterator it = cookieList.begin();
1842  it != cookieList.end(); ++it)
1843  {
1844  stream << (*it).toRawForm() << endl;
1845  }
1846 }
1847 
1848 
1849 /* 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 downloadNowLinkLocal(MythDownloadInfo *dlInfo, bool deleteInfo)
Download blocking methods with link-local address.
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.
static bool resolveLinkLocal(QString &host, int port, int timeLimit=30000)
Convenience method to resolve link-local address.
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.