717a6b6b02a23a27f2e456cb61fb72879286db85
[qtrapids] / src / plugins / searchplugin / SearchPlugin.cpp
1 /***************************************************************************
2  *   Copyright (C) 2009 by Lassi Väätämöinen   *
3  *   lassi.vaatamoinen@ixonos.com   *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20
21 #include <QDebug>
22 #include <QtCore/qplugin.h>
23 #include <QVBoxLayout>
24 #include <QHBoxLayout>
25 #include <QComboBox>
26 #include <QLineEdit>
27 #include <QPushButton>
28 #include <QUrl>
29 #include <QDir>
30 #include <QFileInfo>
31 #include <QWebView>
32 #include <QWebPage>
33 #include <QNetworkReply>
34 #include <QDomDocument>
35
36 #include "SearchPlugin.h"
37 #include "DownloadManager.h"
38
39 namespace qtrapids
40 {
41         const QString ENGINES_DIR = "engines";
42         const QString DESCRIPTION_FILENAME = "opensearch.xml";
43         const QString SEARCH_TERMS_STRING = "{searchTerms}";
44         const QString SEARCHPLUGIN_ID = "SearchPlugin";
45         
46         SearchPlugin::SearchPlugin() : 
47                 comboBox_(NULL), searchLine_(NULL),
48                 searchButton_(NULL), result_(NULL), 
49                 dlManager_(NULL), host_(NULL)
50         {
51                 // TODO: Add back/forward/refresh -buttons to widget to allow some browsing functionality
52         }
53         
54         
55         void SearchPlugin::initialize(PluginHostInterface* host, Info info)
56         {
57                 host_ = host;
58                 
59                 if (host_ != NULL) {
60                 
61                         // Build up the plugin widget:
62                         QWidget *pluginWidget = new QWidget;
63                         QVBoxLayout *vbox = new QVBoxLayout;
64                         QHBoxLayout *hbox = new QHBoxLayout;
65                         comboBox_ = new QComboBox;
66                         searchLine_ = new QLineEdit;
67                         searchButton_ = new QPushButton("Search");
68                         
69                         hbox->addWidget(searchLine_);
70                         hbox->addWidget(searchButton_);
71                         vbox->addWidget(comboBox_);
72                         vbox->addLayout(hbox);
73                         pluginWidget->setLayout(vbox);
74         
75                         connect(searchButton_, SIGNAL(clicked()), this, SLOT(on_searchButton_clicked()));
76                         //connect(this, SIGNAL(searchResult(QWidget*)), this, SLOT(on_searchResult(QWidget*)));
77                         qDebug() << info.directory;
78                         QDir dir(info.directory);
79                         if (dir.cd(ENGINES_DIR)) {
80                                 ParseSearchEngineDescriptions(dir);
81                         }
82                         
83                         host_->setGui(pluginWidget, qtrapids::PluginHostInterface::BASE_WIDGET, this);
84                 }
85         }
86         
87         QWidget* SearchPlugin::getGui()
88         {
89                 return NULL;
90         }
91         
92         QString SearchPlugin::identifier()
93         {
94                 return SEARCHPLUGIN_ID;
95         }
96
97
98         void SearchPlugin::on_searchButton_clicked()
99         {
100                 int i = comboBox_->currentIndex();
101                 QString tmp = engineTemplates_.at(i);
102                 
103                 i = tmp.indexOf(SEARCH_TERMS_STRING);
104                 tmp.replace(i, SEARCH_TERMS_STRING.length(), searchLine_->text());
105                 
106                 QUrl searchUrl(tmp);
107                 qDebug() << searchUrl;
108                 result_ = new QWebView;
109                 
110                 // Get underlying QWebPage and change link delegation, so we can meddle with links in on_linkClicked().
111                 QWebPage *resultPage = result_->page();
112                 resultPage->setLinkDelegationPolicy(QWebPage::DelegateExternalLinks);
113                 
114                 // Get the network access manager for examining HTTP replies.
115                 QNetworkAccessManager *netwAccessManager_ = resultPage->networkAccessManager();
116                 
117                 connect(resultPage, SIGNAL(linkClicked(const QUrl&)), this, SLOT(on_linkClicked(const QUrl&)));
118                 connect(result_, SIGNAL(loadFinished(bool)), this, SLOT(on_loadFinished(bool)));
119                 connect(netwAccessManager_, SIGNAL(finished(QNetworkReply*)), this, SLOT(on_networkReplyFinished(QNetworkReply*)));
120                 
121                 result_->load(searchUrl);
122                 
123                 on_searchResult((QWidget*)result_);
124         }
125         
126         
127         void SearchPlugin::on_loadFinished(bool ok)
128         {
129 #ifdef QTRAPIDS_DEBUG
130                 if (ok) {
131                         qDebug() << "on_loadFinished(): success";
132                 }
133 #endif
134         }
135         
136         
137         void SearchPlugin::on_networkReplyFinished(QNetworkReply* reply)
138         {
139                 
140                 qDebug() << "on_networkReplyFinished()";
141                 QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).toString();
142                 QString filename;
143                 QByteArray replyData;
144                 QUrl url = reply->url();
145                 
146                 qDebug() << "on_networkReplyFinished():" << url;
147                 
148                 // If content type is torrent data, read reply data:
149                 if (contentType == "application/x-bittorrent") {
150                         
151                         // If HTTP-response has Content-Disposition: -header, then we must use that as filename.
152                         if (reply->hasRawHeader("Content-Disposition")) { // NOTE this code block taken from kwebpage.cpp
153                                 const QString value = QLatin1String(reply->rawHeader("Content-Disposition"));
154                                 const int pos = value.indexOf(QLatin1String("filename="));
155                                 if (pos != -1) {
156                                         QString name = value.mid(pos + 9);
157                                         if (name.startsWith(QLatin1Char('"')) && name.endsWith(QLatin1Char('"')))
158                                                 name = name.mid(1, name.size() - 2);
159                                         filename = name;
160                                 }
161                         // No content-disposition header, use last part (the filename) of URL as filename:
162                         } else {
163                                 QString path = url.path();
164                                 QFileInfo fInfo(path);
165                                 filename = fInfo.fileName();
166                         }
167                         
168                         // Destroy ongoing download, if any.
169                         if (dlManager_) {
170                                 delete dlManager_;
171                                 dlManager_ = NULL;
172                         }
173                 
174                         /// @todo Is this a bit of a hack now; we get the reply and check Content-Type,
175                         /// The download is then started afterwards, so we get unecessarily one extra
176                         /// HTTP-response here.  
177                         /// @todo Could this whole content-type checking be logically moved to DownloadManager?
178                         // Start downloading Torrent file.
179                         dlManager_ = new DownloadManager(url, "/tmp/" + filename);
180                         connect(dlManager_, SIGNAL(finished(QString)), this, SLOT(on_downloadFinished(QString)));
181                         dlManager_->start();
182                         
183                 }
184         }
185         
186         
187         void SearchPlugin::on_searchResult(QWidget* resultWidget)
188         {
189 #ifdef QTRAPIDS_DEBUG
190                 qDebug() << "on_searchResult()";
191 #endif
192                 if (host_) {
193                         host_->addPluginWidget(resultWidget, qtrapids::PluginHostInterface::TAB_PAGE);
194                 }
195         }
196
197
198         /// @todo It may be that we don't actually need link delegation, because we check response Content-Type header
199         /// on_linkClicked() in that case unnecessary function
200         void SearchPlugin::on_linkClicked(const QUrl& url)
201         {
202                 qDebug() << "on_linkClicked():" << url;
203                 
204                 if (url.isValid()) {
205                         result_->load(url);
206                 }
207         }
208         
209         
210         void SearchPlugin::on_downloadFinished(QString filepath)
211         {
212 #ifdef QTRAPIDS_DEBUG
213                 qDebug() << "TORRENT DOWNLOADED: " << filepath;
214 #endif
215                 delete dlManager_;
216                 dlManager_ = NULL;
217                 if (host_) {
218                         host_->eventRequest(QVariant(filepath), qtrapids::PluginHostInterface::OPEN_FILE);
219                 }
220         }
221
222
223         void SearchPlugin::ParseSearchEngineDescriptions(const QDir& dir)
224         {
225                 foreach (QString dirName, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
226                 
227                         QFile file(dir.path() + "/" + dirName + "/" + DESCRIPTION_FILENAME);
228                         
229                         if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
230                                 qWarning() << "Unable to open " << DESCRIPTION_FILENAME << " for reading in " << dir.path();
231                                 continue;
232                         }
233                         
234                         // Parse the XML file to DOM document.
235                         QDomDocument document;
236                         
237                         // Second parameter: nameSpaceProcessing = false
238                         if (!document.setContent(&file, false)) {
239                                 qWarning() << "Unable to parse " << DESCRIPTION_FILENAME << " in " << dirName;
240                         } else {
241                                 QDomNodeList urlElements = document.elementsByTagName("Url");
242                                 QDomNodeList shortNameElements = document.elementsByTagName("ShortName");
243                                 
244                                 QDomNode n = urlElements.item(0);
245                                 QDomNode m;
246                                 QDomNamedNodeMap attribMap;
247                                 if (n.hasAttributes()) {
248                                         attribMap = n.attributes();
249                                         m = attribMap.namedItem("template");
250                                         engineTemplates_.push_back(m.nodeValue());
251                                 }
252                                 
253                                 n = shortNameElements.item(0);
254                                 if (n.hasChildNodes()) {
255                                         m = n.firstChild();
256                                         comboBox_->addItem(m.nodeValue());
257                                 }
258                         }
259                 }
260                 /// @todo save parsed xml elements <Shortname> and <Url template="<foo>"> to a model which is displayed in combobox.
261         }
262
263 } // namespace qtrapids
264
265 Q_EXPORT_PLUGIN2(searchplugin, qtrapids::SearchPlugin)