downloadmanager.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. /**
  2. * Copyright (c) 2011 Nokia Corporation.
  3. */
  4. #include "downloadmanager.h"
  5. #include <QFile>
  6. #include <QFileInfo>
  7. #include <QList>
  8. #include <QtCore/QUrl>
  9. #include <QtCore/QDebug>
  10. #include <QtGui/QApplication>
  11. #include <QtNetwork/QNetworkRequest>
  12. #include <QtNetwork/QNetworkReply>
  13. #include <QtNetwork/QNetworkConfiguration>
  14. #include <QtNetwork/QNetworkConfigurationManager>
  15. #include <QStringList>
  16. #include "product.h"
  17. /*!
  18. \class DownloadManager
  19. \brief Manages the downloads of purchased products.
  20. */
  21. /*!
  22. Constructor.
  23. */
  24. DownloadManager::DownloadManager(QObject *parent)
  25. : QObject(parent),
  26. m_manager(0),
  27. m_session(0),
  28. m_scriptEngine(0)
  29. {
  30. m_manager = new QNetworkAccessManager(this);
  31. QNetworkConfigurationManager cfgManager;
  32. QNetworkConfiguration cfg = cfgManager.defaultConfiguration();
  33. m_session = new QNetworkSession(cfg, this);
  34. m_session->open();
  35. m_session->waitForOpened(10000);
  36. connect(m_manager, SIGNAL(finished(QNetworkReply*)),
  37. SLOT(downloadFinished(QNetworkReply*)));
  38. m_scriptEngine = new QScriptEngine(this);
  39. }
  40. /*!
  41. Destructor.
  42. */
  43. DownloadManager::~DownloadManager()
  44. {
  45. cancel(); // Aborts and deletes all requests
  46. m_session->close();
  47. }
  48. /*!
  49. Requests product IDs from \a url.
  50. */
  51. void DownloadManager::requestProductsIds(const QUrl &url)
  52. {
  53. qDebug() << "DownloadManager::requestProductsIds():" << url.toString();
  54. QNetworkRequest request(url);
  55. Request *req = new Request();
  56. req->type = Request::EProductIds;
  57. req->netRequest = m_manager->get(request);
  58. m_currentDownloads.append(req);
  59. }
  60. /*!
  61. Starts downloading a .sis file. The correct file is identified with
  62. \a productId and the purchase is validated with \a purchaseTicket.
  63. */
  64. void DownloadManager::downloadSis(QString productId,
  65. QString purchaseTicket,
  66. const QUrl &url)
  67. {
  68. qDebug() << "DownloadManager::downloadSis():" << url.toString();
  69. QNetworkRequest request(url);
  70. Request* req = new Request();
  71. req->type = Request::EDownloadSis;
  72. req->id = productId;
  73. // POST with purchase ticket in the BODY
  74. //BODY:
  75. //{
  76. //"purchaseTicket": PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9InllcyI/PjxQdXJjaGFzZVRpY2tldCBzaWduYXR1cmU9IjRhOTRiMWUzMzljZmI5MjkzMjdlNTJhYzBiZjRiNGUyNzAwYjEwMjUiIGltc2k9Ijg0ODI0YWE5NTM2NjBkNjkxZjlkZTQxNWQ2NzJkMGY3NTg4MTgzODQiIGltZWk9ImUxZDAwOWIwMjBkNGQzMTBmNGUzODEwN2I1MWQ4YjAyYWEyYjI2MDUiIGFjY291bnRJZD0iYzM2YzYyODVhZmQzNWYwYTkwZDg2MWE2Y2Q0ZTVmMTQyNjc5NWNiZCIgcHJvZHVjdElkPSJJQVBfUmVzb3VyY2VfMSIgYXBwbGljYXRpb25JZD0iSUFQX1Byb2R1Y3RfMSIgdHJhbnNhY3Rpb25UaW1lPSIyMDEwLTExLTAyIDA5OjI0OjM0IFVUQyIgdHJhbnNhY3Rpb25JZD0iMDdjMzI4NGUtODhiMS00MzYxLTljZDYtNTUxMzkwMzk5NTA2IiB4bWxucz0iaHR0cDovL3BheW1lbnQub3ZpLmNvbS9pYXAiLz4=
  77. //}
  78. QString bodyString;
  79. bodyString.append("{");
  80. bodyString.append("\"purchaseTicket\":");
  81. bodyString.append("\""+purchaseTicket+"\"");
  82. bodyString.append("}");
  83. request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
  84. request.setHeader(QNetworkRequest::ContentLengthHeader,
  85. QString::number(bodyString.length()));
  86. request.setRawHeader("User-Agent","QuickHit");
  87. QByteArray bodyBytes = bodyString.toAscii();
  88. qDebug() << "DownloadManager::downloadSis(): Post body:\n" + bodyString;
  89. req->netRequest = m_manager->post(request,bodyBytes);
  90. QObject::connect(req->netRequest, SIGNAL(downloadProgress(qint64,qint64)),
  91. this, SLOT(onDownloadProgress(qint64,qint64)));
  92. m_currentDownloads.append(req);
  93. }
  94. /*!
  95. Returns a request from the list of downloads by \a reply or NULL if not
  96. found.
  97. */
  98. Request *DownloadManager::findRequestForReply(QNetworkReply *reply)
  99. {
  100. for (int i = 0; i < m_currentDownloads.count(); i++) {
  101. if (m_currentDownloads[i]->netRequest == reply) {
  102. return m_currentDownloads[i];
  103. }
  104. }
  105. return 0;
  106. }
  107. /*!
  108. Removes \a request from the list of downloads if found. Note that the
  109. removed instance is not destroyed.
  110. */
  111. void DownloadManager::removeRequest(Request *request)
  112. {
  113. if (request) {
  114. for (int i = 0; i < m_currentDownloads.count(); i++) {
  115. if (m_currentDownloads[i]->netRequest == request->netRequest) {
  116. m_currentDownloads.removeAt(i);
  117. break;
  118. }
  119. }
  120. }
  121. }
  122. /*!
  123. Handles a finished download.
  124. */
  125. void DownloadManager::downloadFinished(QNetworkReply *reply)
  126. {
  127. qDebug() << "DownloadManager::downloadFinished()";
  128. Request *request = findRequestForReply(reply);
  129. if (request) {
  130. if (reply->error() != QNetworkReply::NoError) {
  131. // ERROR
  132. qDebug() << "DownloadManager::downloadFinished(): HTTP error:" << reply->errorString();
  133. emit downloadCompleted(request->type, request->id, "", reply->error());
  134. }
  135. else {
  136. // NO ERRORS
  137. switch (request->type) {
  138. case Request::EDownloadSis :
  139. {
  140. QUrl url = reply->url();
  141. QString filename = saveFileName(url);
  142. if (saveToDisk(filename, reply)) {
  143. emit(downloadCompleted(request->type, request->id, filename, 0));
  144. }
  145. else {
  146. // Failed to save the file to disk!
  147. emit(downloadCompleted(request->type, request->id, filename, -1));
  148. }
  149. break;
  150. }
  151. case Request::EProductIds :
  152. {
  153. processJsonReply(request,reply);
  154. break;
  155. }
  156. };
  157. }
  158. }
  159. removeRequest(request);
  160. reply->deleteLater();
  161. delete request;
  162. }
  163. /*!
  164. Symbian SIS downloading progress
  165. */
  166. void DownloadManager::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
  167. {
  168. QNetworkReply* nr = (QNetworkReply*)sender();
  169. if (nr) {
  170. Request* request = findRequestForReply(nr);
  171. if (request) {
  172. // Emit progress
  173. emit downloadProgress(request->id, bytesReceived, bytesTotal);
  174. }
  175. }
  176. }
  177. /*!
  178. Cancels and deletes all requests.
  179. */
  180. void DownloadManager::cancel()
  181. {
  182. Request *request(0);
  183. while (m_currentDownloads.count()) {
  184. request = m_currentDownloads[0];
  185. if (request && request->netRequest) {
  186. request->netRequest->abort();
  187. request->netRequest->deleteLater();
  188. }
  189. m_currentDownloads.removeAt(0);
  190. delete request;
  191. }
  192. }
  193. /*!
  194. Resolves and returns the file name (including the path), based on \a url, for
  195. the file to save.
  196. */
  197. QString DownloadManager::saveFileName(const QUrl &url)
  198. {
  199. QString path = url.path();
  200. QString basename = QFileInfo(path).fileName();
  201. // Verify the file name.
  202. if (basename.isEmpty()) {
  203. basename = "level.sis";
  204. }
  205. if (!basename.contains(".sis", Qt::CaseInsensitive)) {
  206. basename.append(".sis");
  207. }
  208. basename = QString("c:/Data/") + basename;
  209. if (QFile::exists(basename)) {
  210. // File already exists, remove.
  211. QFile::remove(basename);
  212. }
  213. return basename;
  214. }
  215. /*!
  216. Saves \a data to a file with \a filename. Returns true if successful, false
  217. otherwise.
  218. */
  219. bool DownloadManager::saveToDisk(const QString &filename, QIODevice *data)
  220. {
  221. QFile file(filename);
  222. if (!file.open(QIODevice::WriteOnly)) {
  223. qDebug() << "DownloadManager::saveToDisk(): Could not open " << filename
  224. << " for writing: " << file.errorString();
  225. return false;
  226. }
  227. file.write(data->readAll());
  228. file.close();
  229. return true;
  230. }
  231. /*!
  232. Parses and processes the JSON reply, in \a reply, received from the server.
  233. */
  234. void DownloadManager::processJsonReply(Request *request, QNetworkReply *reply)
  235. {
  236. switch (request->type) {
  237. case Request::EProductIds: {
  238. // JSON protocol format
  239. /*
  240. {
  241. "products": [
  242. { "id": 753701, "image": ".../753701_levelone.png" },
  243. { "id": 753973, "image": ".../753973_leveltwo.png" }
  244. ]
  245. }
  246. */
  247. qDebug() << "DownloadManager::processJsonReply(): Parsing JSON...";
  248. QByteArray bytes = reply->readAll();
  249. QString data(bytes);
  250. qDebug() << data;
  251. // JSON parsing
  252. QScriptValue sc =
  253. m_scriptEngine->evaluate("JSON.parse").call(QScriptValue(),
  254. QScriptValueList() << data);
  255. QList<QObject*> productList;
  256. if (sc.property("products").isArray()) {
  257. qDebug() << "DownloadManager::processJsonReply(): JSON products exist.";
  258. QStringList items;
  259. qScriptValueToSequence(sc.property("products"), items);
  260. QScriptValueIterator it(sc.property("products"));
  261. while (it.hasNext()) {
  262. it.next();
  263. if (it.value().property("id").isNull()) {
  264. break;
  265. }
  266. // Single product data exists
  267. Product *product = new Product();
  268. if (it.value().property("id").isString()) {
  269. QString id = it.value().property("id").toString();
  270. product->setId(id);
  271. if (it.value().property("image").isString()) {
  272. QString image = it.value().property("image").toString();
  273. product->setThumbnail(image);
  274. productList.append(product);
  275. }
  276. else {
  277. product->setThumbnail("");
  278. productList.append(product);
  279. }
  280. qDebug() << "DownloadManager::processJsonReply(): JSON parsing result: Id:"
  281. << product->id() << "; thumb: "
  282. << product->thumbnail();
  283. }
  284. else {
  285. //qDebug() << "DownloadManager::processJsonReply(): Failed to find a product ID!";
  286. delete product;
  287. }
  288. } // while ()
  289. }
  290. else {
  291. qDebug() << "DownloadManager::processJsonReply(): Failed to find JSON products!";
  292. emit downloadCompleted(request->type, request->id, "", -1);
  293. }
  294. // Send result if products exist
  295. if (productList.length() > 0) {
  296. emit productIdsCompleted(productList);
  297. }
  298. break;
  299. } // case Request::EProductIds
  300. }; // switch (request->type)
  301. }