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