Fix: Different qm files in different paths, no overruling (Bug #2657158)

Fix: Unable to open kdb from read-only location (Bug #2657228)

git-svn-id: https://svn.code.sf.net/p/keepassx/code/trunk@279 b624d157-de02-0410-bad0-e51aec6abb33
master
sniperbeamer 16 years ago
parent df17d76ce7
commit 139078801c
  1. 2
      src/Database.h
  2. 35
      src/Kdb3Database.cpp
  3. 4
      src/Kdb3Database.h
  4. 3
      src/crypto/twofish.cpp
  5. 4
      src/lib/SecString.cpp
  6. 47
      src/lib/tools.cpp
  7. 2
      src/main.cpp
  8. 121
      src/mainwindow.cpp
  9. 10
      src/mainwindow.h

@ -246,7 +246,7 @@ public:
\param identifier Normally this is the filename of the database but it can also be an IP address or something else if the database is not file based. \param identifier Normally this is the filename of the database but it can also be an IP address or something else if the database is not file based.
\return TRUE if loading was successfull, otherwise FALSE. \return TRUE if loading was successfull, otherwise FALSE.
*/ */
virtual bool load(QString identifier)=0; virtual bool load(QString identifier, bool readOnly)=0;
//! Saves the current database. //! Saves the current database.
/*! It is not allowed to call this function if no database is loaded. /*! It is not allowed to call this function if no database is loaded.

@ -493,8 +493,8 @@ void Kdb3Database::restoreGroupTreeState(){
} }
} }
bool Kdb3Database::load(QString identifier){ bool Kdb3Database::load(QString identifier, bool readOnly){
return loadReal(identifier, false); return loadReal(identifier, readOnly, false);
} }
#define LOAD_RETURN_CLEANUP \ #define LOAD_RETURN_CLEANUP \
@ -503,7 +503,7 @@ bool Kdb3Database::load(QString identifier){
delete[] buffer; \ delete[] buffer; \
return false; return false;
bool Kdb3Database::loadReal(QString filename, bool differentEncoding) { bool Kdb3Database::loadReal(QString filename, bool readOnly, bool differentEncoding) {
unsigned long total_size,crypto_size; unsigned long total_size,crypto_size;
quint32 Signature1,Signature2,Version,NumGroups,NumEntries,Flags; quint32 Signature1,Signature2,Version,NumGroups,NumEntries,Flags;
quint8 FinalRandomSeed[16]; quint8 FinalRandomSeed[16];
@ -511,7 +511,7 @@ bool Kdb3Database::loadReal(QString filename, bool differentEncoding) {
quint8 EncryptionIV[16]; quint8 EncryptionIV[16];
File = new QFile(filename); File = new QFile(filename);
if(!File->open(QIODevice::ReadWrite)){ if (readOnly) {
if(!File->open(QIODevice::ReadOnly)){ if(!File->open(QIODevice::ReadOnly)){
error=tr("Could not open file."); error=tr("Could not open file.");
delete File; delete File;
@ -519,6 +519,20 @@ bool Kdb3Database::loadReal(QString filename, bool differentEncoding) {
return false; return false;
} }
} }
else {
if(!File->open(QIODevice::ReadWrite)){
if(!File->open(QIODevice::ReadOnly)){
error=tr("Could not open file.");
delete File;
File = NULL;
return false;
}
else{
readOnly = true;
}
}
}
total_size=File->size(); total_size=File->size();
char* buffer = new char[total_size]; char* buffer = new char[total_size];
File->read(buffer,total_size); File->read(buffer,total_size);
@ -604,7 +618,7 @@ bool Kdb3Database::loadReal(QString filename, bool differentEncoding) {
RawMasterKey.copyData(RawMasterKey_Latin1); RawMasterKey.copyData(RawMasterKey_Latin1);
PotentialEncodingIssueLatin1 = false; PotentialEncodingIssueLatin1 = false;
qDebug("Decryption failed. Retrying with Latin-1."); qDebug("Decryption failed. Retrying with Latin-1.");
return loadReal(filename, true); // second try return loadReal(filename, readOnly, true); // second try
} }
if(PotentialEncodingIssueUTF8){ if(PotentialEncodingIssueUTF8){
delete[] buffer; delete[] buffer;
@ -614,7 +628,7 @@ bool Kdb3Database::loadReal(QString filename, bool differentEncoding) {
RawMasterKey.copyData(RawMasterKey_UTF8); RawMasterKey.copyData(RawMasterKey_UTF8);
PotentialEncodingIssueUTF8 = false; PotentialEncodingIssueUTF8 = false;
qDebug("Decryption failed. Retrying with UTF-8."); qDebug("Decryption failed. Retrying with UTF-8.");
return loadReal(filename, true); // second/third try return loadReal(filename, readOnly, true); // second/third try
} }
error=tr("Hash test failed.\nThe key is wrong or the file is damaged."); error=tr("Hash test failed.\nThe key is wrong or the file is damaged.");
KeyError=true; KeyError=true;
@ -1318,13 +1332,8 @@ bool Kdb3Database::save(){
quint8 EncryptionIV[16]; quint8 EncryptionIV[16];
if(!(File->openMode() & QIODevice::WriteOnly)){ if(!(File->openMode() & QIODevice::WriteOnly)){
File->close(); error = tr("The database has been opened read-only.");
} return false;
if(!File->isOpen()){
if(!File->open(QIODevice::ReadWrite)){
error = tr("Could not open file for writing.");
return false;
}
} }
unsigned int FileSize; unsigned int FileSize;

@ -135,7 +135,7 @@ public:
Kdb3Database(); Kdb3Database();
virtual ~Kdb3Database(){}; virtual ~Kdb3Database(){};
virtual bool load(QString identifier); virtual bool load(QString identifier, bool readOnly);
virtual bool save(); virtual bool save();
virtual bool close(); virtual bool close();
virtual void create(); virtual void create();
@ -190,7 +190,7 @@ public:
inline bool hasPasswordEncodingChanged() { return passwordEncodingChanged; }; inline bool hasPasswordEncodingChanged() { return passwordEncodingChanged; };
private: private:
bool loadReal(QString filename, bool differentEncoding); bool loadReal(QString filename, bool readOnly, bool differentEncoding);
QDateTime dateFromPackedStruct5(const unsigned char* pBytes); QDateTime dateFromPackedStruct5(const unsigned char* pBytes);
void dateToPackedStruct5(const QDateTime& datetime, unsigned char* dst); void dateToPackedStruct5(const QDateTime& datetime, unsigned char* dst);
bool isMetaStream(StdEntry& Entry); bool isMetaStream(StdEntry& Entry);

@ -477,8 +477,7 @@
void Twofish_fatal(const char* msg){ void Twofish_fatal(const char* msg){
qCritical("Twofish: Fatal Error: %s",msg); qFatal("Twofish: Fatal Error: %s", msg);
exit(1);
} }

@ -92,13 +92,15 @@ void SecString::overwrite(QString& str){
void SecString::generateSessionKey(){ void SecString::generateSessionKey(){
sessionkey = new quint8[32]; sessionkey = new quint8[32];
lockPage(sessionkey, 32); if (!lockPage(sessionkey, 32))
qDebug("Failed to lock session key page");
randomize(sessionkey, 32); randomize(sessionkey, 32);
RC4.setKey(sessionkey, 32); RC4.setKey(sessionkey, 32);
} }
void SecString::deleteSessionKey() { void SecString::deleteSessionKey() {
overwrite(sessionkey, 32); overwrite(sessionkey, 32);
unlockPage(sessionkey, 32);
delete[] sessionkey; delete[] sessionkey;
} }

@ -56,21 +56,21 @@ void createBanner(QPixmap* Pixmap,const QPixmap* IconAlpha,const QString& Text,i
QString decodeFileError(QFile::FileError Code){ QString decodeFileError(QFile::FileError Code){
switch(Code){ switch(Code){
case QFile::NoError: return QApplication::translate("FileErrors","No error occurred."); case QFile::NoError: return QApplication::translate("FileErrors","No error occurred.");
case QFile::ReadError: return QApplication::translate("FileErrors","An error occurred while reading from the file."); case QFile::ReadError: return QApplication::translate("FileErrors","An error occurred while reading from the file.");
case QFile::WriteError: return QApplication::translate("FileErrors","An error occurred while writing to the file."); case QFile::WriteError: return QApplication::translate("FileErrors","An error occurred while writing to the file.");
case QFile::FatalError: return QApplication::translate("FileErrors","A fatal error occurred."); case QFile::FatalError: return QApplication::translate("FileErrors","A fatal error occurred.");
case QFile::ResourceError: return QApplication::translate("FileErrors","An resource error occurred."); case QFile::ResourceError: return QApplication::translate("FileErrors","An resource error occurred.");
case QFile::OpenError: return QApplication::translate("FileErrors","The file could not be opened."); case QFile::OpenError: return QApplication::translate("FileErrors","The file could not be opened.");
case QFile::AbortError: return QApplication::translate("FileErrors","The operation was aborted."); case QFile::AbortError: return QApplication::translate("FileErrors","The operation was aborted.");
case QFile::TimeOutError: return QApplication::translate("FileErrors","A timeout occurred."); case QFile::TimeOutError: return QApplication::translate("FileErrors","A timeout occurred.");
case QFile::UnspecifiedError: return QApplication::translate("FileErrors","An unspecified error occurred."); case QFile::UnspecifiedError: return QApplication::translate("FileErrors","An unspecified error occurred.");
case QFile::RemoveError: return QApplication::translate("FileErrors","The file could not be removed."); case QFile::RemoveError: return QApplication::translate("FileErrors","The file could not be removed.");
case QFile::RenameError: return QApplication::translate("FileErrors","The file could not be renamed."); case QFile::RenameError: return QApplication::translate("FileErrors","The file could not be renamed.");
case QFile::PositionError: return QApplication::translate("FileErrors","The position in the file could not be changed."); case QFile::PositionError: return QApplication::translate("FileErrors","The position in the file could not be changed.");
case QFile::ResizeError: return QApplication::translate("FileErrors","The file could not be resized."); case QFile::ResizeError: return QApplication::translate("FileErrors","The file could not be resized.");
case QFile::PermissionsError: return QApplication::translate("FileErrors","The file could not be accessed."); case QFile::PermissionsError: return QApplication::translate("FileErrors","The file could not be accessed.");
case QFile::CopyError: return QApplication::translate("FileErrors","The file could not be copied."); case QFile::CopyError: return QApplication::translate("FileErrors","The file could not be copied.");
} }
return QString(); return QString();
} }
@ -134,21 +134,20 @@ QString makePathRelative(const QString& AbsDir,const QString& CurDir){
} }
void showErrMsg(const QString& msg,QWidget* parent){ void showErrMsg(const QString& msg,QWidget* parent){
QMessageBox::critical(parent,QApplication::translate("Main","Error"),msg,QApplication::translate("Main","OK")); QMessageBox::critical(parent, QApplication::translate("Main","Error"), msg);
} }
QString getImageFile(const QString& name){ QString getImageFile(const QString& name){
if (QFile::exists(DataDir+"/icons/"+name)) if (QFile::exists(DataDir+"/icons/"+name))
return DataDir+"/icons/"+name; return DataDir+"/icons/"+name;
else{ else{
QMessageBox::critical(0,QApplication::translate("Main","Error"), QString errMsg = QApplication::translate("Main","File '%1' could not be found.").arg(name);
QApplication::translate("Main","File '%1' could not be found.") showErrMsg(errMsg);
.arg(name),QApplication::translate("Main","OK"),0,0,2,1); qFatal("File '%s' could not be found.", CSTR(errMsg));
exit(1); return QString();
} }
} }
const QIcon& getIcon(const QString& name){ const QIcon& getIcon(const QString& name){
static QHash<QString,QIcon*>IconCache; static QHash<QString,QIcon*>IconCache;
QIcon* CachedIcon=IconCache.value(name); QIcon* CachedIcon=IconCache.value(name);
@ -284,7 +283,7 @@ void installTranslator(){
} }
if (loadTranslation(translator,"keepassx-",language,QStringList() if (loadTranslation(translator,"keepassx-",language,QStringList()
<< DataDir+"/i18n/" << HomeDir)) << HomeDir << DataDir+"/i18n/"))
{ {
if (!translatorActive){ if (!translatorActive){
QApplication::installTranslator(translator); QApplication::installTranslator(translator);
@ -297,8 +296,8 @@ void installTranslator(){
} }
if (loadTranslation(qtTranslator,"qt_",language,QStringList() if (loadTranslation(qtTranslator,"qt_",language,QStringList()
<< QLibraryInfo::location(QLibraryInfo::TranslationsPath) << HomeDir << DataDir+"/i18n/"
<< DataDir+"/i18n/" << HomeDir)) << QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
{ {
if (!qtTranslatorActive){ if (!qtTranslatorActive){
QApplication::installTranslator(qtTranslator); QApplication::installTranslator(qtTranslator);

@ -48,6 +48,8 @@ IIconTheme* IconLoader=NULL;
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
QT_REQUIRE_VERSION(argc, argv, "4.3.0");
#if defined(Q_WS_X11) && defined(GLOBAL_AUTOTYPE) #if defined(Q_WS_X11) && defined(GLOBAL_AUTOTYPE)
QApplication* app = new KeepassApplication(argc,argv); QApplication* app = new KeepassApplication(argc,argv);
#else #else

@ -78,12 +78,12 @@ KeepassMainWindow::KeepassMainWindow(const QString& ArgFile,bool ArgMin,bool Arg
setStateFileOpen(false); setStateFileOpen(false);
setupMenus(); setupMenus();
DetailView->setVisible(config->showEntryDetails()); DetailView->setVisible(config->showEntryDetails());
statusbarState = 0; StatusBarGeneral=new QLabel(statusBar());
StatusBarGeneral=new QLabel(tr("Ready"),statusBar());
//StatusBarSelection=new QLabel(statusBar()); //StatusBarSelection=new QLabel(statusBar());
statusBar()->addWidget(StatusBarGeneral,15); statusBar()->addWidget(StatusBarGeneral,15);
//statusBar()->addWidget(StatusBarSelection,85); //statusBar()->addWidget(StatusBarSelection,85);
statusBar()->setVisible(config->showStatusbar()); statusBar()->setVisible(config->showStatusbar());
setStatusBarMsg(StatusBarReady);
NormalCentralWidget=QMainWindow::centralWidget(); NormalCentralWidget=QMainWindow::centralWidget();
LockedCentralWidget=new QWidget(this); LockedCentralWidget=new QWidget(this);
@ -398,19 +398,27 @@ bool KeepassMainWindow::openDatabase(QString filename,bool IsAuto){
return false; return false;
} }
dbReadOnly = false;
if (QFile::exists(filename+".lock")){ if (QFile::exists(filename+".lock")){
QMessageBox::StandardButton buttonPressed = QMessageBox::question( QMessageBox msgBox(this);
this, msgBox.setIcon(QMessageBox::Question);
tr("Database locked"), msgBox.setWindowTitle(tr("Database locked"));
tr("The database you are trying to open is locked.\n" msgBox.setText(tr("The database you are trying to open is locked.\n"
"This means that either someone else has opened the file or KeePassX crashed last time it opened the database.\n\n" "This means that either someone else has opened the file or KeePassX crashed last time it opened the database.\n\n"
"Do you want to open it anyway?" "Do you want to open it anyway?"
), ));
QMessageBox::Yes|QMessageBox::No, msgBox.addButton(QMessageBox::Yes);
QMessageBox::No msgBox.addButton(QMessageBox::No);
); QPushButton* readOnlyButton = new QPushButton(tr("Open read-only"), &msgBox);
if (buttonPressed != QMessageBox::Yes) msgBox.addButton(readOnlyButton, QMessageBox::AcceptRole);
msgBox.setDefaultButton(readOnlyButton);
msgBox.exec();
if (!msgBox.clickedButton() || msgBox.clickedButton() == msgBox.button(QMessageBox::No))
return false; return false;
else if (msgBox.clickedButton() == readOnlyButton)
dbReadOnly = true;
} }
if(!IsAuto){ if(!IsAuto){
@ -439,18 +447,18 @@ bool KeepassMainWindow::openDatabase(QString filename,bool IsAuto){
EntryView->db=db; EntryView->db=db;
setupDatabaseConnections(db); setupDatabaseConnections(db);
QString err; QString err;
statusbarState = 1; setStatusBarMsg(StatusBarLoading);
StatusBarGeneral->setText(tr("Loading Database..."));
db->setKey(dlg.password(),dlg.keyFile()); db->setKey(dlg.password(),dlg.keyFile());
if(db->load(filename)){
if (!QFile::exists(filename+".lock")){ if (!dbReadOnly && !QFile::exists(filename+".lock")){
QFile lock(filename+".lock"); QFile lock(filename+".lock");
if (!lock.open(QIODevice::WriteOnly)){ if (!lock.open(QIODevice::WriteOnly)){
QMessageBox::critical(this, tr("Error"), tr("Couldn't create database lock file.")); setStatusBarMsg(StatusBarReadOnlyLock);
return false; dbReadOnly = true;
}
} }
}
if(db->load(filename, dbReadOnly)){
if (IsLocked) if (IsLocked)
resetLock(); resetLock();
currentFile = filename; currentFile = filename;
@ -462,8 +470,7 @@ bool KeepassMainWindow::openDatabase(QString filename,bool IsAuto){
setStateFileModified(static_cast<Kdb3Database*>(db)->hasPasswordEncodingChanged()); setStateFileModified(static_cast<Kdb3Database*>(db)->hasPasswordEncodingChanged());
} }
else{ else{
statusbarState = 2; setStatusBarMsg(StatusBarLoadingFailed);
StatusBarGeneral->setText(tr("Loading Failed"));
QString error=db->getError(); QString error=db->getError();
if(error.isEmpty())error=tr("Unknown error while loading database."); if(error.isEmpty())error=tr("Unknown error while loading database.");
QMessageBox::critical(this,tr("Error"), QMessageBox::critical(this,tr("Error"),
@ -478,8 +485,8 @@ bool KeepassMainWindow::openDatabase(QString filename,bool IsAuto){
return false; return false;
} }
} }
statusbarState = 0; if (statusbarState != StatusBarReadOnlyLock)
StatusBarGeneral->setText(tr("Ready")); setStatusBarMsg(StatusBarReady);
inactivityCounter = 0; inactivityCounter = 0;
return true; return true;
@ -509,7 +516,12 @@ bool KeepassMainWindow::closeDatabase(bool lock){
QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel, QMessageBox::Yes); QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel, QMessageBox::Yes);
if(r==QMessageBox::Cancel) return false; //Cancel if(r==QMessageBox::Cancel) return false; //Cancel
if(r==QMessageBox::Yes){ //Yes (Save file) if(r==QMessageBox::Yes){ //Yes (Save file)
if(!OnFileSave()) return false; if (dbReadOnly) {
if(!OnFileSaveAs()) return false;
}
else {
if(!OnFileSave()) return false;
}
} }
} }
} }
@ -1028,6 +1040,14 @@ void KeepassMainWindow::closeEvent(QCloseEvent* e){
return; return;
} }
if(FileOpen && !closeDatabase()){
ShutingDown=false;
e->ignore();
return;
}
e->accept();
#ifdef GLOBAL_AUTOTYPE #ifdef GLOBAL_AUTOTYPE
autoType->unregisterGlobalShortcut(); autoType->unregisterGlobalShortcut();
#endif #endif
@ -1039,20 +1059,9 @@ void KeepassMainWindow::closeEvent(QCloseEvent* e){
if (config->showEntryDetails()) if (config->showEntryDetails())
config->setHSplitterPos(HSplitter->saveState()); config->setHSplitterPos(HSplitter->saveState());
config->setShowStatusbar(statusBar()->isVisible()); config->setShowStatusbar(statusBar()->isVisible());
if(FileOpen){
if(!closeDatabase()){
ShutingDown=false;
e->ignore();
return;
}
else
e->accept();
}
else
e->accept();
delete SysTray; delete SysTray;
QCoreApplication::quit(); QApplication::quit();
} }
void KeepassMainWindow::hideEvent(QHideEvent* event){ void KeepassMainWindow::hideEvent(QHideEvent* event){
@ -1101,17 +1110,7 @@ void KeepassMainWindow::OnExtrasSettings(){
else { else {
setWindowTitle(APP_DISPLAY_NAME); setWindowTitle(APP_DISPLAY_NAME);
} }
switch (statusbarState) { setStatusBarMsg(statusbarState);
case 0:
StatusBarGeneral->setText(tr("Ready"));
break;
case 1:
StatusBarGeneral->setText(tr("Loading Database..."));
break;
case 2:
StatusBarGeneral->setText(tr("Loading Failed"));
break;
}
} }
EntryView->setAlternatingRowColors(config->alternatingRowColors()); EntryView->setAlternatingRowColors(config->alternatingRowColors());
@ -1409,3 +1408,25 @@ void KeepassMainWindow::createBookmarkActions(){
menuBookmarks->addAction(action); menuBookmarks->addAction(action);
} }
} }
void KeepassMainWindow::setStatusBarMsg(StatusBarMsg statusBarMsg) {
QString text;
switch (statusBarMsg) {
case StatusBarReady:
text = tr("Ready");
break;
case StatusBarLoading:
text = tr("Loading Database...");
break;
case StatusBarLoadingFailed:
text = tr("Loading Failed");
break;
case StatusBarReadOnlyLock:
text = tr("Couldn't create lock file. Opening the database read-only.");
break;
}
statusbarState = statusBarMsg;
StatusBarGeneral->setText(text);
}

@ -90,6 +90,11 @@ class KeepassMainWindow : public QMainWindow, private Ui_MainWindow{
void showEvent(QShowEvent* event); void showEvent(QShowEvent* event);
void setLock(); void setLock();
void resetLock(); void resetLock();
enum StatusBarMsg {
StatusBarReady, StatusBarLoading, StatusBarLoadingFailed,
StatusBarReadOnlyLock
};
void setStatusBarMsg(StatusBarMsg statusBarMsg);
SelectionState GroupSelection, EntrySelection; SelectionState GroupSelection, EntrySelection;
bool FileOpen; bool FileOpen;
bool ModFlag; bool ModFlag;
@ -115,7 +120,7 @@ class KeepassMainWindow : public QMainWindow, private Ui_MainWindow{
void createBookmarkActions(); void createBookmarkActions();
QLineEdit* QuickSearchEdit; QLineEdit* QuickSearchEdit;
QLabel* StatusBarGeneral; QLabel* StatusBarGeneral;
QLabel* StatusBarSelection; //QLabel* StatusBarSelection;
QToolBar* toolBar; QToolBar* toolBar;
QSystemTrayIcon* SysTray; QSystemTrayIcon* SysTray;
QAction* ViewShowToolbarAction; QAction* ViewShowToolbarAction;
@ -131,7 +136,8 @@ class KeepassMainWindow : public QMainWindow, private Ui_MainWindow{
QString currentFile; QString currentFile;
int inactivityCounter; int inactivityCounter;
QTimer* inactivityTimer; QTimer* inactivityTimer;
int statusbarState; StatusBarMsg statusbarState;
bool dbReadOnly;
}; };
#endif #endif