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