chmodjob.cpp
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2000 Stephan Kulow <coolo@kde.org> 00003 David Faure <faure@kde.org> 00004 Waldo Bastian <bastian@kde.org> 00005 00006 This library is free software; you can redistribute it and/or 00007 modify it under the terms of the GNU Library General Public 00008 License as published by the Free Software Foundation; either 00009 version 2 of the License, or (at your option) any later version. 00010 00011 This library is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 Library General Public License for more details. 00015 00016 You should have received a copy of the GNU Library General Public License 00017 along with this library; see the file COPYING.LIB. If not, write to 00018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00019 Boston, MA 02110-1301, USA. 00020 */ 00021 00022 #include <config.h> 00023 00024 #include <pwd.h> 00025 #include <grp.h> 00026 #include <sys/types.h> 00027 #include <unistd.h> 00028 #include <assert.h> 00029 00030 #include <tqtimer.h> 00031 #include <tqfile.h> 00032 #include <klocale.h> 00033 #include <kdebug.h> 00034 #include <kmessagebox.h> 00035 00036 #include "kio/job.h" 00037 #include "kio/chmodjob.h" 00038 00039 #include <kdirnotify_stub.h> 00040 00041 using namespace KIO; 00042 00043 ChmodJob::ChmodJob( const KFileItemList& lstItems, int permissions, int mask, 00044 int newOwner, int newGroup, 00045 bool recursive, bool showProgressInfo ) 00046 : KIO::Job( showProgressInfo ), state( STATE_LISTING ), 00047 m_permissions( permissions ), m_mask( mask ), 00048 m_newOwner( newOwner ), m_newGroup( newGroup ), 00049 m_recursive( recursive ), m_lstItems( lstItems ) 00050 { 00051 TQTimer::singleShot( 0, this, TQT_SLOT(processList()) ); 00052 } 00053 00054 void ChmodJob::processList() 00055 { 00056 while ( !m_lstItems.isEmpty() ) 00057 { 00058 KFileItem * item = m_lstItems.first(); 00059 if ( !item->isLink() ) // don't do anything with symlinks 00060 { 00061 // File or directory -> remember to chmod 00062 ChmodInfo info; 00063 info.url = item->url(); 00064 // This is a toplevel file, we apply changes directly (no +X emulation here) 00065 info.permissions = ( m_permissions & m_mask ) | ( item->permissions() & ~m_mask ); 00066 /*kdDebug(7007) << "\n current permissions=" << TQString::number(item->permissions(),8) 00067 << "\n wanted permission=" << TQString::number(m_permissions,8) 00068 << "\n with mask=" << TQString::number(m_mask,8) 00069 << "\n with ~mask (mask bits we keep) =" << TQString::number((uint)~m_mask,8) 00070 << "\n bits we keep =" << TQString::number(item->permissions() & ~m_mask,8) 00071 << "\n new permissions = " << TQString::number(info.permissions,8) 00072 << endl;*/ 00073 m_infos.prepend( info ); 00074 //kdDebug(7007) << "processList : Adding info for " << info.url.prettyURL() << endl; 00075 // Directory and recursive -> list 00076 if ( item->isDir() && m_recursive ) 00077 { 00078 //kdDebug(7007) << "ChmodJob::processList dir -> listing" << endl; 00079 KIO::ListJob * listJob = KIO::listRecursive( item->url(), false /* no GUI */ ); 00080 connect( listJob, TQT_SIGNAL(entries( KIO::Job *, 00081 const KIO::UDSEntryList& )), 00082 TQT_SLOT( slotEntries( KIO::Job*, 00083 const KIO::UDSEntryList& ))); 00084 addSubjob( listJob ); 00085 return; // we'll come back later, when this one's finished 00086 } 00087 } 00088 m_lstItems.removeFirst(); 00089 } 00090 kdDebug(7007) << "ChmodJob::processList -> going to STATE_CHMODING" << endl; 00091 // We have finished, move on 00092 state = STATE_CHMODING; 00093 chmodNextFile(); 00094 } 00095 00096 void ChmodJob::slotEntries( KIO::Job*, const KIO::UDSEntryList & list ) 00097 { 00098 KIO::UDSEntryListConstIterator it = list.begin(); 00099 KIO::UDSEntryListConstIterator end = list.end(); 00100 for (; it != end; ++it) { 00101 KIO::UDSEntry::ConstIterator it2 = (*it).begin(); 00102 mode_t permissions = 0; 00103 bool isDir = false; 00104 bool isLink = false; 00105 TQString relativePath; 00106 for( ; it2 != (*it).end(); it2++ ) { 00107 switch( (*it2).m_uds ) { 00108 case KIO::UDS_NAME: 00109 relativePath = (*it2).m_str; 00110 break; 00111 case KIO::UDS_FILE_TYPE: 00112 isDir = S_ISDIR((*it2).m_long); 00113 break; 00114 case KIO::UDS_LINK_DEST: 00115 isLink = !(*it2).m_str.isEmpty(); 00116 break; 00117 case KIO::UDS_ACCESS: 00118 permissions = (mode_t)((*it2).m_long); 00119 break; 00120 default: 00121 break; 00122 } 00123 } 00124 if ( !isLink && relativePath != TQString::fromLatin1("..") ) 00125 { 00126 ChmodInfo info; 00127 info.url = m_lstItems.first()->url(); // base directory 00128 info.url.addPath( relativePath ); 00129 int mask = m_mask; 00130 // Emulate -X: only give +x to files that had a +x bit already 00131 // So the check is the opposite : if the file had no x bit, don't touch x bits 00132 // For dirs this doesn't apply 00133 if ( !isDir ) 00134 { 00135 int newPerms = m_permissions & mask; 00136 if ( (newPerms & 0111) && !(permissions & 0111) ) 00137 { 00138 // don't interfere with mandatory file locking 00139 if ( newPerms & 02000 ) 00140 mask = mask & ~0101; 00141 else 00142 mask = mask & ~0111; 00143 } 00144 } 00145 info.permissions = ( m_permissions & mask ) | ( permissions & ~mask ); 00146 /*kdDebug(7007) << "\n current permissions=" << TQString::number(permissions,8) 00147 << "\n wanted permission=" << TQString::number(m_permissions,8) 00148 << "\n with mask=" << TQString::number(mask,8) 00149 << "\n with ~mask (mask bits we keep) =" << TQString::number((uint)~mask,8) 00150 << "\n bits we keep =" << TQString::number(permissions & ~mask,8) 00151 << "\n new permissions = " << TQString::number(info.permissions,8) 00152 << endl;*/ 00153 // Prepend this info in our todo list. 00154 // This way, the toplevel dirs are done last. 00155 m_infos.prepend( info ); 00156 } 00157 } 00158 } 00159 00160 void ChmodJob::chmodNextFile() 00161 { 00162 if ( !m_infos.isEmpty() ) 00163 { 00164 ChmodInfo info = m_infos.first(); 00165 m_infos.remove( m_infos.begin() ); 00166 // First update group / owner (if local file) 00167 // (permissions have to set after, in case of suid and sgid) 00168 if ( info.url.isLocalFile() && ( m_newOwner != -1 || m_newGroup != -1 ) ) 00169 { 00170 TQString path = info.url.path(); 00171 if ( chown( TQFile::encodeName(path), m_newOwner, m_newGroup ) != 0 ) 00172 { 00173 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), TQString::null, i18n("&Skip File") ); 00174 if (answer == KMessageBox::Cancel) 00175 { 00176 m_error = ERR_USER_CANCELED; 00177 emitResult(); 00178 return; 00179 } 00180 } 00181 } 00182 00183 kdDebug(7007) << "ChmodJob::chmodNextFile chmod'ing " << info.url.prettyURL() 00184 << " to " << TQString::number(info.permissions,8) << endl; 00185 KIO::SimpleJob * job = KIO::chmod( info.url, info.permissions ); 00186 // copy the metadata for acl and default acl 00187 const TQString aclString = queryMetaData( "ACL_STRING" ); 00188 const TQString defaultAclString = queryMetaData( "DEFAULT_ACL_STRING" ); 00189 if ( !aclString.isEmpty() ) 00190 job->addMetaData( "ACL_STRING", aclString ); 00191 if ( !defaultAclString.isEmpty() ) 00192 job->addMetaData( "DEFAULT_ACL_STRING", defaultAclString ); 00193 addSubjob(job); 00194 } 00195 else 00196 // We have finished 00197 emitResult(); 00198 } 00199 00200 void ChmodJob::slotResult( KIO::Job * job ) 00201 { 00202 if ( job->error() ) 00203 { 00204 m_error = job->error(); 00205 m_errorText = job->errorText(); 00206 emitResult(); 00207 return; 00208 } 00209 //kdDebug(7007) << " ChmodJob::slotResult( KIO::Job * job ) m_lstItems:" << m_lstItems.count() << endl; 00210 switch ( state ) 00211 { 00212 case STATE_LISTING: 00213 subjobs.remove(job); 00214 m_lstItems.removeFirst(); 00215 kdDebug(7007) << "ChmodJob::slotResult -> processList" << endl; 00216 processList(); 00217 return; 00218 case STATE_CHMODING: 00219 subjobs.remove(job); 00220 kdDebug(7007) << "ChmodJob::slotResult -> chmodNextFile" << endl; 00221 chmodNextFile(); 00222 return; 00223 default: 00224 assert(0); 00225 return; 00226 } 00227 } 00228 00229 // antlarr: KDE 4: Make owner and group be const TQString & 00230 KIO_EXPORT ChmodJob *KIO::chmod( const KFileItemList& lstItems, int permissions, int mask, 00231 TQString owner, TQString group, 00232 bool recursive, bool showProgressInfo ) 00233 { 00234 uid_t newOwnerID = (uid_t)-1; // chown(2) : -1 means no change 00235 if ( !owner.isEmpty() ) 00236 { 00237 struct passwd* pw = getpwnam(TQFile::encodeName(owner)); 00238 if ( pw == 0L ) 00239 kdError(250) << " ERROR: No user " << owner << endl; 00240 else 00241 newOwnerID = pw->pw_uid; 00242 } 00243 gid_t newGroupID = (gid_t)-1; // chown(2) : -1 means no change 00244 if ( !group.isEmpty() ) 00245 { 00246 struct group* g = getgrnam(TQFile::encodeName(group)); 00247 if ( g == 0L ) 00248 kdError(250) << " ERROR: No group " << group << endl; 00249 else 00250 newGroupID = g->gr_gid; 00251 } 00252 return new ChmodJob( lstItems, permissions, mask, newOwnerID, newGroupID, recursive, showProgressInfo ); 00253 } 00254 00255 void ChmodJob::virtual_hook( int id, void* data ) 00256 { KIO::Job::virtual_hook( id, data ); } 00257 00258 #include "chmodjob.moc"