Fix forward navigation control on Linux.
[dorian] / search.cpp
1 #include <QNetworkRequest>
2 #include <QNetworkReply>
3 #include <QtGui>
4 #include <QFile>
5 #include <QNetworkAccessManager>
6 #include <QWebPage>
7 #include <QWebElementCollection>
8 #include <QWebFrame>
9
10 #include "search.h"
11 #include "platform.h"
12 #include "trace.h"
13
14 Search *inst = 0;
15
16 Search *Search::instance()
17 {
18     if (!inst) {
19         inst = new Search();
20     }
21     return inst;
22 }
23
24 void Search::close()
25 {
26     delete inst;
27     inst = 0;
28 }
29
30 Search::Search(): QObject(0), reply(0), downloadReply(0)
31 {
32     manager = new QNetworkAccessManager(this);
33     downloadManager = new QNetworkAccessManager(this);
34 }
35
36 void Search::start(const Query &query)
37 {
38     TRACE;
39
40     emit beginSearch();
41
42     searchResults.clear();
43     QNetworkRequest request;
44     request.setUrl(QUrl("http://www.gutenberg.org/catalog/world/results"));
45     // request.setRawHeader("User-Agent", "Dorian " + Platform::version());
46     QString title = query.title;
47     if (title.isEmpty()) {
48         title = ".";
49     }
50     QByteArray data;
51     data = "title=" + QUrl::toPercentEncoding(title) + "&author=" +
52            QUrl::toPercentEncoding(query.author);
53     qDebug() << "Request:" << (request.url().toString() + "?" + data);
54     reply = manager->post(request, data);
55     connect(reply, SIGNAL(finished()), this, SLOT(finished()));
56 }
57
58 QList<Search::Result> Search::results()
59 {
60     return searchResults;
61 }
62
63 void Search::download(const Search::Result &result, const QString &fileName)
64 {
65     TRACE;
66     downloadResult = result;
67     downloadFileName = fileName;
68     qDebug() << "UID" << result.id;
69     Q_UNUSED(fileName);
70     emit beginDownload(0);
71     QUrl url("http://www.gutenberg.org/ebooks/" + result.id + ".epub");
72     qDebug() << "Requesting" << url;
73     QNetworkRequest request;
74     request.setUrl(url);
75     downloadReply = downloadManager->get(request);
76     connect(downloadReply, SIGNAL(finished()), this, SLOT(downloadFinished()));
77 }
78
79 void Search::finished()
80 {
81     TRACE;
82
83     if (!reply) {
84         return;
85     }
86
87     QByteArray data = reply->readAll();
88
89     // Parse search results
90
91     QWebPage page(this);
92     QWebFrame *frame = page.mainFrame();
93     frame->setHtml(QString(data));
94     QWebElementCollection tables = frame->findAllElements("table");
95     if (tables.count() == 1) {
96         QWebElement table = tables[0];
97         foreach (QWebElement row, table.findAll("tr")) {
98             QWebElementCollection cols = row.findAll("td");
99             if (cols.count() < 5) {
100                 continue;
101             }
102             QString id = cols[0].toPlainText().trimmed();
103             if (id.isEmpty()) {
104                 continue;
105             }
106             QString author = cols[2].toPlainText().trimmed();
107             QStringList titles = cols[3].toPlainText().trimmed().
108                                  split("\n", QString::SkipEmptyParts);
109             Result r;
110             r.authors = author.split("\n", QString::SkipEmptyParts);
111             r.id = id;
112             if (titles.count()) {
113                 r.title = titles[0];
114             }
115             r.language = cols[4].toPlainText().trimmed();
116             r.source = "Project Gutenberg";
117             searchResults.append(r);
118             qDebug() << id;
119             qDebug() << " Authors:" << r.authors;
120             qDebug() << " Title:" << r.title;
121             qDebug() << " Language:" << r.language;
122         }
123     }
124
125     reply->deleteLater();
126     reply = 0;
127     emit endSearch();
128 }
129
130 void Search::downloadFinished()
131 {
132     TRACE;
133
134     if (!downloadReply) {
135         return;
136     }
137
138     // Handle download errors
139     if (QNetworkReply::NoError != downloadReply->error()) {
140         qCritical() << "Search::downloadFinished: Network error"
141                 << downloadReply->error();
142         downloadReply->deleteLater();
143         downloadReply = 0;
144         emit endDownload(Search::DownloadError, downloadResult, downloadFileName);
145         return;
146     }
147
148     // Handle redirection
149     QVariant header = downloadReply->header(QNetworkRequest::LocationHeader);
150     if (header.isValid()) {
151         // Handle redirection: Download again with the new URL
152         qDebug() << "Redirected to" << header;
153         QNetworkRequest request;
154         request.setUrl(header.toUrl());
155         downloadReply->deleteLater();
156         downloadReply = downloadManager->get(request);
157         connect(downloadReply, SIGNAL(finished()), this, SLOT(downloadFinished()));
158         return;
159     }
160
161     // Handle download success
162     QByteArray data = downloadReply->readAll();
163     qint64 size = (qint64)data.size();
164     qDebug() << "Got" << size << "bytes";
165     downloadReply->deleteLater();
166     downloadReply = 0;
167     QFile out(downloadFileName);
168     int status = Search::FileError;
169     if (out.open(QIODevice::WriteOnly)) {
170         if (size == out.write(data, size)) {
171             qDebug() << "Book saved to" << downloadFileName;
172             status = Search::Ok;
173         } else {
174             qCritical() << "Search::downloadFinished: Failed to write" << size
175                     << "bytes to" << downloadFileName;
176         }
177         out.close();
178     } else {
179         qCritical() << "Search::downloadFinished: Could not open"
180                 << downloadFileName;
181     }
182     emit endDownload(status, downloadResult, downloadFileName);
183 }