MythTV  master
mythsingledownload.cpp
Go to the documentation of this file.
1 
2 #include "mythsingledownload.h"
3 #include "mythversion.h"
4 #include "mythlogging.h"
5 
6 /*
7  * For simple one-at-a-time downloads, this routine (found at
8  * http://www.insanefactory.com/2012/08/qt-http-request-in-a-single-function/
9  * ) works well.
10  */
11 
12 #define LOC QString("MythSingleDownload: ")
13 
14 bool MythSingleDownload::DownloadURL(const QUrl &url, QByteArray *buffer,
15  uint timeout, uint redirs, qint64 maxsize, QString *final_url)
16 {
17  m_lock.lock();
18 
19  QEventLoop event_loop;
20  m_buffer = buffer;
21 
22  // the HTTP request
23  QNetworkRequest req(url);
24  req.setRawHeader("User-Agent",
25  "MythTV v" MYTH_BINARY_VERSION " MythSingleDownload");
26  m_replylock.lock();
27  m_reply = m_mgr.get(req);
28  m_replylock.unlock();
29 
30  req.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
31  QNetworkRequest::AlwaysNetwork);
32 
33  // "quit()" the event-loop, when the network request "finished()"
34  connect(&m_timer, SIGNAL(timeout()), &event_loop, SLOT(quit()));
35  connect(m_reply, SIGNAL(finished()), &event_loop, SLOT(quit()));
36  connect(m_reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(Progress(qint64,qint64)));
37 
38  // Configure timeout and size limit
39  m_maxsize = maxsize;
40  m_timer.setSingleShot(true);
41  m_timer.start(timeout); // 30 secs. by default
42 
43  bool ret = event_loop.exec() != 0; // blocks stack until quit() is called
44 
45  disconnect(&m_timer, SIGNAL(timeout()), &event_loop, SLOT(quit()));
46  disconnect(m_reply, SIGNAL(finished()), &event_loop, SLOT(quit()));
47  disconnect(m_reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(Progress(qint64,qint64)));
48 
49  if (ret)
50  {
51  LOG(VB_GENERAL, LOG_ERR, LOC + "eventloop failed");
52  }
53 
54  m_replylock.lock();
55  if (!m_timer.isActive())
56  {
57  m_errorstring = "timed-out";
58  m_reply->abort();
59  ret = false;
60  }
61  else
62  {
63  m_timer.stop();
64  m_errorcode = m_reply->error();
65 
66  QString redir = m_reply->attribute(
67  QNetworkRequest::RedirectionTargetAttribute).toUrl().toString();
68 
69  if (redir.length())
70  {
71  if (redirs <= 3)
72  {
73  LOG(VB_GENERAL, LOG_INFO, QString("%1 -> %2").arg(url.toString()).arg(redir));
74  m_replylock.unlock();
75  m_lock.unlock();
76  return DownloadURL(redir, buffer, timeout, redirs + 1, maxsize, final_url);
77  }
78 
79  LOG(VB_GENERAL, LOG_ERR, QString("%1: too many redirects").arg(url.toString()));
80  ret = false;
81  }
82  else if (m_errorcode == QNetworkReply::NoError)
83  {
84  if (final_url != nullptr)
85  *final_url = url.toString();
86  *m_buffer += m_reply->readAll();
87  m_errorstring.clear();
88  ret = true;
89  }
90  else
91  {
92  m_errorstring = m_reply->errorString();
93  ret = false;
94  }
95  }
96 
97  m_replylock.unlock();
98  m_lock.unlock();
99 
100  delete m_reply;
101  m_reply = nullptr;
102  m_buffer = nullptr;
103 
104  return ret;
105 }
106 
108 {
109  QMutexLocker replylock(&m_replylock);
110  if (m_reply)
111  {
112  LOG(VB_GENERAL, LOG_INFO, LOC + "Aborting download");
113  m_reply->abort();
114  }
115 }
116 
117 void MythSingleDownload::Progress(qint64 bytesRead, qint64 /*totalBytes*/)
118 {
119  if (m_maxsize && bytesRead>=m_maxsize)
120  {
121  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Reached specified max file size (%1 bytes)").arg(m_maxsize));
122  {
123  QMutexLocker replylock(&m_replylock);
124  *m_buffer += m_reply->read(m_maxsize);
125  }
126  m_maxsize=0;
127  Cancel();
128  }
129 }
#define LOC
unsigned int uint
Definition: compat.h:140
QNetworkReply::NetworkError m_errorcode
bool DownloadURL(const QUrl &url, QByteArray *buffer, uint timeout=30000, uint redirs=0, qint64 maxsize=0, QString *final_url=nullptr)
QNetworkAccessManager m_mgr
void Progress(qint64 bytesRead, qint64 totalBytes)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
#define MYTH_BINARY_VERSION
Update this whenever the plug-in ABI changes.
Definition: mythversion.h:16
QNetworkReply * m_reply