mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-12-25 15:29:44 -05:00
Import QSaveFile from libkdeqt5staging.
It's planned to become part of Qt 5.1.
This commit is contained in:
parent
d826ae5daf
commit
2f98d95b66
@ -28,6 +28,7 @@ set(keepassx_SOURCES
|
|||||||
core/EntryAttributes.cpp
|
core/EntryAttributes.cpp
|
||||||
core/Group.cpp
|
core/Group.cpp
|
||||||
core/Metadata.cpp
|
core/Metadata.cpp
|
||||||
|
core/qsavefile.cpp
|
||||||
core/TimeInfo.cpp
|
core/TimeInfo.cpp
|
||||||
core/Tools.cpp
|
core/Tools.cpp
|
||||||
core/Uuid.cpp
|
core/Uuid.cpp
|
||||||
@ -97,6 +98,7 @@ set(keepassx_MOC
|
|||||||
core/EntryAttributes.h
|
core/EntryAttributes.h
|
||||||
core/Group.h
|
core/Group.h
|
||||||
core/Metadata.h
|
core/Metadata.h
|
||||||
|
core/qsavefile.h
|
||||||
gui/AboutDialog.h
|
gui/AboutDialog.h
|
||||||
gui/Application.h
|
gui/Application.h
|
||||||
gui/ChangeMasterKeyWidget.h
|
gui/ChangeMasterKeyWidget.h
|
||||||
|
431
src/core/qsavefile.cpp
Normal file
431
src/core/qsavefile.cpp
Normal file
@ -0,0 +1,431 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
** Contact: http://www.qt-project.org/
|
||||||
|
**
|
||||||
|
** This file is part of the QtCore module of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:LGPL$
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** This file may be used under the terms of the GNU Lesser General Public
|
||||||
|
** License version 2.1 as published by the Free Software Foundation and
|
||||||
|
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||||
|
** file. Please review the following information to ensure the GNU Lesser
|
||||||
|
** General Public License version 2.1 requirements will be met:
|
||||||
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** In addition, as a special exception, Nokia gives you certain additional
|
||||||
|
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||||
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU General
|
||||||
|
** Public License version 3.0 as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||||
|
** file. Please review the following information to ensure the GNU General
|
||||||
|
** Public License version 3.0 requirements will be met:
|
||||||
|
** http://www.gnu.org/copyleft/gpl.html.
|
||||||
|
**
|
||||||
|
** Other Usage
|
||||||
|
** Alternatively, this file may be used in accordance with the terms and
|
||||||
|
** conditions contained in a signed written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
**
|
||||||
|
**
|
||||||
|
**
|
||||||
|
**
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "qsavefile.h"
|
||||||
|
#include "qsavefile_p.h"
|
||||||
|
|
||||||
|
#include <QtCore/QAbstractFileEngine>
|
||||||
|
#include <QtCore/QFileInfo>
|
||||||
|
#include <QtCore/QTemporaryFile>
|
||||||
|
|
||||||
|
QSaveFilePrivate::QSaveFilePrivate()
|
||||||
|
: tempFile(0), error(QFile::NoError)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QSaveFilePrivate::~QSaveFilePrivate()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\class QSaveFile
|
||||||
|
\brief The QSaveFile class provides an interface for safely writing to files.
|
||||||
|
|
||||||
|
\ingroup io
|
||||||
|
|
||||||
|
\reentrant
|
||||||
|
|
||||||
|
QSaveFile is an I/O device for writing text and binary files, without losing
|
||||||
|
existing data if the writing operation fails.
|
||||||
|
|
||||||
|
While writing, the contents will be written to a temporary file, and if
|
||||||
|
no error happened, commit() will move it to the final file. This ensures that
|
||||||
|
no data at the final file is lost in case an error happens while writing,
|
||||||
|
and no partially-written file is ever present at the final location. Always
|
||||||
|
use QSaveFile when saving entire documents to disk.
|
||||||
|
|
||||||
|
QSaveFile automatically detects errors while writing, such as the full partition
|
||||||
|
situation, where write() cannot write all the bytes. It will remember that
|
||||||
|
an error happened, and will discard the temporary file in commit().
|
||||||
|
|
||||||
|
Much like with QFile, the file is opened with open(). Data is usually read
|
||||||
|
and written using QDataStream or QTextStream, but you can also call the
|
||||||
|
QIODevice-inherited functions read(), readLine(), readAll(), write().
|
||||||
|
|
||||||
|
Unlike QFile, calling close() is not allowed. commit() replaces it. If commit()
|
||||||
|
was not called and the QSaveFile instance is destroyed, the temporary file is
|
||||||
|
discarded.
|
||||||
|
|
||||||
|
\sa QTextStream, QDataStream, QFileInfo, QDir, QFile, QTemporaryFile
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\internal
|
||||||
|
*/
|
||||||
|
QSaveFile::QSaveFile()
|
||||||
|
: QIODevice(), d_ptr(new QSaveFilePrivate)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
/*!
|
||||||
|
Constructs a new file object with the given \a parent.
|
||||||
|
*/
|
||||||
|
QSaveFile::QSaveFile(QObject *parent)
|
||||||
|
: QIODevice(parent), d_ptr(new QSaveFilePrivate)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
/*!
|
||||||
|
Constructs a new file object to represent the file with the given \a name.
|
||||||
|
*/
|
||||||
|
QSaveFile::QSaveFile(const QString &name)
|
||||||
|
: QIODevice(0), d_ptr(new QSaveFilePrivate)
|
||||||
|
{
|
||||||
|
Q_D(QSaveFile);
|
||||||
|
d->fileName = name;
|
||||||
|
}
|
||||||
|
/*!
|
||||||
|
Constructs a new file object with the given \a parent to represent the
|
||||||
|
file with the specified \a name.
|
||||||
|
*/
|
||||||
|
QSaveFile::QSaveFile(const QString &name, QObject *parent)
|
||||||
|
: QIODevice(parent), d_ptr(new QSaveFilePrivate)
|
||||||
|
{
|
||||||
|
Q_D(QSaveFile);
|
||||||
|
d->fileName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Destroys the file object, discarding the saved contents unless commit() was called.
|
||||||
|
*/
|
||||||
|
QSaveFile::~QSaveFile()
|
||||||
|
{
|
||||||
|
Q_D(QSaveFile);
|
||||||
|
if (d->tempFile) {
|
||||||
|
d->tempFile->setAutoRemove(true);
|
||||||
|
delete d->tempFile;
|
||||||
|
}
|
||||||
|
QIODevice::close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns false since temporary files support random access.
|
||||||
|
|
||||||
|
\sa QIODevice::isSequential()
|
||||||
|
*/
|
||||||
|
bool QSaveFile::isSequential() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the file error status.
|
||||||
|
|
||||||
|
The I/O device status returns an error code. For example, if open()
|
||||||
|
returns false, or a read/write operation returns -1, this function can
|
||||||
|
be called to find out the reason why the operation failed.
|
||||||
|
|
||||||
|
Unlike QFile which clears the error on the next operation, QSaveFile remembers
|
||||||
|
the error until the file is closed, in order to discard the file contents in close().
|
||||||
|
|
||||||
|
\sa unsetError()
|
||||||
|
*/
|
||||||
|
|
||||||
|
QFile::FileError QSaveFile::error() const
|
||||||
|
{
|
||||||
|
return d_func()->error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets the file's error to QFile::NoError.
|
||||||
|
|
||||||
|
This will make QSaveFile forget that an error happened during saving, so you
|
||||||
|
probably don't want to call this, unless you're really sure that you want to
|
||||||
|
save the file anyway.
|
||||||
|
|
||||||
|
\sa error()
|
||||||
|
*/
|
||||||
|
void QSaveFile::unsetError()
|
||||||
|
{
|
||||||
|
d_func()->error = QFile::NoError;
|
||||||
|
setErrorString(QString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the name set by setFileName() or to the QSaveFile
|
||||||
|
constructor.
|
||||||
|
|
||||||
|
\sa setFileName()
|
||||||
|
*/
|
||||||
|
QString QSaveFile::fileName() const
|
||||||
|
{
|
||||||
|
return d_func()->fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets the \a name of the file. The name can have no path, a
|
||||||
|
relative path, or an absolute path.
|
||||||
|
|
||||||
|
\sa QFile::setFileName(), fileName()
|
||||||
|
*/
|
||||||
|
void QSaveFile::setFileName(const QString &name)
|
||||||
|
{
|
||||||
|
d_func()->fileName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Opens the file using OpenMode \a mode, returning true if successful;
|
||||||
|
otherwise false.
|
||||||
|
|
||||||
|
Important: the \a mode must be QIODevice::WriteOnly.
|
||||||
|
It may also have additional flags, such as QIODevice::Text and QIODevice::Unbuffered.
|
||||||
|
|
||||||
|
QIODevice::ReadWrite and QIODevice::Append are not supported at the moment.
|
||||||
|
|
||||||
|
\sa QIODevice::OpenMode, setFileName()
|
||||||
|
*/
|
||||||
|
bool QSaveFile::open(OpenMode mode)
|
||||||
|
{
|
||||||
|
Q_D(QSaveFile);
|
||||||
|
if (isOpen()) {
|
||||||
|
qWarning("QSaveFile::open: File (%s) already open", qPrintable(fileName()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
unsetError();
|
||||||
|
if ((mode & (ReadOnly | WriteOnly)) == 0) {
|
||||||
|
qWarning("QSaveFile::open: Open mode not specified");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// In the future we could implement Append and ReadWrite by copying from the existing file to the temp file...
|
||||||
|
if ((mode & ReadOnly) || (mode & Append)) {
|
||||||
|
qWarning("QSaveFile::open: Unsupported open mode %d", int(mode));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if existing file is writable
|
||||||
|
QFileInfo existingFile(d->fileName);
|
||||||
|
if (existingFile.exists() && !existingFile.isWritable()) {
|
||||||
|
d->error = QFile::WriteError;
|
||||||
|
setErrorString(QSaveFile::tr("Existing file %1 is not writable").arg(d->fileName));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
d->tempFile = new QTemporaryFile;
|
||||||
|
d->tempFile->setAutoRemove(false);
|
||||||
|
d->tempFile->setFileTemplate(d->fileName);
|
||||||
|
if (!d->tempFile->open()) {
|
||||||
|
d->error = d->tempFile->error();
|
||||||
|
setErrorString(d->tempFile->errorString());
|
||||||
|
delete d->tempFile;
|
||||||
|
d->tempFile = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QIODevice::open(mode);
|
||||||
|
if (existingFile.exists())
|
||||||
|
d->tempFile->setPermissions(existingFile.permissions());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\reimp
|
||||||
|
Cannot be called.
|
||||||
|
Call commit() instead.
|
||||||
|
*/
|
||||||
|
void QSaveFile::close()
|
||||||
|
{
|
||||||
|
qFatal("QSaveFile::close called");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Commits the changes to disk, if all previous writes were successful.
|
||||||
|
|
||||||
|
It is mandatory to call this at the end of the saving operation, otherwise the file will be
|
||||||
|
discarded.
|
||||||
|
|
||||||
|
If an error happened during writing, deletes the temporary file and returns false.
|
||||||
|
Otherwise, renames it to the final fileName and returns true on success.
|
||||||
|
Finally, closes the device.
|
||||||
|
|
||||||
|
\sa cancelWriting()
|
||||||
|
*/
|
||||||
|
bool QSaveFile::commit()
|
||||||
|
{
|
||||||
|
Q_D(QSaveFile);
|
||||||
|
if (!d->tempFile)
|
||||||
|
return false;
|
||||||
|
Q_ASSERT(isOpen());
|
||||||
|
QIODevice::close(); // flush and close
|
||||||
|
if (d->error != QFile::NoError) {
|
||||||
|
d->tempFile->remove();
|
||||||
|
unsetError();
|
||||||
|
delete d->tempFile;
|
||||||
|
d->tempFile = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// atomically replace old file with new file
|
||||||
|
// Can't use QFile::rename for that, must use the file engine directly
|
||||||
|
// But that's not available in Qt5, nor is QTemporaryFileEngine
|
||||||
|
// (used by the Qt5-QFileDevice-based QSaveFile).
|
||||||
|
// So we have to do it by hand (non portable) for now.
|
||||||
|
d->tempFile->close();
|
||||||
|
#if 1
|
||||||
|
QAbstractFileEngine* fileEngine = d->tempFile->fileEngine();
|
||||||
|
Q_ASSERT(fileEngine);
|
||||||
|
if (!fileEngine->rename(d->fileName)) {
|
||||||
|
d->error = fileEngine->error();
|
||||||
|
setErrorString(fileEngine->errorString());
|
||||||
|
#else
|
||||||
|
if (::rename(QFile::encodeName(d->tempFile->fileName()).constData(), QFile::encodeName(d->fileName).constData()) != 0) {
|
||||||
|
d->error = QFile::RenameError;
|
||||||
|
setErrorString(QString::fromLocal8Bit(strerror(errno)));
|
||||||
|
#endif
|
||||||
|
d->tempFile->remove();
|
||||||
|
delete d->tempFile;
|
||||||
|
d->tempFile = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
delete d->tempFile;
|
||||||
|
d->tempFile = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets an error code so that commit() discards the temporary file.
|
||||||
|
|
||||||
|
Further write operations are possible after calling this method, but none
|
||||||
|
of it will have any effect, the written file will be discarded.
|
||||||
|
|
||||||
|
\sa commit()
|
||||||
|
*/
|
||||||
|
void QSaveFile::cancelWriting()
|
||||||
|
{
|
||||||
|
if (!isOpen())
|
||||||
|
return;
|
||||||
|
d_func()->error = QFile::WriteError;
|
||||||
|
setErrorString(QSaveFile::tr("Writing canceled by application"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the size of the file.
|
||||||
|
\sa QFile::size()
|
||||||
|
*/
|
||||||
|
qint64 QSaveFile::size() const
|
||||||
|
{
|
||||||
|
Q_D(const QSaveFile);
|
||||||
|
return d->tempFile ? d->tempFile->size() : qint64(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\reimp
|
||||||
|
*/
|
||||||
|
qint64 QSaveFile::pos() const
|
||||||
|
{
|
||||||
|
Q_D(const QSaveFile);
|
||||||
|
return d->tempFile ? d->tempFile->pos() : qint64(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\reimp
|
||||||
|
*/
|
||||||
|
bool QSaveFile::seek(qint64 offset)
|
||||||
|
{
|
||||||
|
Q_D(QSaveFile);
|
||||||
|
return d->tempFile ? d->tempFile->seek(offset) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\reimp
|
||||||
|
*/
|
||||||
|
bool QSaveFile::atEnd() const
|
||||||
|
{
|
||||||
|
Q_D(const QSaveFile);
|
||||||
|
return d->tempFile ? d->tempFile->atEnd() : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Flushes any buffered data to the file. Returns true if successful;
|
||||||
|
otherwise returns false.
|
||||||
|
*/
|
||||||
|
bool QSaveFile::flush()
|
||||||
|
{
|
||||||
|
Q_D(QSaveFile);
|
||||||
|
if (d->tempFile) {
|
||||||
|
if (!d->tempFile->flush()) {
|
||||||
|
d->error = d->tempFile->error();
|
||||||
|
setErrorString(d->tempFile->errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the file handle of the temporary file.
|
||||||
|
|
||||||
|
\sa QFile::handle()
|
||||||
|
*/
|
||||||
|
int QSaveFile::handle() const
|
||||||
|
{
|
||||||
|
Q_D(const QSaveFile);
|
||||||
|
return d->tempFile ? d->tempFile->handle() : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\reimp
|
||||||
|
*/
|
||||||
|
qint64 QSaveFile::readData(char *data, qint64 maxlen)
|
||||||
|
{
|
||||||
|
Q_D(QSaveFile);
|
||||||
|
return d->tempFile ? d->tempFile->read(data, maxlen) : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\reimp
|
||||||
|
*/
|
||||||
|
qint64 QSaveFile::writeData(const char *data, qint64 len)
|
||||||
|
{
|
||||||
|
Q_D(QSaveFile);
|
||||||
|
if (!d->tempFile)
|
||||||
|
return -1;
|
||||||
|
const qint64 written = d->tempFile->write(data, len);
|
||||||
|
if (written != len) {
|
||||||
|
d->error = QFile::WriteError;
|
||||||
|
setErrorString(QSaveFile::tr("Partial write. Partition full?"));
|
||||||
|
}
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\reimp
|
||||||
|
*/
|
||||||
|
qint64 QSaveFile::readLineData(char *data, qint64 maxlen)
|
||||||
|
{
|
||||||
|
Q_D(QSaveFile);
|
||||||
|
return d->tempFile ? d->tempFile->readLine(data, maxlen) : -1;
|
||||||
|
}
|
105
src/core/qsavefile.h
Normal file
105
src/core/qsavefile.h
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
** Contact: http://www.qt-project.org/
|
||||||
|
**
|
||||||
|
** This file is part of the QtCore module of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:LGPL$
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** This file may be used under the terms of the GNU Lesser General Public
|
||||||
|
** License version 2.1 as published by the Free Software Foundation and
|
||||||
|
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||||
|
** file. Please review the following information to ensure the GNU Lesser
|
||||||
|
** General Public License version 2.1 requirements will be met:
|
||||||
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** In addition, as a special exception, Nokia gives you certain additional
|
||||||
|
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||||
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU General
|
||||||
|
** Public License version 3.0 as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||||
|
** file. Please review the following information to ensure the GNU General
|
||||||
|
** Public License version 3.0 requirements will be met:
|
||||||
|
** http://www.gnu.org/copyleft/gpl.html.
|
||||||
|
**
|
||||||
|
** Other Usage
|
||||||
|
** Alternatively, this file may be used in accordance with the terms and
|
||||||
|
** conditions contained in a signed written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
**
|
||||||
|
**
|
||||||
|
**
|
||||||
|
**
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef QSAVEFILE_H
|
||||||
|
#define QSAVEFILE_H
|
||||||
|
|
||||||
|
#include <QtCore/QFile>
|
||||||
|
#include <QtCore/QString>
|
||||||
|
|
||||||
|
#ifdef open
|
||||||
|
#error qsavefile.h must be included before any header file that defines open
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class QAbstractFileEngine;
|
||||||
|
class QSaveFilePrivate;
|
||||||
|
|
||||||
|
class QSaveFile : public QIODevice
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DECLARE_PRIVATE(QSaveFile)
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
QSaveFile();
|
||||||
|
explicit QSaveFile(const QString &name);
|
||||||
|
explicit QSaveFile(QObject *parent);
|
||||||
|
QSaveFile(const QString &name, QObject *parent);
|
||||||
|
~QSaveFile();
|
||||||
|
|
||||||
|
QFile::FileError error() const;
|
||||||
|
void unsetError();
|
||||||
|
|
||||||
|
QString fileName() const;
|
||||||
|
void setFileName(const QString &name);
|
||||||
|
|
||||||
|
bool isSequential() const;
|
||||||
|
|
||||||
|
virtual bool open(OpenMode flags);
|
||||||
|
bool commit();
|
||||||
|
|
||||||
|
void cancelWriting();
|
||||||
|
|
||||||
|
qint64 size() const;
|
||||||
|
qint64 pos() const;
|
||||||
|
bool seek(qint64 offset);
|
||||||
|
bool atEnd() const;
|
||||||
|
bool flush();
|
||||||
|
|
||||||
|
bool resize(qint64 sz);
|
||||||
|
|
||||||
|
int handle() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
qint64 readData(char *data, qint64 maxlen);
|
||||||
|
qint64 writeData(const char *data, qint64 len);
|
||||||
|
qint64 readLineData(char *data, qint64 maxlen);
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void close();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_DISABLE_COPY(QSaveFile)
|
||||||
|
|
||||||
|
QSaveFilePrivate* const d_ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QSAVEFILE_H
|
71
src/core/qsavefile_p.h
Normal file
71
src/core/qsavefile_p.h
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
** Contact: http://www.qt-project.org/
|
||||||
|
**
|
||||||
|
** This file is part of the QtCore module of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:LGPL$
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** This file may be used under the terms of the GNU Lesser General Public
|
||||||
|
** License version 2.1 as published by the Free Software Foundation and
|
||||||
|
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||||
|
** file. Please review the following information to ensure the GNU Lesser
|
||||||
|
** General Public License version 2.1 requirements will be met:
|
||||||
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** In addition, as a special exception, Nokia gives you certain additional
|
||||||
|
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||||
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU General
|
||||||
|
** Public License version 3.0 as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||||
|
** file. Please review the following information to ensure the GNU General
|
||||||
|
** Public License version 3.0 requirements will be met:
|
||||||
|
** http://www.gnu.org/copyleft/gpl.html.
|
||||||
|
**
|
||||||
|
** Other Usage
|
||||||
|
** Alternatively, this file may be used in accordance with the terms and
|
||||||
|
** conditions contained in a signed written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
**
|
||||||
|
**
|
||||||
|
**
|
||||||
|
**
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef QSAVEFILE_P_H
|
||||||
|
#define QSAVEFILE_P_H
|
||||||
|
|
||||||
|
//
|
||||||
|
// W A R N I N G
|
||||||
|
// -------------
|
||||||
|
//
|
||||||
|
// This file is not part of the Qt API. It exists purely as an
|
||||||
|
// implementation detail. This header file may change from version to
|
||||||
|
// version without notice, or even be removed.
|
||||||
|
//
|
||||||
|
// We mean it.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <QtCore/QTemporaryFile>
|
||||||
|
|
||||||
|
class QSaveFilePrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QSaveFilePrivate();
|
||||||
|
~QSaveFilePrivate();
|
||||||
|
|
||||||
|
QString fileName;
|
||||||
|
QTemporaryFile *tempFile;
|
||||||
|
|
||||||
|
QFile::FileError error;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QSAVEFILE_P_H
|
||||||
|
|
@ -146,6 +146,9 @@ add_unit_test(NAME testdeletedobjects SOURCES TestDeletedObjects.cpp MOCS TestDe
|
|||||||
add_unit_test(NAME testkeepass1reader SOURCES TestKeePass1Reader.cpp MOCS TestKeePass1Reader.h
|
add_unit_test(NAME testkeepass1reader SOURCES TestKeePass1Reader.cpp MOCS TestKeePass1Reader.h
|
||||||
LIBS ${TEST_LIBRARIES})
|
LIBS ${TEST_LIBRARIES})
|
||||||
|
|
||||||
|
add_unit_test(NAME testqsavefile SOURCES TestQSaveFile.cpp MOCS TestQSaveFile.h
|
||||||
|
LIBS ${TEST_LIBRARIES})
|
||||||
|
|
||||||
if(WITH_GUI_TESTS)
|
if(WITH_GUI_TESTS)
|
||||||
add_subdirectory(gui)
|
add_subdirectory(gui)
|
||||||
endif(WITH_GUI_TESTS)
|
endif(WITH_GUI_TESTS)
|
||||||
|
192
tests/TestQSaveFile.cpp
Normal file
192
tests/TestQSaveFile.cpp
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
||||||
|
* Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "TestQSaveFile.h"
|
||||||
|
|
||||||
|
#include <QtTest/QtTest>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
# include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "tests.h"
|
||||||
|
#include "core/qsavefile.h"
|
||||||
|
|
||||||
|
class DirCleanup
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DirCleanup(const QString& dir, const QString& filePrefix) : m_dir(dir), m_filePrefix(filePrefix) {}
|
||||||
|
~DirCleanup() {
|
||||||
|
QDir dir(m_dir);
|
||||||
|
QStringList files = dir.entryList(QStringList() << (m_filePrefix + "*"), QDir::Files);
|
||||||
|
Q_FOREACH (const QString& file, files) {
|
||||||
|
QFile::remove(m_dir + "/" + file);
|
||||||
|
}
|
||||||
|
|
||||||
|
QDir().rmdir(m_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_dir;
|
||||||
|
QString m_filePrefix;
|
||||||
|
};
|
||||||
|
|
||||||
|
void TestQSaveFile::transactionalWrite()
|
||||||
|
{
|
||||||
|
const QString dir = tmpDir();
|
||||||
|
const QString targetFile = dir + QString::fromLatin1("/outfile");
|
||||||
|
DirCleanup dirCleanup(dir, "outfile");
|
||||||
|
QFile::remove(targetFile);
|
||||||
|
QSaveFile file(targetFile);
|
||||||
|
QVERIFY(file.open(QIODevice::WriteOnly));
|
||||||
|
QVERIFY(file.isOpen());
|
||||||
|
QCOMPARE(file.fileName(), targetFile);
|
||||||
|
QVERIFY(!QFile::exists(targetFile));
|
||||||
|
|
||||||
|
QTextStream ts(&file);
|
||||||
|
ts << "This is test data one.\n";
|
||||||
|
ts.flush();
|
||||||
|
QCOMPARE(file.error(), QFile::NoError);
|
||||||
|
QVERIFY(!QFile::exists(targetFile));
|
||||||
|
|
||||||
|
QVERIFY(file.commit());
|
||||||
|
QVERIFY(QFile::exists(targetFile));
|
||||||
|
QCOMPARE(file.fileName(), targetFile);
|
||||||
|
|
||||||
|
// Check that we can reuse a QSaveFile object
|
||||||
|
// (and test the case of an existing target file)
|
||||||
|
QVERIFY(file.open(QIODevice::WriteOnly));
|
||||||
|
QCOMPARE(file.write("Hello"), 5LL);
|
||||||
|
QVERIFY(file.commit());
|
||||||
|
|
||||||
|
QFile reader(targetFile);
|
||||||
|
QVERIFY(reader.open(QIODevice::ReadOnly));
|
||||||
|
QCOMPARE(QString::fromLatin1(reader.readAll().constData()), QString::fromLatin1("Hello"));
|
||||||
|
reader.close();
|
||||||
|
|
||||||
|
QVERIFY(QFile::remove(targetFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestQSaveFile::autoFlush()
|
||||||
|
{
|
||||||
|
const QString dir = tmpDir();
|
||||||
|
const QString targetFile = dir + QString::fromLatin1("/outfile");
|
||||||
|
DirCleanup dirCleanup(dir, "outfile");
|
||||||
|
QFile::remove(targetFile);
|
||||||
|
QSaveFile file(targetFile);
|
||||||
|
QVERIFY(file.open(QIODevice::WriteOnly));
|
||||||
|
|
||||||
|
QTextStream ts(&file);
|
||||||
|
ts << "Auto-flush.";
|
||||||
|
// no flush
|
||||||
|
QVERIFY(file.commit()); // close will emit aboutToClose, which will flush the stream
|
||||||
|
QFile reader(targetFile);
|
||||||
|
QVERIFY(reader.open(QIODevice::ReadOnly));
|
||||||
|
QCOMPARE(QString::fromLatin1(reader.readAll().constData()), QString::fromLatin1("Auto-flush."));
|
||||||
|
|
||||||
|
QVERIFY(QFile::remove(targetFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestQSaveFile::transactionalWriteNoPermissions()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_UNIX
|
||||||
|
if (::geteuid() == 0)
|
||||||
|
QSKIP("not valid running this test as root", SkipAll);
|
||||||
|
|
||||||
|
// You can write into /dev/zero, but you can't create a /dev/zero.XXXXXX temp file.
|
||||||
|
QSaveFile file("/dev/zero");
|
||||||
|
if (!QDir("/dev").exists())
|
||||||
|
QSKIP("/dev doesn't exist on this system", SkipAll);
|
||||||
|
|
||||||
|
QVERIFY(!file.open(QIODevice::WriteOnly));
|
||||||
|
QCOMPARE(static_cast<int>(file.error()), static_cast<int>(QFile::OpenError));
|
||||||
|
QVERIFY(!file.commit());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestQSaveFile::transactionalWriteCanceled()
|
||||||
|
{
|
||||||
|
const QString dir = tmpDir();
|
||||||
|
const QString targetFile = dir + QString::fromLatin1("/outfile");
|
||||||
|
DirCleanup dirCleanup(dir, "outfile");
|
||||||
|
QFile::remove(targetFile);
|
||||||
|
QSaveFile file(targetFile);
|
||||||
|
QVERIFY(file.open(QIODevice::WriteOnly));
|
||||||
|
|
||||||
|
QTextStream ts(&file);
|
||||||
|
ts << "This writing operation will soon be canceled.\n";
|
||||||
|
ts.flush();
|
||||||
|
QCOMPARE(file.error(), QFile::NoError);
|
||||||
|
QVERIFY(!QFile::exists(targetFile));
|
||||||
|
|
||||||
|
// We change our mind, let's abort writing
|
||||||
|
file.cancelWriting();
|
||||||
|
|
||||||
|
QVERIFY(!file.commit());
|
||||||
|
|
||||||
|
QVERIFY(!QFile::exists(targetFile)); // temp file was discarded
|
||||||
|
QCOMPARE(file.fileName(), targetFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestQSaveFile::transactionalWriteErrorRenaming()
|
||||||
|
{
|
||||||
|
const QString dir = tmpDir();
|
||||||
|
const QString targetFile = dir + QString::fromLatin1("/outfile");
|
||||||
|
DirCleanup dirCleanup(dir, "outfile");
|
||||||
|
QSaveFile file(targetFile);
|
||||||
|
QVERIFY(file.open(QIODevice::WriteOnly));
|
||||||
|
QCOMPARE(file.write("Hello"), qint64(5));
|
||||||
|
QVERIFY(!QFile::exists(targetFile));
|
||||||
|
|
||||||
|
#ifdef Q_OS_UNIX
|
||||||
|
QFile dirAsFile(dir); // yay, I have to use QFile to change a dir's permissions...
|
||||||
|
QVERIFY(dirAsFile.setPermissions(QFile::Permissions(0))); // no permissions
|
||||||
|
#else
|
||||||
|
QVERIFY(file.setPermissions(QFile::ReadOwner));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QVERIFY(!file.commit());
|
||||||
|
QVERIFY(!QFile::exists(targetFile)); // renaming failed
|
||||||
|
QCOMPARE(file.error(), QFile::RenameError);
|
||||||
|
|
||||||
|
// Restore permissions so that the cleanup can happen
|
||||||
|
#ifdef Q_OS_UNIX
|
||||||
|
QVERIFY(dirAsFile.setPermissions(QFile::Permissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner)));
|
||||||
|
#else
|
||||||
|
QVERIFY(file.setPermissions(QFile::ReadOwner | QFile::WriteOwner));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TestQSaveFile::tmpDir()
|
||||||
|
{
|
||||||
|
QTemporaryFile* tmpFile = new QTemporaryFile(QDir::tempPath() + "/qttest_temp.XXXXXX");
|
||||||
|
if (!tmpFile->open()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
QString dirName = tmpFile->fileName();
|
||||||
|
delete tmpFile;
|
||||||
|
if (!QDir().mkdir(dirName)) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return dirName;
|
||||||
|
}
|
||||||
|
|
||||||
|
KEEPASSX_QTEST_CORE_MAIN(TestQSaveFile)
|
39
tests/TestQSaveFile.h
Normal file
39
tests/TestQSaveFile.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
||||||
|
* Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TESTQSAVEFILE_H
|
||||||
|
#define TESTQSAVEFILE_H
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
|
||||||
|
class TestQSaveFile : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void transactionalWrite();
|
||||||
|
void autoFlush();
|
||||||
|
void transactionalWriteNoPermissions();
|
||||||
|
void transactionalWriteCanceled();
|
||||||
|
void transactionalWriteErrorRenaming();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString tmpDir();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TESTQSAVEFILE_H
|
Loading…
Reference in New Issue
Block a user