Cache and protect MasterKey - speeds up saving a lot

Added option to save database after every change
Improved license information

git-svn-id: https://svn.code.sf.net/p/keepassx/code/trunk@226 b624d157-de02-0410-bad0-e51aec6abb33
master
sniperbeamer 16 years ago
parent 5fe4a5d2c3
commit 598d204716
  1. 44
      COPYING
  2. 1
      src/Database.h
  3. 72
      src/Kdb3Database.cpp
  4. 11
      src/Kdb3Database.h
  5. 2
      src/KpxConfig.h
  6. 3
      src/crypto/sha256.cpp
  7. 3
      src/crypto/sha256.h
  8. 12
      src/dialogs/SettingsDlg.cpp
  9. 2
      src/dialogs/SettingsDlg.h
  10. 21
      src/forms/SettingsDlg.ui
  11. 4
      src/import/Import_PwManager.cpp
  12. 43
      src/lib/SecString.cpp
  13. 21
      src/lib/SecString.h
  14. 10
      src/lib/tools.cpp
  15. 1
      src/lib/tools.h
  16. 32
      src/mainwindow.cpp

@ -1,11 +1,5 @@
Copyright (C) 2005-2007 Tarek Saidi <tarek.saidi@arcor.de>
Copyright (C) 2003-2007 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2001 Niels Müler
Copyright (C) 1998, 2001, 2002 Free Software Foundation, Inc.
Copyright (C) 2003, 2004 Michael Buesch <mbuesch@freenet.de>
Copyright (c) 2003 Dr Brian Gladman, Worcester, UK
Copyright (C) 2001-2003 Christophe Devine
Copyright (C) 1992-2007 Trolltech ASA
Copyright (C) 2007-2008 Felix Geyer
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -16,6 +10,42 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
text of the GNU General Public License below for more details.
Other licenses:
apg/*:
Copyright (c) 1999, 2000, 2001, 2002, 2003 Adel I. Mirzazhanov
3-clause BSD license
crypto/aes*:
Copyright (c) 1998-2008, Brian Gladman, Worcester
3-clause BSD license
crypto/arcfour*:
Copyright (C) 2003-2008 Dominik Reichl <dominik.reichl@t-online.de>
GPLv2 or later
crypto/blowfish*:
Copyright (C) 1998, 2001, 2002 Free Software Foundation, Inc.
Copyright (C) 2003 by Michael Buesch
Copyright (C) 2007 by Tarek Saidi <tarek.saidi@arcor.de>
GPLv2
crypto/sha256*:
Copyright (C) 2001-2003 by Christophe Devine
Copyright (C) 2005-2006 by Tarek Saidi
GPLv2
crypto/two*:
Copyright (C) 2005-2007 Tarek Saidi <tarek.saidi@arcor.de>
Copyright (c) 2003,2004 Dominik Reichl <dominik.reichl@t-online.de>
GPLv2
crypto/yarrow*:
Copyright (C) 2007 Tarek Saidi <tarek.saidi@arcor.de>
Copyright (C) 2001 Niels Müler
GPLv2
---------------------------------------------------------------------------
GNU GENERAL PUBLIC LICENSE

@ -239,6 +239,7 @@ public:
virtual bool setKey(const QString& password,const QString& keyfile)=0;
virtual bool isKeyError()=0;
virtual void generateMasterKey()=0;
//! Loads a database.
/*! It is not allowed to call this function if a database is already loaded.

@ -40,6 +40,9 @@ bool StdEntryLessThan(const Kdb3Database::StdEntry& This,const Kdb3Database::Std
}
Kdb3Database::Kdb3Database() : RawMasterKey(32), RawMasterKey_Latin1(32), MasterKey(32){
}
QString Kdb3Database::getError(){
return error;
}
@ -477,7 +480,6 @@ void Kdb3Database::restoreGroupTreeState(){
bool Kdb3Database::load(QString filename){
unsigned long total_size,crypto_size;
quint32 Signature1,Signature2,Version,NumGroups,NumEntries,Flags;
quint8 TransfRandomSeed[32];
quint8 FinalRandomSeed[16];
quint8 ContentsHash[32];
quint8 EncryptionIV[16];
@ -531,16 +533,20 @@ else{
LOAD_RETURN_CLEANUP
}
KeyTransform::transform(RawMasterKey,MasterKey,TransfRandomSeed,KeyTransfRounds);
RawMasterKey.unlock();
MasterKey.unlock();
KeyTransform::transform(*RawMasterKey,*MasterKey,TransfRandomSeed,KeyTransfRounds);
quint8 FinalKey[32];
SHA256 sha;
sha.update(FinalRandomSeed,16);
sha.update(MasterKey,32);
sha.update(*MasterKey,32);
sha.finish(FinalKey);
RawMasterKey.lock();
MasterKey.lock();
if(Algorithm == Rijndael_Cipher){
AESdecrypt aes;
aes.key256(FinalKey);
@ -572,7 +578,8 @@ if(memcmp(ContentsHash, FinalKey, 32) != 0){
// but KeePass/Win32 uses Windows Codepage 1252.
// Too stay compatible with databases created with KeePassX <= 0.3.1
// the loading function gives both encodings a try.
memcpy(RawMasterKey,RawMasterKey_Latin1,32);
RawMasterKey.copyData(RawMasterKey_Latin1);
PotentialEncodingIssue=false;
qDebug("Decryption failed. Retrying with Latin-1.");
return load(filename); // second try
@ -829,7 +836,9 @@ bool Kdb3Database::setPasswordKey(const QString& Password){
assert(Password.size());
QTextCodec* codec=QTextCodec::codecForName("Windows-1252");
QByteArray Password_CP1252 = codec->fromUnicode(Password);
SHA256::hashBuffer(Password_CP1252.data(),RawMasterKey,Password_CP1252.size());
RawMasterKey.unlock();
SHA256::hashBuffer(Password_CP1252.data(),*RawMasterKey,Password_CP1252.size());
RawMasterKey.lock();
QByteArray Password_Latin1 = Password.toLatin1();
if(Password_Latin1 != Password_CP1252){
// KeePassX used Latin-1 encoding for passwords until version 0.3.1
@ -837,7 +846,9 @@ bool Kdb3Database::setPasswordKey(const QString& Password){
// Too stay compatible with databases created with KeePassX <= 0.3.1
// the loading function gives both encodings a try.
PotentialEncodingIssue = true;
SHA256::hashBuffer(Password_Latin1.data(),RawMasterKey_Latin1,Password_Latin1.size());
RawMasterKey_Latin1.unlock();
SHA256::hashBuffer(Password_Latin1.data(),*RawMasterKey_Latin1,Password_Latin1.size());
RawMasterKey_Latin1.lock();
}
else {
// If the password does not contain problematic characters we don't need
@ -858,21 +869,27 @@ bool Kdb3Database::setFileKey(const QString& filename){
error=tr("Key file is empty.");
return false;
}
RawMasterKey.unlock();
if(FileSize == 32){
if(file.read((char*)RawMasterKey,32) != 32){
if(file.read((char*)(*RawMasterKey),32) != 32){
error=decodeFileError(file.error());
RawMasterKey.lock();
return false;
}
RawMasterKey.lock();
return true;
}
if(FileSize == 64){
char hex[64];
if(file.read(hex,64) != 64){
error=decodeFileError(file.error());
RawMasterKey.lock();
return false;
}
if (convHexToBinaryKey(hex,(char*)RawMasterKey))
if (convHexToBinaryKey(hex,(char*)(*RawMasterKey))){
RawMasterKey.lock();
return true;
}
}
SHA256 sha;
unsigned char* buffer = new unsigned char[2048];
@ -883,22 +900,25 @@ bool Kdb3Database::setFileKey(const QString& filename){
sha.update(buffer,read);
if(read != 2048) break;
}
sha.finish(RawMasterKey);
sha.finish(*RawMasterKey);
RawMasterKey.lock();
delete [] buffer;
return true;
}
bool Kdb3Database::setCompositeKey(const QString& Password,const QString& filename){
unsigned char PasswordKey[32];
unsigned char FileKey[32];
SHA256 sha;
if(!setFileKey(filename))return false;
memcpy(FileKey,RawMasterKey,32);
RawMasterKey.unlock();
sha.update(*RawMasterKey,32);
RawMasterKey.lock();
setPasswordKey(Password);
memcpy(PasswordKey,RawMasterKey,32);
SHA256 sha;
sha.update(PasswordKey,32);
sha.update(FileKey,32);
sha.finish(RawMasterKey);
RawMasterKey.unlock();
sha.update(*RawMasterKey,32);
sha.finish(*RawMasterKey);
RawMasterKey.lock();
return true;
}
@ -1187,7 +1207,6 @@ bool Kdb3Database::save(){
return false;
}
quint32 NumGroups,NumEntries,Signature1,Signature2,Flags,Version;
quint8 TransfRandomSeed[32];
quint8 FinalRandomSeed[16];
quint8 ContentsHash[32];
quint8 EncryptionIV[16];
@ -1260,7 +1279,6 @@ bool Kdb3Database::save(){
qSort(saveEntries.begin(),saveEntries.end(),StdEntryLessThan);
randomize(FinalRandomSeed,16);
randomize(TransfRandomSeed,32);
randomize(EncryptionIV,16);
unsigned int pos=DB_HEADER_SIZE; // Skip the header, it will be written later
@ -1281,12 +1299,13 @@ bool Kdb3Database::save(){
memcpy(buffer+56,ContentsHash,32);
memcpy(buffer+88,TransfRandomSeed,32);
memcpyToLEnd32(buffer+120,&KeyTransfRounds);
KeyTransform::transform(RawMasterKey,MasterKey,TransfRandomSeed,KeyTransfRounds);
quint8 FinalKey[32];
SHA256 sha;
sha.update(FinalRandomSeed,16);
sha.update(MasterKey,32);
MasterKey.unlock();
sha.update(*MasterKey,32);
MasterKey.lock();
sha.finish(FinalKey);
unsigned long EncryptedPartSize;
@ -1805,6 +1824,15 @@ QList<IEntryHandle*> Kdb3Database::trashEntries(){
return handles;
}
void Kdb3Database::generateMasterKey(){
randomize(TransfRandomSeed,32);
RawMasterKey.unlock();
MasterKey.unlock();
KeyTransform::transform(*RawMasterKey,*MasterKey,TransfRandomSeed,KeyTransfRounds);
RawMasterKey.lock();
MasterKey.lock();
}
void KeyTransform::transform(quint8* src, quint8* dst, quint8* KeySeed, int rounds){
KeyTransform* ktLeft = new KeyTransform(&src[0], &dst[0], KeySeed, rounds);

@ -137,6 +137,7 @@ public:
QStringList GroupPath;
};
Kdb3Database();
virtual ~Kdb3Database(){};
virtual bool load(QString identifier);
virtual bool save();
@ -187,7 +188,8 @@ public:
virtual void moveGroup(IGroupHandle* Group,IGroupHandle* NewParent,int Position);
virtual IGroupHandle* addGroup(const CGroup* Group,IGroupHandle* Parent);
virtual bool isParent(IGroupHandle* parent, IGroupHandle* child);
virtual void generateMasterKey();
@ -241,9 +243,10 @@ private:
QMap<quint32,bool> TreeStateMetaStream;
unsigned int KeyTransfRounds;
CryptAlgorithm Algorithm;
quint8 RawMasterKey[32];
quint8 RawMasterKey_Latin1[32];
quint8 MasterKey[32];
SecData RawMasterKey;
SecData RawMasterKey_Latin1;
SecData MasterKey;
quint8 TransfRandomSeed[32];
bool hasV4IconMetaStream;
};

@ -72,6 +72,7 @@ public:
QString mountDir(){return settings.value("Options/MountDir",DEFAULT_MOUNT_DIR).toString();}
bool openLastFile(){return settings.value("Options/OpenLastFile",true).toBool();}
bool autoSave(){return settings.value("Options/AutoSave",false).toBool();}
bool autoSaveChange(){return settings.value("Options/AutoSaveChange",false).toBool();}
int pwGenCategory(){return settings.value("Options/PwGenCategory",0).toInt();}
QString pwGenCharList(){return settings.value("Options/PwGenCharList").toString();}
int pwGenLength(){return settings.value("Options/PwGenLength",25).toInt();}
@ -132,6 +133,7 @@ public:
void setMountDir(const QString& value){settings.setValue("Options/MountDir",value);}
void setOpenLastFile(bool value){settings.setValue("Options/OpenLastFile",value);}
void setAutoSave(bool value){settings.setValue("Options/AutoSave",value);}
void setAutoSaveChange(bool value){settings.setValue("Options/AutoSaveChange",value);}
void setPwGenCategory(int value){settings.setValue("Options/PwGenCategory",value);}
void setPwGenCharList(const QString& value){settings.setValue("Options/PwGenCharList",value);}
void setPwGenLength(int value){settings.setValue("Options/PwGenLength",value);}

@ -1,7 +1,6 @@
/***************************************************************************
* Copyright (C) 2001-2003 by Christophe Devine *
* Copyright (C) 2005-2006 by Tarek Saidi *
* based on the FIPS-180-2 compliant SHA-256 implementation of *
* Christophe Devine. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *

@ -1,7 +1,6 @@
/***************************************************************************
* Copyright (C) 2001-2003 by Christophe Devine *
* Copyright (C) 2005-2006 by Tarek Saidi *
* based on the FIPS-180-2 compliant SHA-256 implementation of *
* Christophe Devine. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *

@ -51,6 +51,8 @@ CSettingsDlg::CSettingsDlg(QWidget* parent):QDialog(parent,Qt::Dialog)
connect(Button_CustomizeEntryDetails,SIGNAL(clicked()),this,SLOT(OnCustomizeEntryDetails()));
connect(CheckBox_InactivityLock, SIGNAL(toggled(bool)), SLOT(OnInactivityLockChange(bool)));
connect(CheckBox_AutoSave, SIGNAL(toggled(bool)), SLOT(OnAutoSaveToggle(bool)));
connect(CheckBox_AutoSaveChange, SIGNAL(toggled(bool)), SLOT(OnAutoSaveChangeToggle(bool)));
#if !defined(AUTOTYPE)
Box_AutoType->setVisible(false);
@ -76,6 +78,7 @@ CSettingsDlg::CSettingsDlg(QWidget* parent):QDialog(parent,Qt::Dialog)
CheckBox_StartLocked->setChecked(config->startLocked());
checkBox_SaveFileDlgHistory->setChecked(config->saveFileDlgHistory());
CheckBox_AutoSave->setChecked(config->autoSave());
CheckBox_AutoSaveChange->setChecked(config->autoSaveChange());
checkBox_AskBeforeDelete->setChecked(config->askBeforeDelete());
switch(config->groupTreeState()){
@ -211,6 +214,7 @@ void CSettingsDlg::apply(){
config->setOpenLastFile(CheckBox_OpenLast->isChecked());
config->setRememberLastKey(CheckBox_RememberLastKey->isChecked());
config->setAutoSave(CheckBox_AutoSave->isChecked());
config->setAutoSaveChange(CheckBox_AutoSaveChange->isChecked());
config->setAskBeforeDelete(checkBox_AskBeforeDelete->isChecked());
//Appearence
@ -330,6 +334,14 @@ void CSettingsDlg::OnInactivityLockChange(bool checked){
SpinBox_InacitivtyTime->setEnabled(checked);
}
void CSettingsDlg::OnAutoSaveToggle(bool checked){
CheckBox_AutoSaveChange->setEnabled(!checked);
}
void CSettingsDlg::OnAutoSaveChangeToggle(bool checked){
CheckBox_AutoSave->setEnabled(!checked);
}
#ifdef GLOBAL_AUTOTYPE
void CSettingsDlg::resetGlobalShortcut(){
AutoType::unregisterGlobalShortcut();

@ -45,6 +45,8 @@ class CSettingsDlg : public QDialog, private Ui_SettingsDialog
void OnBrowserCmdBrowse();
void OnCustomizeEntryDetails();
void OnInactivityLockChange(bool checked);
void OnAutoSaveToggle(bool checked);
void OnAutoSaveChangeToggle(bool checked);
#ifdef GLOBAL_AUTOTYPE
private slots:

@ -6,7 +6,7 @@
<x>0</x>
<y>0</y>
<width>606</width>
<height>475</height>
<height>479</height>
</rect>
</property>
<property name="windowTitle" >
@ -52,7 +52,7 @@
<x>0</x>
<y>0</y>
<width>584</width>
<height>341</height>
<height>345</height>
</rect>
</property>
<property name="whatsThis" >
@ -290,6 +290,13 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="CheckBox_AutoSaveChange" >
<property name="text" >
<string>Automatically save database after every change</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_AskBeforeDelete" >
<property name="text" >
@ -318,7 +325,7 @@
<x>0</x>
<y>0</y>
<width>584</width>
<height>341</height>
<height>345</height>
</rect>
</property>
<attribute name="title" >
@ -678,7 +685,7 @@
<x>0</x>
<y>0</y>
<width>584</width>
<height>341</height>
<height>345</height>
</rect>
</property>
<attribute name="title" >
@ -840,7 +847,7 @@
<x>0</x>
<y>0</y>
<width>584</width>
<height>341</height>
<height>345</height>
</rect>
</property>
<attribute name="title" >
@ -885,7 +892,7 @@
<x>0</x>
<y>0</y>
<width>584</width>
<height>341</height>
<height>345</height>
</rect>
</property>
<attribute name="title" >
@ -1005,7 +1012,7 @@
<x>0</x>
<y>0</y>
<width>584</width>
<height>341</height>
<height>345</height>
</rect>
</property>
<attribute name="title" >

@ -105,8 +105,8 @@ bool Import_PwManager::importDatabase(QWidget* GuiParent, IDatabase* db){
if(!parseXmlContent((char*)xml)){
delete [] xml;
QMessageBox::critical(GuiParent,tr("Import Failed"),tr("Invalid XML data (see stdout for details).")); return false;}
database->setKey(password,QString());
QMessageBox::critical(GuiParent,tr("Import Failed"),tr("Invalid XML data (see stdout for details).")); return false;
}
return true;
}

@ -98,3 +98,46 @@ void SecString::generateSessionKey(){
randomize(sessionkey, 32);
RC4.setKey(sessionkey, 32);
}
SecData::SecData(int len) : locked(true){
length = len;
data = new quint8[len];
}
SecData::~SecData(){
if (!locked){
for (int i=0; i<length; i++)
data[i] = 0;
}
delete data;
}
void SecData::lock(){
Q_ASSERT(!locked);
SecString::RC4.encrypt(data, data, length);
locked = true;
}
void SecData::unlock(){
Q_ASSERT(locked);
SecString::RC4.decrypt(data, data, length);
locked = false;
}
void SecData::copyData(quint8* src){
unlock();
memcpy(data, src, length);
lock();
}
void SecData::copyData(SecData& secData){
secData.unlock();
copyData(*secData);
secData.lock();
}
quint8* SecData::operator*(){
Q_ASSERT(!locked);
return data;
}

@ -22,11 +22,16 @@
#include "crypto/arcfour.h"
class SecData;
//! QString based class with in-memory encryption of its content.
/*!
This class can hold a QString object in an encrypted buffer. To get access to the string it is neccassary to unlock the SecString object.
*/
class SecString{
friend class SecData;
public:
SecString();
~SecString();
@ -56,5 +61,21 @@ private:
};
class SecData{
public:
SecData(int len);
~SecData();
void lock();
void unlock();
void copyData(quint8* src);
void copyData(SecData& secData);
quint8* operator*();
private:
quint8* data;
int length;
bool locked;
};
#endif

@ -224,3 +224,13 @@ bool lockPage(void* addr, int len){
return false;
#endif
}
bool unlockPage(void* addr, int len){
#if defined(Q_WS_X11) || defined(Q_WS_MAC)
return (munlock(addr, len)==0);
#elif defined(Q_WS_WIN)
return VirtualUnlock(addr, len);
#else
return false;
#endif
}

@ -34,5 +34,6 @@ QString makePathRelative(const QString& Abs,const QString& Cur);
QString getImageFile(const QString& name);
bool createKeyFile(const QString& filename,QString* err, int length=32, bool Hex=true);
bool lockPage(void* addr, int len);
bool unlockPage(void* addr, int len);
#endif //TOOLS_H

@ -488,12 +488,12 @@ bool KeepassMainWindow::closeDatabase(bool lock){
Q_ASSERT(FileOpen);
Q_ASSERT(db!=NULL);
if(ModFlag){
if(config->autoSave()){
if(config->autoSave() && db->file()){
if(!OnFileSave()) return false;
}
else{
QMessageBox::StandardButton r=QMessageBox::question(this,tr("Save modified file?"),
tr("The current file was modified. Do you want\nto save the changes?"),
tr("The current file was modified.\nDo you want to save the changes?"),
QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel, QMessageBox::Yes);
if(r==QMessageBox::Cancel) return false; //Cancel
if(r==QMessageBox::Yes){ //Yes (Save file)
@ -536,8 +536,9 @@ void KeepassMainWindow::OnFileNewKdb(){
if(!closeDatabase())return;
if (IsLocked)
resetLock();
db=db_new;
db->setKey(dlg.password(),dlg.keyFile());
db=db_new;
db->setKey(dlg.password(),dlg.keyFile());
db->generateMasterKey();
setWindowTitle(QString("[%1][*] - KeePassX").arg(tr("new")));
GroupView->db=db;
EntryView->db=db;
@ -545,7 +546,6 @@ void KeepassMainWindow::OnFileNewKdb(){
EntryView->showGroup(NULL);
setStateFileOpen(true);
setStateFileModified(true);
FileOpen=true;
setupDatabaseConnections(db);
setStateGroupSelected(NONE);
setStateEntrySelected(NONE);
@ -592,6 +592,7 @@ void KeepassMainWindow::setStateFileOpen(bool IsOpen){
FileCloseAction->setEnabled(IsOpen||IsLocked);
FileSettingsAction->setEnabled(IsOpen);
FileChangeKeyAction->setEnabled(IsOpen);
menuExport->setEnabled(IsOpen);
EditSearchAction->setEnabled(IsOpen);
GroupView->setEnabled(IsOpen);
EntryView->setEnabled(IsOpen);
@ -627,10 +628,11 @@ void KeepassMainWindow::setStateFileOpen(bool IsOpen){
void KeepassMainWindow::setStateFileModified(bool mod){
if(!FileOpen){
FileSaveAction->setIcon(getIcon("filesave"));
return;
if (config->autoSaveChange() && mod && db->file()){
OnFileSave();
mod = false;
}
ModFlag=mod;
if(mod)
FileSaveAction->setIcon(getIcon("filesave"));
@ -884,7 +886,10 @@ bool KeepassMainWindow::OnFileSaveAs(){
void KeepassMainWindow::OnFileSettings(){
CDbSettingsDlg dlg(this,db);
if(dlg.exec()) setStateFileModified(true);
if(dlg.exec()){
db->generateMasterKey();
setStateFileModified(true);
}
}
void KeepassMainWindow::OnFileChangeKey(){
@ -892,8 +897,9 @@ void KeepassMainWindow::OnFileChangeKey(){
QString filename = file ? file->fileName() : QString();
PasswordDialog dlg(this,PasswordDialog::Mode_Change,PasswordDialog::Flag_None,filename);
if(dlg.exec()==PasswordDialog::Exit_Ok){
setStateFileModified(true);
db->setKey(dlg.password(),dlg.keyFile());
db->generateMasterKey();
setStateFileModified(true);
}
}
@ -920,6 +926,7 @@ void KeepassMainWindow::OnImport(QAction* action){
}
db=tmpdb;
db->setKey(dlg.password(),dlg.keyFile());
db->generateMasterKey();
GroupView->db=db;
EntryView->db=db;
setupDatabaseConnections(db);
@ -989,7 +996,7 @@ void KeepassMainWindow::OnUsernPasswVisibilityChanged(bool value){
}
void KeepassMainWindow::OnFileModified(){
setStateFileModified(true);
setStateFileModified(true);
}
void KeepassMainWindow::closeEvent(QCloseEvent* e){
@ -1017,7 +1024,8 @@ void KeepassMainWindow::closeEvent(QCloseEvent* e){
if(!closeDatabase()){
ShutingDown=false;
e->ignore();
return;}
return;
}
else
e->accept();
}