Merge branch 'stardict' of ssh://drop.maemo.org/git/mdictionary into stardict
[mdictionary] / src / plugins / xdxf / XdxfDictDownloader.cpp
1 /*******************************************************************************
2
3     This file is part of mDictionary.
4
5     mDictionary 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 3 of the License, or
8     (at your option) any later version.
9
10     mDictionary 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 mDictionary.  If not, see <http://www.gnu.org/licenses/>.
17
18     Copyright 2010 Comarch S.A.
19
20 *******************************************************************************/
21
22 /*!
23   \file XdxfDictDownloader.cpp
24   \author Mateusz Półrola <mateusz.polrola@comarch.com>
25   */
26
27 #include "XdxfDictDownloader.h"
28 #include "XdxfDictDownloadProgressDialog.h"
29 #include <QDebug>
30 #include <QProcess>
31
32
33 #include <bzlib.h>
34 #ifndef Q_WS_MAEMO_5
35 #include <libtar.h>
36 #endif
37 #include <stdio.h>
38 #include <fcntl.h>
39
40 typedef void BZFILE;
41
42
43
44 XdxfDictDownloader::XdxfDictDownloader(QObject *parent) :
45     QObject(parent) {
46     parentDialog = 0;
47     manager = new QNetworkAccessManager(this);
48
49     connect(manager, SIGNAL(finished(QNetworkReply*)),
50             this, SLOT(dictListReceived(QNetworkReply*)));
51
52
53     progressDialog = 0;
54     connect(&http, SIGNAL(finished()), this, SLOT(processFinished()));
55     connect(&http, SIGNAL(error(QString)),
56             this, SLOT(downloadingError(QString)));
57     connect(&http, SIGNAL(progress(qint64,qint64)),
58             this, SLOT(updateDownloadProgress(qint64,qint64)));
59
60 }
61
62 void XdxfDictDownloader::download(QWidget *parent) {
63     parentDialog = parent;
64     aborted = false;
65
66     manager->get(QNetworkRequest(QUrl("http://xdxf.revdanica.com/down/")));
67
68     progressDialog = new XdxfDictDownloadProgressDialog(parent);
69
70     connect(progressDialog, SIGNAL(cancelDownloading()),
71             this, SLOT(breakDownloading()));
72     connect(this, SIGNAL(downloadProgress(float)),
73             progressDialog, SLOT(updateProgress(float)));
74
75     progressDialog->setText(tr("Downloading dictionaries list"));
76     progressDialog->show();
77 }
78
79 QString XdxfDictDownloader::downloadedFile() {
80     return _downloadedFile;
81 }
82
83 void XdxfDictDownloader::updateDownloadProgress(qint64 downloaded,
84                                                 qint64 total)   {
85     Q_EMIT downloadProgress(float(downloaded) / float(total));
86 }
87
88 void XdxfDictDownloader::downloadingError(QString error) {
89     breakDownloading();
90     Q_EMIT notify(Notify::Error, error);
91 }
92
93 void XdxfDictDownloader::breakDownloading() {
94     //if user cancel downloading we kill all running processes, hide progress dialog and set flag that user cancel downloading.
95     aborted = true;
96     http.kill();
97
98     if(progressDialog && progressDialog->isVisible()) {
99         progressDialog->accept();
100     }
101
102 }
103
104 void XdxfDictDownloader::processFinished() {
105     //first check if user cancel downloading
106     if(aborted) return;
107
108     if(!extract("/tmp/" + _fileName)) {
109         Q_EMIT notify(Notify::Error,
110                 "Error while extracting dictionary archive");
111         return;
112     }
113     downloadComplete();
114 }
115
116 void XdxfDictDownloader::downloadComplete() {
117     if(aborted) return;
118     // Downloaded tar file name is different than extracted folder so we need
119     // some clean directory to identify extracted files
120     QDir dir("/tmp/mdict");
121     QString dictDirName = dir.entryList().at(2);
122
123     // Dict is in /tmp/mdict/<extracted directory>/dict.xdxf
124     QFile dictFile("/tmp/mdict/" + dictDirName + "/dict.xdxf");
125     dictFile.copy(QDir::homePath() + "/.mdictionary/" + dictDirName + ".xdxf");
126     QFile::remove("/tmp/" + _fileName);
127     QFile::remove("/tmp/" + _fileName.replace(QRegExp(".bz2$"), ""));
128
129     _downloadedFile = QDir::homePath() + "/.mdictionary/" + dictDirName + ".xdxf";
130
131     progressDialog->accept();
132     delete progressDialog;
133     progressDialog = 0;
134
135     emit fileDownloaded(_downloadedFile);
136 }
137
138 void XdxfDictDownloader::dictListReceived(QNetworkReply *reply) {
139     progressDialog->accept();
140     if(aborted) return;
141
142
143     if(reply->error() != QNetworkReply::NoError) {
144         Q_EMIT notify(Notify::Error, reply->errorString());
145         return;
146     }
147
148     QString page(QString::fromUtf8(reply->readAll()));
149
150     // You can look at http://xdxf.revdanica.com/down/, we need to get table
151     // with dictionaries entries following regexp match its begining
152     QRegExp regOuter("<td>Icon</td><td>Name</td><td>Archive filename</td><td>Archive file size</td><td>Dict file size</td><td>Number of articles</td><td>From</td><td>To</td><td>Submitted by</td><td>Submition date</td></tr>(.*)</table>");
153     regOuter.setMinimal(true);
154     if(!regOuter.indexIn(page))
155         return;
156
157     // Cutting each entry and creating coresponded DownloadDict object
158     page = regOuter.capturedTexts().at(1);
159     QRegExp regInner("<tr>.*</tr>");
160     regInner.setMinimal(true);
161     int pos = 0;
162
163     while ((pos = regInner.indexIn(page, pos)) != -1) {
164         DownloadDict temp = DownloadDict(regInner.cap(0));
165         if(!temp.fromLang().isEmpty())
166             dicts.append(temp);
167         pos += regInner.matchedLength();
168     }
169
170     XdxfDictSelectDialog selectDialog(dicts, parentDialog);
171
172     if(selectDialog.exec()==QDialog::Accepted) {
173
174         progressDialog->setText(tr("Downloading dictionary"));
175         progressDialog->show();
176
177         QString url = selectDialog.link();
178
179         _fileName = url.split('/').last();
180
181         QProcess clean;
182         clean.start("rm -rf /tmp/mdict");
183         clean.waitForFinished(-1);
184         clean.start("mkdir /tmp/mdict");
185         clean.waitForFinished(-1);
186
187         http.download(QUrl(url), "/tmp/" + _fileName);
188     }
189 }
190
191 bool XdxfDictDownloader::extract(QString file) {
192     // Extracting bz2
193     FILE * archive = fopen(file.toStdString().c_str(), "rb");
194     if (archive == 0)
195         return false;
196     int err;
197     BZFILE * afterbzFile = BZ2_bzReadOpen(&err, archive, 0, 0, 0, 0);
198     if(err != BZ_OK) {
199         BZ2_bzReadClose(&err, afterbzFile);
200         return false;
201     }
202
203     FILE * tarfile = fopen(file.replace(QRegExp(".bz2$"), "").
204             toStdString().c_str(), "w");
205
206     int bufflen = 100;
207     char buff[bufflen];
208     while(err == BZ_OK) {
209         int len = BZ2_bzRead(&err, afterbzFile, buff, bufflen);
210         if(fwrite(buff, 1, len, tarfile) != len)
211             return false;
212     }
213     BZ2_bzReadClose(&err, afterbzFile);
214     fclose(tarfile);
215     fclose(archive);
216
217     // Extracting tar
218     #ifndef Q_WS_MAEMO_5
219     TAR *t;
220     char * tarfname = new char[file.replace(QRegExp(".bz2%"), "").size()+1];
221     strcpy(tarfname, file.replace(QRegExp(".bz2%"), "").toStdString().c_str());
222
223     err = tar_open(&t, tarfname, 0, O_RDONLY, 0, 0);
224     if(err == -1)
225         return false;
226
227     err = tar_extract_all(t, "/tmp/mdict/");
228     if(err == -1) {
229         return false;
230     }
231     tar_close(t);
232     #else
233     QProcess tar;
234     tar.start("tar -xvf " + file.replace(QRegExp(".bz2%"), "") + " -C /tmp/mdict");
235     tar.waitForFinished(-1);
236     #endif
237
238     return true;
239 }
240
241