summaryrefslogtreecommitdiffstats
path: root/kopete/libkopete/kopetepassword.cpp
blob: f0b788a9c2988e5985a53e127445c48dbca01530 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
/*
    kopetepassword.cpp - Kopete Password

    Copyright (c) 2004      by Richard Smith         <[email protected]>
    Kopete    (c) 2002-2004 by the Kopete developers <[email protected]>

    *************************************************************************
    *                                                                       *
    * This library is free software; you can redistribute it and/or         *
    * modify it under the terms of the GNU Lesser General Public            *
    * License as published by the Free Software Foundation; either          *
    * version 2 of the License, or (at your option) any later version.      *
    *                                                                       *
    *************************************************************************
*/

#include "kopeteuiglobal.h"
#include "kopetepassword.h"
#include "kopetepassworddialog.h"
#include "kopetewalletmanager.h"

#include <kwallet.h>

#include <qapplication.h>
#include <qlabel.h>
#include <qlineedit.h>
#include <qcheckbox.h>

#include <kactivelabel.h>
#include <kapplication.h>
#include <kconfig.h>
#include <kdebug.h>
#include <kdialogbase.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kiconloader.h>
#include <kpassdlg.h>
#include <kstringhandler.h>

class Kopete::Password::Private
{
public:
	Private( const QString &group, uint maxLen, bool blanksAllowed )
	 : refCount( 1 ), configGroup( group ), remembered( false ), maximumLength( maxLen ),
	 isWrong( false ), allowBlankPassword( blanksAllowed )
	{
	}
	Private *incRef()
	{
		++refCount;
		return this;
	}
	void decRef()
	{
		if( --refCount == 0 )
			delete this;
	}
	/** Reference count */
	int refCount;
	/** Group to use for KConfig and KWallet */
	const QString configGroup;
	/** Is the password being remembered? */
	bool remembered;
	/** The current password in the KConfig file, or QString::null if no password there */
	QString passwordFromKConfig;
	/** The maximum length allowed for this password, or -1 if there is no limit */
	uint maximumLength;
	/** Is the current password known to be wrong? */
	bool isWrong;
	/** Are we allowed to have blank passwords? */
	bool allowBlankPassword;
	/** The cached password */
	QString cachedValue;
};

/**
 * Implementation detail of Kopete::Password: manages a single password request
 * @internal
 * @author Richard Smith <[email protected]>
 */
class KopetePasswordRequest : public KopetePasswordRequestBase
{
public:
	KopetePasswordRequest( QObject *owner, Kopete::Password &pass )
	 : QObject( owner ), mPassword( pass ), mWallet( 0 )
	{
	}

	/**
	 * Start the request - ask for the wallet
	 */
	void begin()
	{
		kdDebug( 14010 ) << k_funcinfo << endl;
		Kopete::WalletManager::self()->openWallet( this, SLOT( walletReceived( KWallet::Wallet* ) ) );
	}

	void walletReceived( KWallet::Wallet *wallet )
	{
		kdDebug( 14010 ) << k_funcinfo << endl;
		mWallet = wallet;
		processRequest();
	}

	/**
	 * Got wallet; now carry out whatever action this request represents
	 */
	virtual void processRequest() = 0;

	void slotOkPressed() {}
	void slotCancelPressed() {}

protected:
	Kopete::Password mPassword;
	KWallet::Wallet *mWallet;
};

/**
 * Implementation detail of Kopete::Password: manages a single password retrieval request
 * @internal
 * @author Richard Smith <[email protected]>
 */
class KopetePasswordGetRequest : public KopetePasswordRequest
{
public:
	KopetePasswordGetRequest( QObject *owner, Kopete::Password &pass )
	 : KopetePasswordRequest( owner, pass )
	{
	}

	QString grabPassword()
	{
		// Before trying to read from the wallet, check if the config file holds a password.
		// If so, remove it from the config and set it through KWallet instead.
		QString pwd;
		if ( mPassword.d->remembered && !mPassword.d->passwordFromKConfig.isNull() )
		{
			pwd = mPassword.d->passwordFromKConfig;
			mPassword.set( pwd );
			return pwd;
		}

		if ( mWallet && mWallet->readPassword( mPassword.d->configGroup, pwd ) == 0 && !pwd.isNull() )
			return pwd;

		if ( mPassword.d->remembered && !mPassword.d->passwordFromKConfig.isNull() )
			return mPassword.d->passwordFromKConfig;

		return QString::null;
	}

	void finished( const QString &result )
	{
		mPassword.d->cachedValue = result;
		emit requestFinished( result );
		delete this;
	}
};

class KopetePasswordGetRequestPrompt : public KopetePasswordGetRequest
{
public:
	KopetePasswordGetRequestPrompt( QObject *owner, Kopete::Password &pass,  const QPixmap &image, const QString &prompt, Kopete::Password::PasswordSource source )
	 : KopetePasswordGetRequest( owner, pass ), mImage( image ), mPrompt( prompt ), mSource( source ), mView( 0 )
	{
	}

	void processRequest()
	{
		QString result = grabPassword();
		if ( mSource == Kopete::Password::FromUser || result.isNull() )
			doPasswordDialog();
		else
			finished( result );
	}

	void doPasswordDialog()
	{
		kdDebug( 14010 ) << k_funcinfo << endl;

		KDialogBase *passwdDialog = new KDialogBase( Kopete::UI::Global::mainWidget(), "passwdDialog", true, i18n( "Password Required" ),
			KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, true );

		mView = new KopetePasswordDialog( passwdDialog );
		passwdDialog->setMainWidget( mView );

		mView->m_text->setText( mPrompt );
		mView->m_image->setPixmap( mImage );
		/* Do not put the default password, or it will confuse those which doesn't echo anything for the password
		mView->m_password->insert( password );
		*/
		int maxLength = mPassword.maximumLength();
		if ( maxLength != 0 )
			mView->m_password->setMaxLength( maxLength );
		mView->m_password->setFocus();

		// FIXME: either document what these are for or remove them - lilac
		mView->adjustSize();
		passwdDialog->adjustSize();

		connect( passwdDialog, SIGNAL( okClicked() ), SLOT( slotOkPressed() ) );
		connect( passwdDialog, SIGNAL( cancelClicked() ), SLOT( slotCancelPressed() ) );
		connect( this, SIGNAL( destroyed() ), passwdDialog, SLOT( deleteLater() ) );
		passwdDialog->show();
	}

	void slotOkPressed()
	{
		QString result = QString::fromLocal8Bit( mView->m_password->password() );
		if ( mView->m_save_passwd->isChecked() )
			mPassword.set( result );

		finished( result );
	}

	void slotCancelPressed()
	{
		finished( QString::null );
	}

private:
	QPixmap mImage;
	QString mPrompt;
	Kopete::Password::PasswordSource mSource;
	unsigned int mMaxLength;
	KopetePasswordDialog *mView;
};

class KopetePasswordGetRequestNoPrompt : public KopetePasswordGetRequest
{
public:
	KopetePasswordGetRequestNoPrompt( QObject *owner, Kopete::Password &pass )
	 : KopetePasswordGetRequest( owner, pass )
	{
	}

	void processRequest()
	{
		finished( grabPassword() );
	}
};

/**
 * Implementation detail of Kopete::Password: manages a single password change request
 * @internal
 * @author Richard Smith <[email protected]>
 */
class KopetePasswordSetRequest : public KopetePasswordRequest
{
public:
	KopetePasswordSetRequest( Kopete::Password &pass, const QString &newPass )
	 : KopetePasswordRequest( 0, pass ), mNewPass( newPass )
	{
		if ( KApplication *app = KApplication::kApplication() )
			app->ref();
	}
	~KopetePasswordSetRequest()
	{
		if ( KApplication *app = KApplication::kApplication() )
			app->deref();
		kdDebug( 14010 ) << k_funcinfo << "job complete" << endl;
	}
	void processRequest()
	{
		if ( setPassword() )
		{
			mPassword.setWrong( false );
			mPassword.d->cachedValue = mNewPass;
		}
		delete this;
	}
	bool setPassword()
	{
		kdDebug( 14010 ) << k_funcinfo << " setting password for " << mPassword.d->configGroup << endl;

		if ( mWallet && mWallet->writePassword( mPassword.d->configGroup, mNewPass ) == 0 )
		{
			mPassword.d->remembered = true;
			mPassword.d->passwordFromKConfig = QString::null;
			mPassword.writeConfig();
			return true;
		}

		if ( KWallet::Wallet::isEnabled() )
		{
			// If we end up here, the wallet is enabled, but failed somehow.
			// Ask the user what to do now.

			//NOTE: This will start a nested event loop. However, this is fine; the only code we
			// call after this point is in Kopete::Password, so as long as we've not been deleted
			// everything should work out OK. We have no parent QObject, so we should survive.
			if ( KMessageBox::warningContinueCancel( Kopete::UI::Global::mainWidget(),
			        i18n( "<qt>Kopete is unable to save your password securely in your wallet;<br>"
			              "do you want to save the password in the <b>unsafe</b> configuration file instead?</qt>" ),
			        i18n( "Unable to Store Secure Password" ),
			        KGuiItem( i18n( "Store &Unsafe" ), QString::fromLatin1( "unlock" ) ),
			        QString::fromLatin1( "KWalletFallbackToKConfig" ) ) != KMessageBox::Continue )
			{
				return false;
			}
		}
		mPassword.d->remembered = true;
		mPassword.d->passwordFromKConfig = mNewPass;
		mPassword.writeConfig();
		return true;
	}

private:
	QString mNewPass;
};

class KopetePasswordClearRequest : public KopetePasswordRequest
{
public:
	KopetePasswordClearRequest( Kopete::Password &pass )
	 : KopetePasswordRequest( 0, pass )
	{
		if ( KApplication *app = KApplication::kApplication() )
			app->ref();
	}
	~KopetePasswordClearRequest()
	{
		if ( KApplication *app = KApplication::kApplication() )
			app->deref();
		kdDebug( 14010 ) << k_funcinfo << "job complete" << endl;
	}
	void processRequest()
	{
		if ( clearPassword() )
		{
			mPassword.setWrong( true );
			mPassword.d->cachedValue = QString::null;
		}

		delete this;
	}
	bool clearPassword()
	{
		kdDebug( 14010 ) << k_funcinfo << " clearing password" << endl;

		mPassword.d->remembered = false;
		mPassword.d->passwordFromKConfig = QString::null;
		mPassword.writeConfig();
		if ( mWallet )
			mWallet->removeEntry( mPassword.d->configGroup );
		return true;
	}
};

Kopete::Password::Password( const QString &configGroup, uint maximumLength, const char *name )
 : QObject( 0, name ), d( new Private( configGroup, maximumLength, false ) )
{
	readConfig();
}

Kopete::Password::Password( const QString &configGroup, uint maximumLength,
	bool allowBlankPassword, const char *name )
 : QObject( 0, name ), d( new Private( configGroup, maximumLength, allowBlankPassword ) )
{
	readConfig();
}

Kopete::Password::Password( Password &other, const char *name )
 : QObject( 0, name ), d( other.d->incRef() )
{
}

Kopete::Password::~Password()
{
	d->decRef();
}

Kopete::Password &Kopete::Password::operator=( Password &other )
{
	if ( d == other.d ) return *this;
	d->decRef();
	d = other.d->incRef();
	return *this;
}

void Kopete::Password::readConfig()
{
	KConfig *config = KGlobal::config();
	config->setGroup( d->configGroup );

	QString passwordCrypted = config->readEntry( "Password" );
	if ( passwordCrypted.isNull() )
		d->passwordFromKConfig = QString::null;
	else
		d->passwordFromKConfig = KStringHandler::obscure( passwordCrypted );

	d->remembered = config->readBoolEntry( "RememberPassword", false );
	d->isWrong = config->readBoolEntry( "PasswordIsWrong", false );
}

void Kopete::Password::writeConfig()
{
	KConfig *config = KGlobal::config();
	if(!config->hasGroup(d->configGroup))
	{
		//### (KOPETE)
		// if the kopete account has been removed, we have no way to know it.
		//  but we don't want in any case to recreate the group.
		//  see Bug 106460
		// (the problem is that when we remove the account, we remove the password
		//  also, which cause a call to this function )
		return;
	}
		  
	config->setGroup( d->configGroup );

	if ( d->remembered && !d->passwordFromKConfig.isNull() )
		config->writeEntry( "Password", KStringHandler::obscure( d->passwordFromKConfig ) );
	else
		config->deleteEntry( "Password" );

	config->writeEntry( "RememberPassword", d->remembered );
	config->writeEntry( "PasswordIsWrong", d->isWrong );
}

int Kopete::Password::preferredImageSize()
{
	return IconSize(KIcon::Toolbar);
}

bool Kopete::Password::allowBlankPassword()
{
	return d->allowBlankPassword;
}

uint Kopete::Password::maximumLength()
{
	return d->maximumLength;
}

void Kopete::Password::setMaximumLength( uint max )
{
	d->maximumLength = max;
}

bool Kopete::Password::isWrong()
{
	return d->isWrong;
}

void Kopete::Password::setWrong( bool bWrong )
{
	d->isWrong = bWrong;
	writeConfig();

	if ( bWrong ) d->cachedValue = QString::null;
}

void Kopete::Password::requestWithoutPrompt( QObject *returnObj, const char *slot )
{
	KopetePasswordRequest *request = new KopetePasswordGetRequestNoPrompt( returnObj, *this );
	// call connect on returnObj so we can still connect if 'slot' is protected/private
	returnObj->connect( request, SIGNAL( requestFinished( const QString & ) ), slot );
	request->begin();
}

void Kopete::Password::request( QObject *returnObj, const char *slot, const QPixmap &image, const QString &prompt, Kopete::Password::PasswordSource source )
{
	KopetePasswordRequest *request = new KopetePasswordGetRequestPrompt( returnObj, *this, image, prompt, source );
	returnObj->connect( request, SIGNAL( requestFinished( const QString & ) ), slot );
	request->begin();
}

QString Kopete::Password::cachedValue()
{
	return d->cachedValue;
}

void Kopete::Password::set( const QString &pass )
{
	// if we're being told to forget the password, and we aren't remembering one,
	// don't try to open the wallet. fixes bug #71804.
	if( pass.isNull() && !d->allowBlankPassword )
	{
		if( remembered() )
			clear();
		return;
	}

	KopetePasswordRequest *request = new KopetePasswordSetRequest( *this, pass );
	request->begin();
}

void Kopete::Password::clear()
{
	KopetePasswordClearRequest *request = new KopetePasswordClearRequest( *this );
	request->begin();
}

bool Kopete::Password::remembered()
{
	return d->remembered;
}

#include "kopetepassword.moc"

// vim: set noet ts=4 sts=4 sw=4: