kde-playground/kdepimlibs/kpimutils/kfileio.cpp

350 lines
11 KiB
C++
Raw Normal View History

/*
Copyright (c) 2005 Tom Albers <tomalbers@kde.nl>
Copyright (c) 1997-1999 Stefan Taferner <taferner@kde.org>
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This library 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 Library General Public
License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#include "kfileio.h"
#include "kpimutils_export.h"
#include <KDebug>
#include <KLocalizedString>
#include <KMessageBox>
#include <KStandardGuiItem>
#include <kde_file.h> //krazy:exclude=camelcase
#include <QDir>
#include <QByteArray>
#include <QWidget>
#include <QFile>
#include <QFileInfo>
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
namespace KPIMUtils {
//-----------------------------------------------------------------------------
static void msgDialog( const QString &msg )
{
KMessageBox::sorry( 0, msg, i18n( "File I/O Error" ) );
}
//-----------------------------------------------------------------------------
QByteArray kFileToByteArray( const QString &aFileName, bool aEnsureNL,
bool aVerbose )
{
QByteArray result;
QFileInfo info( aFileName );
unsigned int readLen;
unsigned int len = info.size();
QFile file( aFileName );
//assert(aFileName!=0);
if ( aFileName.isEmpty() ) {
return "";
}
if ( !info.exists() ) {
if ( aVerbose ) {
msgDialog( i18n( "The specified file does not exist:\n%1", aFileName ) );
}
return QByteArray();
}
if ( info.isDir() ) {
if ( aVerbose ) {
msgDialog( i18n( "This is a folder and not a file:\n%1", aFileName ) );
}
return QByteArray();
}
if ( !info.isReadable() ) {
if ( aVerbose ) {
msgDialog( i18n( "You do not have read permissions to the file:\n%1", aFileName ) );
}
return QByteArray();
}
if ( len == 0 ) {
return QByteArray();
}
if ( !file.open( QIODevice::Unbuffered|QIODevice::ReadOnly ) ) {
if ( aVerbose ) {
switch ( file.error() ) {
case QFile::ReadError:
msgDialog( i18n( "Could not read file:\n%1", aFileName ) );
break;
case QFile::OpenError:
msgDialog( i18n( "Could not open file:\n%1", aFileName ) );
break;
default:
msgDialog( i18n( "Error while reading file:\n%1", aFileName ) );
}
}
return QByteArray();
}
result.resize( len + int( aEnsureNL ) );
readLen = file.read( result.data(), len );
if ( aEnsureNL ) {
if ( result[readLen-1] != '\n' ) {
result[readLen++] = '\n';
len++;
} else {
result.truncate( len );
}
}
if ( readLen < len ) {
QString msg = i18np( "Could only read 1 byte of %2.",
"Could only read %1 bytes of %2.",
readLen, len );
msgDialog( msg );
result.truncate( readLen );
}
return result;
}
//-----------------------------------------------------------------------------
bool kByteArrayToFile( const QByteArray &aBuffer, const QString &aFileName,
bool aAskIfExists, bool aBackup, bool aVerbose )
{
// TODO: use KSaveFile
QFile file( aFileName );
//assert(aFileName!=0);
if ( aFileName.isEmpty() ) {
return false;
}
if ( file.exists() ) {
if ( aAskIfExists ) {
QString str;
str = i18n( "File %1 exists.\nDo you want to replace it?", aFileName );
const int rc =
KMessageBox::warningContinueCancel( 0, str, i18n( "Save to File" ),
KGuiItem( i18n( "&Replace" ) ) );
if ( rc != KMessageBox::Continue ) {
return false;
}
}
if ( aBackup ) {
// make a backup copy
// TODO: use KSaveFile::backupFile()
QString bakName = aFileName;
bakName += QLatin1Char('~');
QFile::remove( bakName );
if ( !QDir::current().rename( aFileName, bakName ) ) {
// failed to rename file
if ( !aVerbose ) {
return false;
}
const int rc =
KMessageBox::warningContinueCancel(
0,
i18n( "Failed to make a backup copy of %1.\nContinue anyway?", aFileName ),
i18n( "Save to File" ), KStandardGuiItem::save() );
if ( rc != KMessageBox::Continue ) {
return false;
}
}
}
}
if ( !file.open( QIODevice::Unbuffered|QIODevice::WriteOnly|QIODevice::Truncate ) ) {
if ( aVerbose ) {
switch ( file.error() ) {
case QFile::WriteError:
msgDialog( i18n( "Could not write to file:\n%1", aFileName ) );
break;
case QFile::OpenError:
msgDialog( i18n( "Could not open file for writing:\n%1", aFileName ) );
break;
default:
msgDialog( i18n( "Error while writing file:\n%1", aFileName ) );
}
}
return false;
}
const int writeLen = file.write( aBuffer.data(), aBuffer.size() );
if ( writeLen < 0 ) {
if ( aVerbose ) {
msgDialog( i18n( "Could not write to file:\n%1", aFileName ) );
}
return false;
} else if ( writeLen < aBuffer.size() ) {
QString msg = i18np( "Could only write 1 byte of %2.",
"Could only write %1 bytes of %2.",
writeLen, aBuffer.size() );
if ( aVerbose ) {
msgDialog( msg );
}
return false;
}
return true;
}
QString checkAndCorrectPermissionsIfPossible( const QString &toCheck,
const bool recursive,
const bool wantItReadable,
const bool wantItWritable )
{
// First we have to find out which type the toCheck is. This can be
// a directory (follow if recursive) or a file (check permissions).
// Symlinks are followed as expected.
QFileInfo fiToCheck( toCheck );
fiToCheck.setCaching( false );
QByteArray toCheckEnc = QFile::encodeName( toCheck );
QString error;
KDE_struct_stat statbuffer;
if ( !fiToCheck.exists() ) {
error.append( i18n( "%1 does not exist", toCheck ) + QLatin1Char('\n') );
}
// check the access bit of a folder.
if ( fiToCheck.isDir() ) {
if ( KDE_stat( toCheckEnc, &statbuffer ) != 0 ) {
kDebug() << "wantItA: Can't read perms of" << toCheck;
}
QDir g( toCheck );
if ( !g.isReadable() ) {
if ( chmod( toCheckEnc, statbuffer.st_mode + S_IXUSR ) != 0 ) {
error.append( i18n( "%1 is not accessible and that is "
"unchangeable.", toCheck ) + QLatin1Char('\n') );
} else {
kDebug() << "Changed access bit for" << toCheck;
}
}
}
// For each file or folder we can check if the file is readable
// and writable, as requested.
if ( fiToCheck.isFile() || fiToCheck.isDir() ) {
if ( !fiToCheck.isReadable() && wantItReadable ) {
// Get the current permissions. No need to do anything with an
// error, it will het added to errors anyhow, later on.
if ( KDE_stat( toCheckEnc, &statbuffer ) != 0 ) {
kDebug() << "wantItR: Can't read perms of" << toCheck;
}
// Lets try changing it.
if ( chmod( toCheckEnc, statbuffer.st_mode + S_IRUSR ) != 0 ) {
error.append( i18n( "%1 is not readable and that is unchangeable.",
toCheck ) + QLatin1Char('\n') );
} else {
kDebug() << "Changed the read bit for" << toCheck;
}
}
if ( !fiToCheck.isWritable() && wantItWritable ) {
// Gets the current persmissions. Needed because it can be changed
// curing previous operation.
if ( KDE_stat( toCheckEnc, &statbuffer ) != 0 ) {
kDebug() << "wantItW: Can't read perms of" << toCheck;
}
// Lets try changing it.
if ( chmod ( toCheckEnc, statbuffer.st_mode + S_IWUSR ) != 0 ) {
error.append( i18n( "%1 is not writable and that is unchangeable.", toCheck ) + QLatin1Char('\n') );
} else {
kDebug() << "Changed the write bit for" << toCheck;
}
}
}
// If it is a folder and recursive is true, then we check the contents of
// the folder.
if ( fiToCheck.isDir() && recursive ) {
QDir g( toCheck );
// First check if the folder is readable for us. If not, we get
// some ugly crashes.
if ( !g.isReadable() ) {
error.append( i18n( "Folder %1 is inaccessible.", toCheck ) + QLatin1Char('\n') );
} else {
foreach ( const QFileInfo &fi, g.entryInfoList() ) {
QString newToCheck = toCheck + QLatin1Char('/') + fi.fileName();
if ( fi.fileName() != QLatin1String(".") && fi.fileName() != QLatin1String("..") ) {
error.append (
checkAndCorrectPermissionsIfPossible( newToCheck, recursive,
wantItReadable, wantItWritable ) );
}
}
}
}
return error;
}
bool checkAndCorrectPermissionsIfPossibleWithErrorHandling( QWidget *parent,
const QString &toCheck,
const bool recursive,
const bool wantItReadable,
const bool wantItWritable )
{
QString error =
checkAndCorrectPermissionsIfPossible( toCheck, recursive, wantItReadable, wantItWritable );
// There is no KMessageBox with Retry, Cancel and Details.
// so, I can't provide a functionality to recheck. So it now
// it is just a warning.
if ( !error.isEmpty() ) {
kDebug() << "checkPermissions found:" << error;
KMessageBox::detailedSorry( parent,
i18n( "Some files or folders do not have the "
"necessary permissions, please correct "
"them manually." ),
error, i18n( "Permissions Check" ), 0 );
return false;
} else {
return true;
}
}
bool removeDirAndContentsRecursively( const QString & path )
{
bool success = true;
QDir d;
d.setPath( path );
d.setFilter( QDir::Files | QDir::Dirs | QDir::Hidden | QDir::NoSymLinks );
QFileInfoList list = d.entryInfoList();
Q_FOREACH ( const QFileInfo &fi, list ) {
if ( fi.isDir() ) {
if ( fi.fileName() != QLatin1String(".") && fi.fileName() != QLatin1String("..") ) {
success = success && removeDirAndContentsRecursively( fi.absoluteFilePath() );
}
} else {
success = success && d.remove( fi.absoluteFilePath() );
}
}
if ( success ) {
success = success && d.rmdir( path ); // nuke ourselves, we should be empty now
}
return success;
}
}