chmodjob.cpp
/* This file is part of the KDE libraries
Copyright (C) 2000 Stephan Kulow <coolo@kde.org>
David Faure <faure@kde.org>
Waldo Bastian <bastian@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 <config.h>
#include <pwd.h>
#include <grp.h>
#include <sys/types.h>
#include <unistd.h>
#include <assert.h>
#include <qtimer.h>
#include <qfile.h>
#include <klocale.h>
#include <kdebug.h>
#include <kmessagebox.h>
#include "kio/job.h"
#include "kio/chmodjob.h"
#include <kdirnotify_stub.h>
using namespace KIO;
ChmodJob::ChmodJob( const KFileItemList& lstItems, int permissions, int mask,
int newOwner, int newGroup,
bool recursive, bool showProgressInfo )
: KIO::Job( showProgressInfo ), state( STATE_LISTING ),
m_permissions( permissions ), m_mask( mask ),
m_newOwner( newOwner ), m_newGroup( newGroup ),
m_recursive( recursive ), m_lstItems( lstItems )
{
QTimer::singleShot( 0, this, SLOT(processList()) );
}
void ChmodJob::processList()
{
while ( !m_lstItems.isEmpty() )
{
KFileItem * item = m_lstItems.first();
if ( !item->isLink() ) // don't do anything with symlinks
{
// File or directory -> remember to chmod
ChmodInfo info;
info.url = item->url();
// This is a toplevel file, we apply changes directly (no +X emulation here)
info.permissions = ( m_permissions & m_mask ) | ( item->permissions() & ~m_mask );
/*kdDebug(7007) << "\n current permissions=" << QString::number(item->permissions(),8)
<< "\n wanted permission=" << QString::number(m_permissions,8)
<< "\n with mask=" << QString::number(m_mask,8)
<< "\n with ~mask (mask bits we keep) =" << QString::number((uint)~m_mask,8)
<< "\n bits we keep =" << QString::number(item->permissions() & ~m_mask,8)
<< "\n new permissions = " << QString::number(info.permissions,8)
<< endl;*/
m_infos.prepend( info );
//kdDebug(7007) << "processList : Adding info for " << info.url.prettyURL() << endl;
// Directory and recursive -> list
if ( item->isDir() && m_recursive )
{
//kdDebug(7007) << "ChmodJob::processList dir -> listing" << endl;
KIO::ListJob * listJob = KIO::listRecursive( item->url(), false /* no GUI */ );
connect( listJob, SIGNAL(entries( KIO::Job *,
const KIO::UDSEntryList& )),
SLOT( slotEntries( KIO::Job*,
const KIO::UDSEntryList& )));
addSubjob( listJob );
return; // we'll come back later, when this one's finished
}
}
m_lstItems.removeFirst();
}
kdDebug(7007) << "ChmodJob::processList -> going to STATE_CHMODING" << endl;
// We have finished, move on
state = STATE_CHMODING;
chmodNextFile();
}
void ChmodJob::slotEntries( KIO::Job*, const KIO::UDSEntryList & list )
{
KIO::UDSEntryListConstIterator it = list.begin();
KIO::UDSEntryListConstIterator end = list.end();
for (; it != end; ++it) {
KIO::UDSEntry::ConstIterator it2 = (*it).begin();
mode_t permissions = 0;
bool isDir = false;
bool isLink = false;
QString relativePath;
for( ; it2 != (*it).end(); it2++ ) {
switch( (*it2).m_uds ) {
case KIO::UDS_NAME:
relativePath = (*it2).m_str;
break;
case KIO::UDS_FILE_TYPE:
isDir = S_ISDIR((*it2).m_long);
break;
case KIO::UDS_LINK_DEST:
isLink = !(*it2).m_str.isEmpty();
break;
case KIO::UDS_ACCESS:
permissions = (mode_t)((*it2).m_long);
break;
default:
break;
}
}
if ( !isLink && relativePath != QString::fromLatin1("..") )
{
ChmodInfo info;
info.url = m_lstItems.first()->url(); // base directory
info.url.addPath( relativePath );
int mask = m_mask;
// Emulate -X: only give +x to files that had a +x bit already
// So the check is the opposite : if the file had no x bit, don't touch x bits
// For dirs this doesn't apply
if ( !isDir )
{
int newPerms = m_permissions & mask;
if ( (newPerms & 0111) && !(permissions & 0111) )
{
// don't interfere with mandatory file locking
if ( newPerms & 02000 )
mask = mask & ~0101;
else
mask = mask & ~0111;
}
}
info.permissions = ( m_permissions & mask ) | ( permissions & ~mask );
/*kdDebug(7007) << "\n current permissions=" << QString::number(permissions,8)
<< "\n wanted permission=" << QString::number(m_permissions,8)
<< "\n with mask=" << QString::number(mask,8)
<< "\n with ~mask (mask bits we keep) =" << QString::number((uint)~mask,8)
<< "\n bits we keep =" << QString::number(permissions & ~mask,8)
<< "\n new permissions = " << QString::number(info.permissions,8)
<< endl;*/
// Prepend this info in our todo list.
// This way, the toplevel dirs are done last.
m_infos.prepend( info );
}
}
}
void ChmodJob::chmodNextFile()
{
if ( !m_infos.isEmpty() )
{
ChmodInfo info = m_infos.first();
m_infos.remove( m_infos.begin() );
// First update group / owner (if local file)
// (permissions have to set after, in case of suid and sgid)
if ( info.url.isLocalFile() && ( m_newOwner != -1 || m_newGroup != -1 ) )
{
QString path = info.url.path();
if ( chown( QFile::encodeName(path), m_newOwner, m_newGroup ) != 0 )
{
int answer = KMessageBox::warningContinueCancel( 0, i18n( "<qt>Could not modify the ownership of file <b>%1</b>. You have insufficient access to the file to perform the change.</qt>" ).arg(path), QString::null, i18n("&Skip File") );
if (answer == KMessageBox::Cancel)
{
m_error = ERR_USER_CANCELED;
emitResult();
return;
}
}
}
kdDebug(7007) << "ChmodJob::chmodNextFile chmod'ing " << info.url.prettyURL()
<< " to " << QString::number(info.permissions,8) << endl;
KIO::SimpleJob * job = KIO::chmod( info.url, info.permissions );
// copy the metadata for acl and default acl
const QString aclString = queryMetaData( "ACL_STRING" );
const QString defaultAclString = queryMetaData( "DEFAULT_ACL_STRING" );
if ( !aclString.isEmpty() )
job->addMetaData( "ACL_STRING", aclString );
if ( !defaultAclString.isEmpty() )
job->addMetaData( "DEFAULT_ACL_STRING", defaultAclString );
addSubjob(job);
}
else
// We have finished
emitResult();
}
void ChmodJob::slotResult( KIO::Job * job )
{
if ( job->error() )
{
m_error = job->error();
m_errorText = job->errorText();
emitResult();
return;
}
//kdDebug(7007) << " ChmodJob::slotResult( KIO::Job * job ) m_lstItems:" << m_lstItems.count() << endl;
switch ( state )
{
case STATE_LISTING:
subjobs.remove(job);
m_lstItems.removeFirst();
kdDebug(7007) << "ChmodJob::slotResult -> processList" << endl;
processList();
return;
case STATE_CHMODING:
subjobs.remove(job);
kdDebug(7007) << "ChmodJob::slotResult -> chmodNextFile" << endl;
chmodNextFile();
return;
default:
assert(0);
return;
}
}
// antlarr: KDE 4: Make owner and group be const QString &
KIO_EXPORT ChmodJob *KIO::chmod( const KFileItemList& lstItems, int permissions, int mask,
QString owner, QString group,
bool recursive, bool showProgressInfo )
{
uid_t newOwnerID = (uid_t)-1; // chown(2) : -1 means no change
if ( !owner.isEmpty() )
{
struct passwd* pw = getpwnam(QFile::encodeName(owner));
if ( pw == 0L )
kdError(250) << " ERROR: No user " << owner << endl;
else
newOwnerID = pw->pw_uid;
}
gid_t newGroupID = (gid_t)-1; // chown(2) : -1 means no change
if ( !group.isEmpty() )
{
struct group* g = getgrnam(QFile::encodeName(group));
if ( g == 0L )
kdError(250) << " ERROR: No group " << group << endl;
else
newGroupID = g->gr_gid;
}
return new ChmodJob( lstItems, permissions, mask, newOwnerID, newGroupID, recursive, showProgressInfo );
}
void ChmodJob::virtual_hook( int id, void* data )
{ KIO::Job::virtual_hook( id, data ); }
#include "chmodjob.moc"
KFileItem::url
const KURL & url() const
Returns the url of the file.
Definition: kfileitem.h:113
KIO::Job::addMetaData
void addMetaData(const QString &key, const QString &value)
Add key/value pair to the meta data that is sent to the slave.
Definition: job.cpp:405
KFileItem::isDir
bool isDir() const
Returns true if this item represents a directory.
Definition: kfileitem.cpp:682
KIO::Job::errorText
const QString & errorText() const
Returns the error text if there has been an error.
Definition: jobclasses.h:111
KIO::ListJob
A ListJob is allows you to get the get the content of a directory.
Definition: jobclasses.h:1392
KFileItem::permissions
mode_t permissions() const
Returns the permissions of the file (stat.st_mode containing only permissions).
Definition: kfileitem.h:134
KIO::Job::queryMetaData
QString queryMetaData(const QString &key)
Query meta data received from the slave.
Definition: job.cpp:393
KIO::Job::addSubjob
virtual void addSubjob(Job *job, bool inheritMetaData=true)
Add a job that has to be finished before a result is emitted.
Definition: job.cpp:162
KIO::chmod
KIO_EXPORT ChmodJob * chmod(const KFileItemList &lstItems, int permissions, int mask, QString newOwner, QString newGroup, bool recursive, bool showProgressInfo=true)
Creates a job that changes permissions/ownership on several files or directories, optionally recursiv...
Definition: chmodjob.cpp:230
KIO::ChmodJob
This job changes permissions on a list of files or directories, optionally in a recursive manner...
Definition: chmodjob.h:38
KIO::UDS_NAME
Filename - as displayed in directory listings etc.
Definition: global.h:332
KIO::UDS_FILE_TYPE
File type, part of the mode returned by stat (for a link, this returns the file type of the pointed i...
Definition: global.h:363
KIO::Job::emitResult
void emitResult()
Utility function to emit the result signal, and suicide this job.
Definition: job.cpp:228
KIO::UDS_LINK_DEST
Name of the file where the link points to Allows to check for a symlink (don't use S_ISLNK !) ...
Definition: global.h:366
KIO::Job::error
int error() const
Returns the error code, if there has been an error.
Definition: jobclasses.h:95
KFileItem::isLink
bool isLink() const
Returns true if this item represents a link in the UNIX sense of a link.
Definition: kfileitem.h:186
KIO::UDS_ACCESS
Access permissions (part of the mode returned by stat)
Definition: global.h:353
KIO::ChmodJob::ChmodJob
ChmodJob(const KFileItemList &lstItems, int permissions, int mask, int newOwner, int newGroup, bool recursive, bool showProgressInfo)
Create new ChmodJobs using the KIO::chmod() function.
Definition: chmodjob.cpp:43
KIO::listRecursive
KIO_EXPORT ListJob * listRecursive(const KURL &url, bool showProgressInfo=true, bool includeHidden=true)
The same as the previous method, but recurses subdirectories.
Definition: job.cpp:2158
KIO::Job
The base class for all jobs.
Definition: jobclasses.h:68
KFileItem
A KFileItem is a generic class to handle a file, local or remote.
Definition: kfileitem.h:41
KIO::SimpleJob
A simple job (one url and one command).
Definition: jobclasses.h:528

KDE Home | KDE Accessibility Home | Description of Access Keys