2 #include <QCoreApplication>
8 #include <QNetworkCookieJar>
9 #include <QNetworkCookie>
10 #include <QAuthenticator>
11 #include <QTextStream>
12 #include <QNetworkProxy>
13 #include <QMutexLocker>
34 #define LOC QString("DownloadManager: ")
35 #define CACHE_REDIRECTION_LIMIT 10
47 m_request(NULL), m_reply(NULL), m_data(NULL),
50 m_processReply(
true), m_done(
false), m_bytesReceived(0),
51 m_bytesTotal(0), m_lastStat(MythDate::
current()),
52 m_authCallback(NULL), m_authArg(NULL),
53 m_headers(NULL), m_errorCode(QNetworkReply::NoError)
55 qRegisterMetaType<QNetworkReply::NetworkError>(
"QNetworkReply::NetworkError");
62 if (m_reply && m_processReply)
63 m_reply->deleteLater();
74 QMutexLocker lock(&m_lock);
78 void SetDone(
bool done)
80 QMutexLocker lock(&m_lock);
119 void load(
const QString &filename);
120 void save(
const QString &filename);
142 ok = rf->
SaveAs(m_dlInfo->m_privData);
146 m_dlInfo->m_errorCode = QNetworkReply::UnknownNetworkError;
148 m_dlInfo->m_bytesReceived = m_dlInfo->m_privData.size();
149 m_dlInfo->m_bytesTotal = m_dlInfo->m_bytesReceived;
151 m_parent->downloadFinished(m_dlInfo);
211 m_infoLock(new QMutex(QMutex::Recursive)),
241 bool downloading =
false;
242 bool itemsInQueue =
false;
243 bool waitAnyway =
false;
250 m_manager =
new QNetworkAccessManager(
this);
254 QCoreApplication::applicationName() +
"-" +
266 QObject::connect(
m_manager, SIGNAL(finished(QNetworkReply*)),
this,
274 LOG(VB_GENERAL, LOG_DEBUG,
"Updating DLManager's Cookie Jar");
283 QCoreApplication::processEvents();
285 if (!itemsInQueue || waitAnyway)
308 QUrl qurl(dlInfo->
m_url);
321 if (dlInfo->
m_url.startsWith(
"myth://"))
349 const QString &
dest, QByteArray *
data,
383 const QString &
dest, QByteArray *
data,
387 const QHash<QByteArray, QByteArray> *headers)
410 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"preCache('%1')").arg(url));
411 queueItem(url, NULL, QString(), NULL, NULL);
425 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"queueDownload('%1', '%2', %3)")
426 .arg(url).arg(dest).arg((
long long)caller));
440 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"queueDownload('%1', '%2', %3)")
441 .arg(req->url().toString()).arg((
long long)
data)
442 .arg((
long long)caller));
444 queueItem(req->url().toString(), req, QString(),
data, caller);
489 QNetworkReply *reply = dlInfo->
m_reply;
510 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"download('%1', '%2')")
511 .arg(req->url().toString()).arg((
long long)
data));
525 const bool reload,
AuthCallback authCallback,
void *authArg,
526 const QHash<QByteArray, QByteArray> *headers)
542 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"queuePost('%1', '%2')")
543 .arg(url).arg((
long long)data));
547 LOG(VB_GENERAL, LOG_ERR,
LOC +
"queuePost(), data is NULL!");
563 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"queuePost('%1', '%2')")
564 .arg(req->url().toString()).arg((
long long)
data));
568 LOG(VB_GENERAL, LOG_ERR,
LOC +
"queuePost(), data is NULL!");
572 queueItem(req->url().toString(), req, QString(),
data, caller,
583 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"post('%1', '%2')")
584 .arg(url).arg((
long long)data));
588 LOG(VB_GENERAL, LOG_ERR,
LOC +
"post(), data is NULL!");
602 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"post('%1', '%2')")
603 .arg(req->url().toString()).arg((
long long)
data));
607 LOG(VB_GENERAL, LOG_ERR,
LOC +
"post(), data is NULL!");
625 const QHash<QByteArray, QByteArray> *headers)
627 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"postAuth('%1', '%2')")
628 .arg(url).arg((
long long)data));
632 LOG(VB_GENERAL, LOG_ERR,
LOC +
"postAuth(), data is NULL!");
658 static const char dateFormat[] =
"ddd, dd MMM yyyy hh:mm:ss 'GMT'";
659 QUrl qurl(dlInfo->
m_url);
660 QNetworkRequest request;
669 request.setUrl(qurl);
680 while (!(redirectLoc =
getHeader(qurl,
"Location")).isNull())
682 if (limit == CACHE_REDIRECTION_LIMIT)
684 LOG(VB_GENERAL, LOG_WARNING, QString(
"Cache Redirection limit "
686 .arg(qurl.toString()));
689 qurl.setUrl(redirectLoc);
693 LOG(VB_NETWORK, LOG_DEBUG, QString(
"Checking cache for %1")
694 .arg(qurl.toString()));
697 QNetworkCacheMetaData urlData =
m_manager->cache()->metaData(qurl);
699 if ((urlData.isValid()) &&
700 ((!urlData.expirationDate().isValid()) ||
701 (QDateTime(urlData.expirationDate().toUTC()).secsTo(now) < 10)))
703 QString dateString =
getHeader(urlData,
"Date");
705 if (!dateString.isNull())
709 loadDate.setTimeSpec(Qt::UTC);
710 if (loadDate.secsTo(now) <= 720)
713 LOG(VB_NETWORK, LOG_DEBUG, QString(
"Preferring cache for %1")
714 .arg(qurl.toString()));
721 request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
722 QNetworkRequest::PreferCache);
724 request.setRawHeader(
"User-Agent",
725 "MythTV v" MYTH_BINARY_VERSION
" MythDownloadManager");
729 QHash<QByteArray, QByteArray>::const_iterator it =
731 for ( ; it != dlInfo->
m_headers->constEnd(); ++it )
733 if (!it.key().isEmpty() && !it.value().isEmpty())
735 request.setRawHeader(it.key(), it.value());
758 connect(
m_manager, SIGNAL(authenticationRequired(QNetworkReply *,
760 this, SLOT(
authCallback(QNetworkReply *, QAuthenticator *)));
763 connect(dlInfo->
m_reply, SIGNAL(
error(QNetworkReply::NetworkError)),
this,
774 QAuthenticator *authenticator)
786 LOG(VB_FILE, LOG_DEBUG,
"Calling auth callback");
814 while ((!dlInfo->
IsDone()) &&
816 (((!dlInfo->
m_url.startsWith(
"myth://")) &&
818 ((dlInfo->
m_url.startsWith(
"myth://")) &&
827 bool done = dlInfo->
IsDone();
829 done && (dlInfo->
m_errorCode == QNetworkReply::NoError);
838 LOG(VB_FILE, LOG_DEBUG,
839 LOC + QString(
"Aborting download - lack of data transfer"));
863 while (lit.hasNext())
866 dlInfo = lit.value();
867 if (dlInfo->
m_url == url)
872 LOG(VB_FILE, LOG_DEBUG,
873 LOC + QString(
"Aborting download - user request"));
878 dlInfo->
m_errorCode = QNetworkReply::OperationCanceledError;
889 LOG(VB_FILE, LOG_DEBUG,
890 LOC + QString(
"Aborting download - user request"));
896 dlInfo->
m_errorCode = QNetworkReply::OperationCanceledError;
923 QMap <QString, MythDownloadInfo*>::iterator mit =
m_downloadInfos.begin();
926 dlInfo = mit.value();
941 QNetworkReply *reply = (QNetworkReply*)sender();
943 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"downloadError %1 ")
944 .arg(errorCode) + reply->errorString() );
949 reply->deleteLater();
967 const QUrl& oldRedirectUrl)
const
969 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"redirectUrl()"));
972 if(!possibleRedirectUrl.isEmpty() && possibleRedirectUrl != oldRedirectUrl)
973 redirectUrl = possibleRedirectUrl;
983 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"downloadFinished(%1)")
984 .arg((
long long)reply));
989 reply->deleteLater();
995 if (!dlInfo || !dlInfo->
m_reply)
1009 static const char dateFormat[] =
"ddd, dd MMM yyyy hh:mm:ss 'GMT'";
1010 QNetworkReply *reply = dlInfo->
m_reply;
1014 QUrl possibleRedirectUrl =
1015 reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
1023 LOG(VB_FILE, LOG_DEBUG,
LOC +
1024 QString(
"downloadFinished(%1): Redirect: %2 -> %3")
1025 .arg((
long long)dlInfo)
1026 .arg(reply->url().toString())
1027 .arg(dlInfo->m_redirectedTo.toString()));
1029 QNetworkRequest request(dlInfo->m_redirectedTo);
1030 if (dlInfo->m_preferCache)
1031 request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
1032 QNetworkRequest::PreferCache);
1033 request.setRawHeader(
"User-Agent",
1034 "MythDownloadManager v" MYTH_BINARY_VERSION);
1036 switch (dlInfo->m_requestType)
1039 dlInfo->m_reply =
m_manager->post(request, *dlInfo->m_data);
1042 dlInfo->m_reply =
m_manager->head(request);
1046 dlInfo->m_reply =
m_manager->get(request);
1052 connect(dlInfo->m_reply, SIGNAL(
error(QNetworkReply::NetworkError)),
1058 reply->deleteLater();
1062 LOG(VB_FILE, LOG_DEBUG, QString(
"downloadFinished(%1): COMPLETE: %2")
1063 .arg((
long long)dlInfo).arg(dlInfo->m_url));
1067 QUrl fileUrl = dlInfo->m_url;
1068 QString redirectLoc;
1070 while (!(redirectLoc =
getHeader(fileUrl,
"Location")).isNull())
1072 if (limit == CACHE_REDIRECTION_LIMIT)
1074 LOG(VB_GENERAL, LOG_WARNING, QString(
"Cache Redirection limit "
1076 .arg(fileUrl.toString()));
1079 fileUrl.setUrl(redirectLoc);
1084 QNetworkCacheMetaData urlData =
m_manager->cache()->metaData(fileUrl);
1086 if (
getHeader(urlData,
"Date").isNull())
1088 QNetworkCacheMetaData::RawHeaderList headers = urlData.rawHeaders();
1089 QNetworkCacheMetaData::RawHeader newheader;
1091 newheader = QNetworkCacheMetaData::RawHeader(
"Date",
1092 now.toString(dateFormat).toLatin1());
1093 headers.append(newheader);
1094 urlData.setRawHeaders(headers);
1096 m_manager->cache()->updateMetaData(urlData);
1101 dlInfo->m_redirectedTo.clear();
1107 if (reply && dlInfo->m_processReply)
1109 bool append = (!dlInfo->m_syncMode && dlInfo->m_caller);
1110 QByteArray
data = reply->readAll();
1111 dataSize = data.size();
1114 dlInfo->m_bytesReceived += dataSize;
1116 dlInfo->m_bytesReceived = dataSize;
1118 dlInfo->m_bytesTotal = dlInfo->m_bytesReceived;
1123 dlInfo->m_data->append(data);
1125 *dlInfo->m_data =
data;
1127 else if (!dlInfo->m_outFile.isEmpty())
1129 saveFile(dlInfo->m_outFile, data, append);
1136 (*dlInfo->m_data) = dlInfo->m_privData;
1138 else if (!dlInfo->m_outFile.isEmpty())
1140 saveFile(dlInfo->m_outFile, dlInfo->m_privData);
1142 dlInfo->m_bytesReceived += dataSize;
1143 dlInfo->m_bytesTotal = dlInfo->m_bytesReceived;
1152 dlInfo->SetDone(
true);
1154 if (!dlInfo->m_syncMode)
1156 if (dlInfo->m_caller)
1158 LOG(VB_FILE, LOG_DEBUG, QString(
"downloadFinished(%1): "
1159 "COMPLETE: %2, sending event to caller")
1160 .arg((
long long)dlInfo).arg(dlInfo->m_url));
1163 args << dlInfo->m_url;
1164 args << dlInfo->m_outFile;
1165 args << QString::number(dlInfo->m_bytesTotal);
1167 args << (reply ? reply->errorString() : QString());
1168 args << QString::number((
int)(reply ? reply->error() :
1169 dlInfo->m_errorCode));
1171 QCoreApplication::postEvent(dlInfo->m_caller,
1172 new MythEvent(
"DOWNLOAD_FILE FINISHED", args));
1191 QNetworkReply *reply = (QNetworkReply*)sender();
1193 LOG(VB_FILE, LOG_DEBUG,
LOC +
1194 QString(
"downloadProgress(%1, %2) (for reply %3)")
1195 .arg(bytesReceived).arg(bytesTotal).arg((
long long)reply));
1208 LOG(VB_FILE, LOG_DEBUG,
LOC +
1209 QString(
"downloadProgress: %1 to %2 is at %3 of %4 bytes downloaded")
1211 .arg(bytesReceived).arg(bytesTotal));
1215 LOG(VB_FILE, LOG_DEBUG, QString(
"downloadProgress(%1): "
1216 "sending event to caller")
1217 .arg(reply->url().toString()));
1220 QByteArray
data = reply->readAll();
1225 dlInfo->
m_data->append(data);
1231 args << dlInfo->
m_url;
1233 args << QString::number(bytesReceived);
1234 args << QString::number(bytesTotal);
1236 QCoreApplication::postEvent(dlInfo->
m_caller,
1237 new MythEvent(
"DOWNLOAD_FILE UPDATE", args));
1249 const QByteArray &
data,
1252 if (outFile.isEmpty() || !data.size())
1255 QFile file(outFile);
1256 QFileInfo fileInfo(outFile);
1257 QDir qdir(fileInfo.absolutePath());
1259 if (!qdir.exists() && !qdir.mkpath(fileInfo.absolutePath()))
1261 LOG(VB_GENERAL, LOG_ERR, QString(
"Failed to create: '%1'")
1262 .arg(fileInfo.absolutePath()));
1266 QIODevice::OpenMode
mode = QIODevice::Unbuffered|QIODevice::WriteOnly;
1268 mode |= QIODevice::Append;
1270 if (!file.open(mode))
1272 LOG(VB_GENERAL, LOG_ERR, QString(
"Failed to open: '%1'") .arg(outFile));
1277 size_t remaining = data.size();
1278 uint failure_cnt = 0;
1279 while ((remaining > 0) && (failure_cnt < 5))
1281 ssize_t written = file.write(data.data() + offset, remaining);
1291 remaining -= written;
1311 static const char dateFormat[] =
"ddd, dd MMM yyyy hh:mm:ss 'GMT'";
1312 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"GetLastModified('%1')").arg(url));
1317 QUrl cacheUrl = QUrl(url);
1320 QString redirectLoc;
1322 while (!(redirectLoc =
getHeader(cacheUrl,
"Location")).isNull())
1324 if (limit == CACHE_REDIRECTION_LIMIT)
1326 LOG(VB_GENERAL, LOG_WARNING, QString(
"Cache Redirection limit "
1328 .arg(cacheUrl.toString()));
1331 cacheUrl.setUrl(redirectLoc);
1336 QNetworkCacheMetaData urlData =
m_manager->cache()->metaData(cacheUrl);
1339 if (urlData.isValid() &&
1340 ((!urlData.expirationDate().isValid()) ||
1341 (urlData.expirationDate().secsTo(now) < 0)))
1343 if (QDateTime(urlData.lastModified().toUTC()).secsTo(now) <= 1800)
1345 result = urlData.lastModified().toUTC();
1349 QString date =
getHeader(urlData,
"Date");
1352 QDateTime loadDate =
1354 loadDate.setTimeSpec(Qt::UTC);
1355 if (loadDate.secsTo(now) <= 720)
1357 result = urlData.lastModified().toUTC();
1363 if (!result.isValid())
1366 dlInfo->
m_url = url;
1374 dlInfo->
m_reply->header(QNetworkRequest::LastModifiedHeader);
1375 if (lastMod.isValid())
1376 result = lastMod.toDateTime().toUTC();
1382 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"GetLastModified('%1'): Result %2")
1383 .arg(url).arg(result.toString()));
1397 jar->
load(filename);
1412 jar->
save(filename);
1434 return static_cast<QNetworkCookieJar *
>(outJar);
1462 m_manager->setCookieJar(static_cast<QNetworkCookieJar *>(outJar));
1471 return QString::null;
1474 QNetworkCacheMetaData metadata =
m_manager->cache()->metaData(url);
1486 const QString& header)
1488 QNetworkCacheMetaData::RawHeaderList headers = cacheData.rawHeaders();
1490 QNetworkCacheMetaData::RawHeaderList::iterator it = headers.begin();
1491 for (; !found && it != headers.end(); ++it)
1493 if (QString((*it).first) == header)
1496 return QString((*it).second);
1500 return QString::null;
1509 const QList<QNetworkCookie> cookieList = old.allCookies();
1510 setAllCookies(cookieList);
1524 LOG(VB_GENERAL, LOG_DEBUG, QString(
"MythCookieJar: loading cookies from: %1").arg(filename));
1527 if (!f.open(QIODevice::ReadOnly))
1529 LOG(VB_GENERAL, LOG_WARNING, QString(
"MythCookieJar::load() failed to open file for reading: %1").arg(filename));
1533 QList<QNetworkCookie> cookieList;
1534 QTextStream stream(&f);
1535 while (!stream.atEnd())
1537 QString cookie = stream.readLine();
1538 cookieList << QNetworkCookie::parseCookies(cookie.toLocal8Bit());
1541 setAllCookies(cookieList);
1549 LOG(VB_GENERAL, LOG_DEBUG, QString(
"MythCookieJar: saving cookies to: %1").arg(filename));
1552 if (!f.open(QIODevice::WriteOnly))
1554 LOG(VB_GENERAL, LOG_ERR, QString(
"MythCookieJar::save() failed to open file for writing: %1").arg(filename));
1558 QList<QNetworkCookie> cookieList = allCookies();
1559 QTextStream stream(&f);
1561 for (QList<QNetworkCookie>::iterator it = cookieList.begin();
1562 it != cookieList.end(); ++it)
1564 stream << (*it).toRawForm() << endl;