1
/*****************************************************************************
2
* This file is part of Kiten, a KDE Japanese Reference Tool... *
3
* Copyright (C) 2011 Daniel E. Moctezuma <democtezuma@gmail.com> *
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. *
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. *
15
* You should have received a copy of the GNU General Public License *
16
* along with this program; if not, write to the Free Software *
17
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 *
19
*****************************************************************************/
21
#include "dictionaryupdatemanager.h"
24
#include "kitenconfig.h"
25
#include "kitenmacros.h"
28
#include <KActionCollection>
31
#include <KIO/Scheduler>
32
#include <KIO/StoredTransferJob>
34
#include <KMessageBox>
35
#include <KStandardDirs>
38
#include <QTemporaryFile>
40
#include <QTextStream>
42
// URL to the information file.
43
#define INFO_URL "http://ftp.monash.edu.au/pub/nihongo/edicthdr.txt"
44
// URL to the EDICT dictionary.
45
#define EDICT_URL "http://ftp.monash.edu.au/pub/nihongo/edict.gz"
46
// URL to the KANJIDIC dictionary.
47
#define KANJIDIC_URL "http://ftp.monash.edu.au/pub/nihongo/kanjidic.gz"
49
DictionaryUpdateManager::DictionaryUpdateManager( Kiten *parent )
52
, _config( parent->getConfig() )
53
, _succeeded( QStringList() )
54
, _failed( QStringList() )
57
_actionUpdate = _parent->actionCollection()->add<KAction>( "update_dictionaries" );
58
_actionUpdate->setText( i18n( "Check for dictionary &updates" ) );
59
_actionUpdate->setShortcut( Qt::CTRL+Qt::Key_U );
61
connect( _actionUpdate, SIGNAL( triggered() ),
62
this, SLOT( checkForUpdates() ) );
65
void DictionaryUpdateManager::checkForUpdates()
67
kDebug() << "Checking for EDICT & KANJIDIC updates." << endl;
68
// Download the information file we need to check
69
// whether or not an update to our dictionaries is necessary.
70
KIO::StoredTransferJob *job = KIO::storedGet( KUrl( INFO_URL ) );
71
connect( job, SIGNAL( result( KJob* ) ),
72
this, SLOT( checkInfoFile( KJob* ) ) );
75
void DictionaryUpdateManager::checkIfUpdateFinished()
77
// Emit the updateFinished signal once we finished
78
// to update our installed dictionaries.
79
if( _counter == _config->dictionary_list().size() )
81
// Make sure to reset this variable to 0.
83
emit updateFinished();
87
void DictionaryUpdateManager::checkInfoFile( KJob *job )
89
KIO::StoredTransferJob *storedJob = static_cast<KIO::StoredTransferJob*>( job );
90
QByteArray data( storedJob->data() );
91
// Check if we have valid data to work with.
92
if( data.isNull() || data.isEmpty() )
94
KMessageBox::sorry( 0, i18n( "Update canceled.\nCould not read file." ) );
99
// We don't need to store this information file in our Hard Disk Drive,
100
// a temporary file is enough.
101
QTemporaryFile tempFile;
102
if( ! tempFile.open() )
104
KMessageBox::sorry( 0, i18n( "Update canceled.\nCould not open file." ) );
105
kDebug() << "Could not open tempFile." << endl;
106
tempFile.deleteLater();
110
tempFile.write( data );
113
// Get the latest creation date for the EDICT dictionary on the server.
114
QDate webFileDate = getFileDate( tempFile );
115
if( ! webFileDate.isValid() )
117
// The program should not get to this point.
118
// Maybe the format/content of (one or both) the files changed?
119
// or maybe one or both of the files could not be opened in the
120
// getFileDate function that would return an invalid empty date.
121
KMessageBox::sorry( 0, i18n( "Update canceled.\nThe update information file has an invalid date." ) );
122
tempFile.deleteLater();
127
// At this point we have a valid creation date from the server.
128
// Now we can check our dictionaries.
130
// Connect to this signal to know when the update process is finished.
131
connect( this, SIGNAL( updateFinished() ),
132
this, SLOT( showUpdateResults() ) );
134
// This variable help us to know if we need to download any file.
135
bool updates = false;
136
// Iterate on each of our installed dictionaries.
137
foreach( const QString &dict, _config->dictionary_list() )
139
QString filePath = KGlobal::dirs()->findResource( "data", QString( "kiten/" ) + dict.toLower() );
140
QFile file( filePath );
141
kDebug() << "Local dictionary path:" << file.fileName() << endl;
143
// Get the creation date for this dictionary.
144
QDate localFileDate = getFileDate( file );
146
if( ! localFileDate.isValid() )
148
// Add it to our 'failed to udate' list.
149
_failed.append( dict.toUpper() );
150
kDebug() << "Failed (invalid date):" << dict.toUpper() << endl;
153
else if( localFileDate == webFileDate )
155
// Add it to our 'up to date' list.
156
_succeeded.append( dict.toUpper() );
157
kDebug() << "Success (up to date):" << dict.toUpper() << endl;
162
// Indicate we need to download something.
165
if( dict.toLower() == EDICT )
167
downloadDictionary( EDICT_URL );
171
downloadDictionary( KANJIDIC_URL );
176
// Let Kiten know we finished and don't need to download anything else.
179
emit updateFinished();
182
tempFile.deleteLater();
186
void DictionaryUpdateManager::downloadDictionary( const QString &url )
188
kDebug() << "Download started!" << endl;
189
// Download dictionary.
190
KIO::StoredTransferJob *dictionaryJob = KIO::storedGet( KUrl( url ) );
191
connect( dictionaryJob, SIGNAL( result( KJob* ) ),
192
this, SLOT( installDictionary( KJob* ) ) );
195
QDate DictionaryUpdateManager::getFileDate( QFile &file )
197
if( ! file.open( QIODevice::ReadOnly | QIODevice::Text ) )
199
kDebug() << "Could not open " << file.fileName() << endl;
203
QTextStream fileStream( &file );
204
fileStream.setCodec( QTextCodec::codecForName( "eucJP" ) );
206
// The first line of the file is in the following form:
208
// EDICT, EDICT_SUB(P), EDICT2 Japanese-English Electronic Dictionary Files/
209
// Copyright Electronic Dictionary Research & Development Group - year/
210
// Created: year-month-day/
213
// NOTE: the above last line only appears in the web status file for EDICT.
214
// That file has only 2 lines, one is the '?' simbols with the name of the
215
// dictionary, copyright and creation date, the other one is the number
216
// of entries in the EDICT dictionary.
217
// (see INFO_URL macro).
219
// We take the 4th section which is the last one
220
// separated by the '/' character and the
221
// 10 rightmost characters for that section.
222
QString dateSection = fileStream.readLine()
223
.section( '/', -1, -1, QString::SectionSkipEmpty )
226
// dateSection has the following value: year-month-day
227
// Finally we take the numbers separated by the '-' character.
228
int year = dateSection.section( '-', 0, 0 ).toInt();
229
int month = dateSection.section( '-', 1, 1 ).toInt();
230
int day = dateSection.section( '-', 2, 2 ).toInt();
234
kDebug() << "Date found:" << dateSection << "(" << file.fileName() << ")" << endl;
236
return QDate( year, month, day );
239
void DictionaryUpdateManager::installDictionary( KJob *job )
241
// Increase the number of dictionaries we try to install.
242
// This way we can know when we finished with our installed dictionaries.
245
KIO::StoredTransferJob *storedJob = static_cast<KIO::StoredTransferJob*>( job );
246
QByteArray data( storedJob->data() );
247
QString url( storedJob->url().prettyUrl() );
249
// What we actually downloaded was a GZIP file that we need to extract.
250
// As there is no need to keep this file, we make it a temporary file.
251
QTemporaryFile compressedFile;
252
if( ! compressedFile.open() )
254
kDebug() << "Could not create the downloaded .gz file." << endl;
255
_failed.append( url.contains( EDICT ) ? EDICT : KANJIDIC );
257
checkIfUpdateFinished();
260
// Create the GZIP file from the downloaded data.
261
compressedFile.write( data );
262
compressedFile.close();
264
kDebug() << "Dictionary download finished!" << endl;
265
kDebug() << "Extracting dictionary..." << endl;
267
// Extract the GZIP file.
268
QIODevice *device = KFilterDev::deviceForFile( compressedFile.fileName(), "application/x-gzip" );
269
if( ! device->open( QIODevice::ReadOnly ) )
271
kDebug() << "Could not extract the dictionary file." << endl;
272
_failed.append( url.contains( EDICT ) ? EDICT : KANJIDIC );
275
checkIfUpdateFinished();
279
// Check the first line's first character of the extracted file.
280
// EDICT starts with a ' ', KANJIDIC starts with a '#'.
281
QString fileName = device->readLine().startsWith( '#' ) ? QString( "kanjidic" ) : QString( "edict" );
282
// Reset the position where we are going to start reading the content.
284
// Thanks to the above lines we can get the path to the correct file to be updated.
285
QString dictPath = KGlobal::dirs()->locateLocal( "data"
286
, QString( "kiten/" ) + fileName
288
QFile dictionary( dictPath );
289
if( ! dictionary.open( QIODevice::WriteOnly ) )
291
kDebug() << "Could not create the new dictionary file." << endl;
292
_failed.append( fileName.toUpper() );
296
checkIfUpdateFinished();
300
// Write the new dictionary file to disk.
301
dictionary.write( device->readAll() );
308
// Check if we finished updating.
309
checkIfUpdateFinished();
310
kDebug() << "Successfully installed at:" << dictionary.fileName() << endl;
313
void DictionaryUpdateManager::showUpdateResults()
315
// Avoid multiple calls to this slot.
316
disconnect( this, SIGNAL( updateFinished() ),
317
this, SLOT( showUpdateResults() ) );
319
if( ! _succeeded.isEmpty() && _failed.isEmpty() )
321
KMessageBox::information( 0, i18n( "You already have the latest updates." ) );
323
else if( _succeeded.isEmpty() && _failed.isEmpty() )
325
KMessageBox::information( 0, i18n( "Successfully updated your dictionaries." ) );
327
else if( ! _succeeded.isEmpty() && ! _failed.isEmpty() )
329
KMessageBox::information( 0, i18n( "Successfully updated:\n%1\n\nFailed to update:\n%2"
330
, _succeeded.join( "\n" )
331
, _failed.join( "\n" ) ) );
333
else if( _succeeded.isEmpty() && ! _failed.isEmpty() )
335
KMessageBox::sorry( 0, i18n( "Failed to update:\n%1"
336
, _failed.join( "\n" ) ) );
339
// Avoid repetitions in our lists.
344
#include "dictionaryupdatemanager.moc"