1
/***************************************************************************
2
* Copyright (C) 2004-2009 by Thomas Fischer *
3
* fischer@unix-ag.uni-kl.de *
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 *
17
* Free Software Foundation, Inc., *
18
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19
***************************************************************************/
21
#include <qapplication.h>
28
#include <klineedit.h>
31
#include <kmessagebox.h>
37
#include "webquerygooglescholar.h"
41
WebQueryGoogleScholarWidget::WebQueryGoogleScholarWidget( QWidget *parent, const char *name )
42
: WebQueryWidget( parent, name )
46
Settings *settings = Settings::self();
47
QString value = settings->getWebQueryDefault( "GoogleScholar" );
48
value = value == QString::null ? "" : value;
49
lineEditQuery->setText( value );
50
slotTextChanged( value, true );
53
WebQueryGoogleScholar::WebQueryGoogleScholar( QWidget* parent )
54
: WebQuery( parent ), m_transferJob( NULL ), m_transferJobBuffer( NULL )
56
m_importer = new BibTeX::FileImporterBibTeX( FALSE );
57
m_importer->setIgnoreComments( TRUE );
58
m_widget = new WebQueryGoogleScholarWidget( parent );
61
WebQueryGoogleScholar::~WebQueryGoogleScholar()
67
QString WebQueryGoogleScholar::title()
69
return i18n( "Google Scholar" );
72
QString WebQueryGoogleScholar::disclaimer()
74
return i18n( "About Google Scholar" );
77
QString WebQueryGoogleScholar::disclaimerURL()
79
return "http://scholar.google.com/intl/en/scholar/about.html";
82
WebQueryWidget *WebQueryGoogleScholar::widget()
87
void WebQueryGoogleScholar::query()
91
/** save search term in settings */
92
Settings *settings = Settings::self();
93
settings->setWebQueryDefault( "GoogleScholar", m_widget->lineEditQuery->text() );
95
/** generate web-save search term */
96
m_searchTerm = m_widget->lineEditQuery->text().stripWhiteSpace().replace( '$', "" );
97
m_searchTerm = m_searchTerm.replace( "%", "%25" ).replace( "+", "%2B" ).replace( " ", "%20" ).replace( "#", "%23" ).replace( "&", "%26" ).replace( "?", "%3F" );
98
if ( m_searchTerm.isEmpty() )
100
setEndSearch( WebQuery::statusInvalidQuery );
104
/** initialize variables */
106
m_numberOfResults = m_widget->spinBoxMaxHits->value();
107
setNumStages( m_numberOfResults + 5 );
109
/** reset KDE configuration for cookie handling */
110
readAndChangeConfig();
112
/** prepare HTTP request (buffer, signals, job) */
113
m_transferJobBuffer = new QBuffer();
114
m_transferJobBuffer->open( IO_WriteOnly );
115
KIO::TransferJob* m_transferJob = KIO::get( KURL( "http://scholar.google.com/scholar_ncr" ), false, false );
116
connect( m_transferJob, SIGNAL( data( KIO::Job *, const QByteArray & ) ), this, SLOT( slotData( KIO::Job *, const QByteArray & ) ) );
117
connect( m_transferJob, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotFinishedStartpage( KIO::Job * ) ) );
120
void WebQueryGoogleScholar::cancelQuery()
122
/** user aborted search */
124
if ( m_transferJob != NULL ) m_transferJob->kill( false );
125
setEndSearch( WebQuery::statusError );
128
void WebQueryGoogleScholar::slotFinishedStartpage( KIO::Job *job )
130
/** close and delete buffer (content does not matter) */
131
m_transferJobBuffer->close();
132
delete m_transferJobBuffer;
134
/** if aborted in the mean time, clean up everything */
141
/** error occurred */
142
if ( job->error() != 0 )
145
kdDebug() << "Error in slotFinishedStartpage: " << job->error() << endl;
146
setEndSearch( statusError );
150
/** update progress bar */
153
/** prepare next HTTP request for preferences page (buffer, signals, job) */
154
m_transferJobBuffer = new QBuffer();
155
m_transferJobBuffer->open( IO_WriteOnly );
156
KIO::TransferJob* m_transferJob = KIO::get( KURL( "http://scholar.google.com/scholar_preferences?hl=en" ), false, false );
157
connect( m_transferJob, SIGNAL( data( KIO::Job *, const QByteArray & ) ), this, SLOT( slotData( KIO::Job *, const QByteArray & ) ) );
158
connect( m_transferJob, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotFinishedLoadingSettings( KIO::Job * ) ) );
162
void WebQueryGoogleScholar::slotFinishedLoadingSettings( KIO::Job *job )
164
/** close and delete buffer (content does not matter) */
165
m_transferJobBuffer->close();
166
QString htmlCode = textFromBuffer( m_transferJobBuffer );
167
delete m_transferJobBuffer;
169
/** if aborted in the mean time, clean up everything */
176
/** error occurred */
177
if ( job->error() != 0 )
180
kdDebug() << "Error in slotFinishedLoadingSettings: " << job->error() << endl;
181
setEndSearch( statusError );
185
/** update progress bar */
188
/** parse html code to get form values */
189
QMap<QString, QString> keyValues = evalFormFields( htmlCode );
190
/** set form values for BibTeX search */
191
keyValues["scis"] = "yes";
192
keyValues["scisf"] = "4";
193
keyValues["submit"] = "Save+Preferences";
194
keyValues["num"] = QString::number( m_numberOfResults );
196
/** prepare next HTTP request to submit preferences (buffer, signals, job) */
197
KURL nextUrl( formFieldsToUrl( "http://scholar.google.com/scholar_setprefs", keyValues ) );
198
m_transferJobBuffer = new QBuffer();
199
m_transferJobBuffer->open( IO_WriteOnly );
200
KIO::TransferJob* m_transferJob = KIO::get( nextUrl, false, false );
201
connect( m_transferJob, SIGNAL( data( KIO::Job *, const QByteArray & ) ), this, SLOT( slotData( KIO::Job *, const QByteArray & ) ) );
202
connect( m_transferJob, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotFinishedSavingSettings( KIO::Job * ) ) );
205
void WebQueryGoogleScholar::slotFinishedSavingSettings( KIO::Job *job )
207
/** close and delete buffer (content does not matter) */
208
m_transferJobBuffer->close();
209
QString htmlCode = textFromBuffer( m_transferJobBuffer );
210
delete m_transferJobBuffer;
212
/** if aborted in the mean time, clean up everything */
219
/** error occurred */
220
if ( job->error() != 0 )
223
kdDebug() << "Error in slotFinishedSavingSettings: " << job->error() << endl;
224
setEndSearch( statusError );
228
/** update progress bar */
231
/** parse html code to get form values */
232
QMap<QString, QString> keyValues = evalFormFields( htmlCode );
233
/** set form values for search */
234
keyValues["q"] = m_searchTerm;
235
keyValues["num"] = QString::number( m_numberOfResults );
237
/** prepare next HTTP request for actual search (buffer, signals, job) */
238
KURL nextUrl( formFieldsToUrl( "http://scholar.google.com/scholar", keyValues ) );
239
m_transferJobBuffer = new QBuffer();
240
m_transferJobBuffer->open( IO_WriteOnly );
241
KIO::TransferJob* m_transferJob = KIO::get( nextUrl, false, false );
242
connect( m_transferJob, SIGNAL( data( KIO::Job *, const QByteArray & ) ), this, SLOT( slotData( KIO::Job *, const QByteArray & ) ) );
243
connect( m_transferJob, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotFinishedReceivingResultOverview( KIO::Job * ) ) );
246
void WebQueryGoogleScholar::slotFinishedReceivingResultOverview( KIO::Job *job )
248
/** close and delete buffer (content does not matter) */
249
m_transferJobBuffer->close();
250
QString htmlCode = textFromBuffer( m_transferJobBuffer );
251
delete m_transferJobBuffer;
253
/** if aborted in the mean time, clean up everything */
260
/** error occurred */
261
if ( job->error() != 0 )
264
kdDebug() << "Error in slotFinishedReceivingResultOverview: " << job->error() << endl;
265
setEndSearch( statusError );
269
/** update progress bar */
272
/** find all links to BibTeX files in result page */
273
QRegExp reBibUrl( "/scholar.bib[^ \">]+" );
275
while ( !m_aborted && ( pos = htmlCode.find( reBibUrl, pos + 1 ) ) > 0 )
277
/** download individual BibTeX file for each search hit */
278
KURL bibUrl( "http://scholar.google.com" + reBibUrl.cap( 0 ).replace( "&", "&" ) );
279
BibTeX::File *tmpBibFile = downloadBibTeXFile( bibUrl );
281
/** update progress bar */
284
/** parse, evaluate and store first BibTeX entry */
285
if ( tmpBibFile != NULL )
287
BibTeX::File::ElementList::iterator it = tmpBibFile->begin();
288
if ( it != tmpBibFile->end() )
290
BibTeX::Entry *entry = dynamic_cast<BibTeX::Entry*>( *it );
292
emit foundEntry( new BibTeX::Entry( entry ), false );
298
/** restore old cookie configuration */
301
/** set result status */
303
setEndSearch( statusAborted );
305
setEndSearch( statusSuccess );
308
void WebQueryGoogleScholar::readAndChangeConfig()
310
KConfig cfg( "kcookiejarrc" );
311
cfg.setGroup( "Cookie Policy" );
312
m_originalEnableCookies = cfg.readBoolEntry( "Cookies", true );
313
m_originalSessionCookies = cfg.readBoolEntry( "AcceptSessionCookies", true );
314
QStringList cookieSettingsList = QStringList::split( ',', cfg.readEntry( "CookieDomainAdvice", "" ) );
315
m_originalCookieGlobalAdvice = cfg.readEntry( "CookieGlobalAdvice", "Accept" );
317
for ( QStringList::Iterator it = cookieSettingsList.begin(); it != cookieSettingsList.end(); ++it )
319
QStringList keyValue = QStringList::split( ':', *it );
320
if ( keyValue.size() == 2 )
322
m_originalCookieMap[keyValue[0]] = keyValue[1];
326
cfg.writeEntry( "Cookies", true );
327
cfg.writeEntry( "CookieGlobalAdvice", "Accept" );
328
cfg.writeEntry( "AcceptSessionCookies", true );
329
cookieSettingsList.clear();
330
for ( QMap<QString, QString>::Iterator it = m_originalCookieMap.begin(); it != m_originalCookieMap.end(); ++it )
332
QString value = it.key().contains( ".google." ) ? "Accept" : it.data();
333
cookieSettingsList << it.key() + ":" + value;
335
cfg.writeEntry( "CookieDomainAdvice", cookieSettingsList.join( "," ) );
338
( void )DCOPRef( "kded", "kcookiejar" ).send( "reloadPolicy" );
341
void WebQueryGoogleScholar::restoreConfig()
343
KConfig cfg( "kcookiejarrc" );
344
cfg.setGroup( "Cookie Policy" );
345
cfg.writeEntry( "CookieGlobalAdvice", m_originalCookieGlobalAdvice );
346
cfg.writeEntry( "Cookies", m_originalEnableCookies );
347
cfg.writeEntry( "AcceptSessionCookies", m_originalSessionCookies );
348
QStringList cookieSettingsList;
349
for ( QMap<QString, QString>::Iterator it = m_originalCookieMap.begin(); it != m_originalCookieMap.end(); ++it )
350
cookieSettingsList << it.key() + ":" + it.data();
351
cfg.writeEntry( "CookieDomainAdvice", cookieSettingsList.join( "," ) );
354
if ( !m_originalEnableCookies )
355
( void )DCOPRef( "kded", "kcookiejar" ).send( "shutdown" );
357
( void )DCOPRef( "kded", "kcookiejar" ).send( "reloadPolicy" );
360
QString WebQueryGoogleScholar::textFromBuffer( QBuffer *buffer )
362
QString htmlCode = "";
363
buffer->open( IO_ReadOnly );
364
QTextStream ts( buffer );
365
while ( !ts.atEnd() )
366
htmlCode.append( ts.readLine() );
371
QMap <QString, QString> WebQueryGoogleScholar::evalFormFields( const QString &htmlCode )
373
QMap<QString, QString> keyValues;
375
QRegExp reInput( "<input[^>]+>" );
376
QRegExp reSplit( "[<>=\" ]+" );
378
while (( pos = htmlCode.find( reInput, pos + 1 ) ) > 5 )
380
QStringList elements = QStringList::split( reSplit, reInput.cap( 0 ) );
381
bool checked = false;
382
bool isCheckable = false;
383
bool isSubmit = false;
384
QString key = QString::null;
385
QString value = QString::null;
386
for ( QStringList::Iterator it = elements.begin(); it != elements.end(); ++it )
390
++it; if ( it != elements.end() ) key = *it; else break;
392
if ( *it == "value" )
394
++it; if ( it != elements.end() ) value = *it; else
399
if ( *it == "checked" )
404
if ( it == elements.end() ) break;
405
isCheckable = *it == "radio" || *it == "checkbox";
406
isSubmit = *it == "submit";
409
if (( !isCheckable || checked ) && ( !isSubmit || value == "submit" ) && value != QString::null && key != QString::null )
411
keyValues[key] = value;
415
QRegExp reSelect( "<select name=([^ >\"]+).*</select>" );
416
reSelect.setMinimal( true );
417
QRegExp reOption( "<option[^>]+>" );
419
while (( pos3 = htmlCode.find( reSelect, pos3 + 1 ) ) > 5 )
421
QString key = reSelect.cap( 1 );
422
QString sub = reSelect.cap( 0 );
424
while (( pos2 = sub.find( reOption, pos2 + 1 ) ) > 5 )
426
QStringList elements = QStringList::split( reSplit, reOption.cap( 0 ) );
427
bool selected = false;
428
QString value = QString::null;
429
for ( QStringList::Iterator it = elements.begin(); it != elements.end(); ++it )
431
if ( *it == "value" )
433
++it; if ( it != elements.end() ) value = *it; else
438
if ( *it == "selected" )
441
if ( selected && value != QString::null && key != QString::null )
443
keyValues[key] = value;
451
QString WebQueryGoogleScholar::formFieldsToUrl( const QString &prefix, const QMap<QString, QString> &keyValues )
454
QString nextUrl = prefix;
455
for ( QMap<QString, QString>::ConstIterator it = keyValues.begin(); it != keyValues.end(); ++it )
458
nextUrl.append( "?" );
460
nextUrl.append( "&" );
462
nextUrl.append( it.key() + "=" + it.data() );