/*************************************************************************** smb4tdefileio - Does file IO operations for Smb4K ------------------- begin : Do Jan 1 2004 copyright : (C) 2004-2007 by Alexander Reinholdt email : dustpuppy@users.berlios.de ***************************************************************************/ /*************************************************************************** * 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 of the License, or * * (at your option) any later version. * * * * 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, write to the * * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ // TQt includes #include #include #include // KDE includes #include #include #include // system specific includes #include #include #include #include #include #include #include #include // application specific includes #include "smb4tdefileio.h" #include "smb4kdefs.h" #include "smb4kerror.h" #include "smb4tdeglobal.h" #include "smb4ksettings.h" using namespace Smb4TDEGlobal; Smb4KFileIO::Smb4KFileIO( TQObject *parent, const char *name ) : TQObject( parent, name ) { m_operation = NoOperation; m_state = Idle; m_error_occurred = false; m_proc = new TDEProcess( this, "FileIOProcess" ); m_proc->setUseShell( true ); connect( m_proc, TQ_SIGNAL( receivedStderr( TDEProcess *, char *, int ) ), this, TQ_SLOT( slotReceivedStderr( TDEProcess *, char *, int ) ) ); connect( m_proc, TQ_SIGNAL( receivedStdout( TDEProcess *, char *, int ) ), this, TQ_SLOT( slotReceivedStdout( TDEProcess *, char *, int ) ) ); connect( m_proc, TQ_SIGNAL( processExited( TDEProcess * ) ), this, TQ_SLOT( slotProcessExited( TDEProcess * ) ) ); connect( kapp, TQ_SIGNAL( shutDown() ), this, TQ_SLOT( slotShutdown() ) ); } Smb4KFileIO::~Smb4KFileIO() { } bool Smb4KFileIO::writeSudoers( Smb4KFileIO::Operation operation ) { m_operation = operation; bool ok = false; // Stop here if nothing has changed: if ( m_operation == NoOperation ) { emit finished(); ok = true; return ok; } TQString file = "sudoers"; if ( createLockFile( file ) ) { // Find the file first: TQCString canonical_path = findFile( file ); if ( !canonical_path.isEmpty() ) { // Stat the file, so that we know that it is safe to // read from and write to it and whether we need to // ask for the super user's password: struct stat buf; if ( lstat( canonical_path, &buf ) == -1 ) { int error_number = errno; Smb4KError::error( ERROR_GETTING_PERMISSIONS, canonical_path, strerror( error_number ) ); emit failed(); emit finished(); removeLockFile(); return ok; // false } // Look for the groups the user is in: long ngroups_max; ngroups_max = sysconf(_SC_NGROUPS_MAX); gid_t list[ngroups_max]; if ( getgroups( ngroups_max, list ) == -1 ) { int error_number = errno; Smb4KError::error( ERROR_GETTING_GIDS, TQString(), strerror( error_number ) ); emit failed(); emit finished(); return ok; // false } gid_t sup_gid = 65534; // set this to gid 'nobody' for initialization bool found_gid = false; int i = 0; while ( list[i] ) { if ( list[i] == buf.st_gid ) { sup_gid = list[i]; found_gid = true; } i++; } // Error out if the file is irregular. // Yes, yes, I know that this is normally done in a different // way and that there might be a race here, but, hey, right here // I don't care! if ( !S_ISREG( buf.st_mode ) || S_ISFIFO( buf.st_mode ) || S_ISLNK( buf.st_mode ) ) { Smb4KError::error( ERROR_FILE_IS_IRREGULAR, canonical_path, TQString() ); emit failed(); emit finished(); removeLockFile(); return ok; // false } // Check access rights: if ( (buf.st_uid == getuid() && (buf.st_mode & 00600) == (S_IWUSR | S_IRUSR)) /* user */ || (found_gid && buf.st_gid == sup_gid && (buf.st_mode & 00060) == (S_IWGRP | S_IRGRP)) /* group */ || ((buf.st_mode & 00006) == (S_IWOTH | S_IROTH)) /* others */ ) { // The user has read and write access. TQFile file ( canonical_path ); TQStringList contents; if ( file.open( IO_ReadWrite ) ) { TQTextStream ts( &file ); ts.setEncoding( TQTextStream::Locale ); contents = TQStringList::split( "\n", ts.read(), true ); bool write = false; switch ( m_operation ) { case Insert: { size_t hostnamelen = 255; char *hn = new char[hostnamelen]; if ( gethostname( hn, hostnamelen ) == -1 ) { int error_number = errno; Smb4KError::error( ERROR_GETTING_HOSTNAME, TQString(), strerror( error_number ) ); emit failed(); emit finished(); removeLockFile(); return ok; // false } TQString hostname( hn ); delete [] hn; if ( contents.grep( "# Entries for Smb4K users." ).count() == 0 ) { contents.append( "# Entries for Smb4K users." ); contents.append( "# Generated by Smb4K. Please do not modify!" ); contents.append( "User_Alias\tSMB4KUSERS = "+TQString( "%1" ).arg( getpwuid( getuid() )->pw_name ) ); contents.append( "Defaults:SMB4KUSERS\tenv_keep += \"PASSWD USER\"" ); contents.append( "SMB4KUSERS\t"+hostname+" = NOPASSWD: "+Smb4KSettings::smb4k_kill() ); contents.append( "SMB4KUSERS\t"+hostname+" = NOPASSWD: "+Smb4KSettings::smb4k_umount() ); contents.append( "SMB4KUSERS\t"+hostname+" = NOPASSWD: "+Smb4KSettings::smb4k_mount() ); contents.append( "# End of Smb4K user entries." ); write = true; } else { // Find the beginning and the end of the entries in // the sudoers file: TQStringList::Iterator begin = contents.find( "# Entries for Smb4K users." ); TQStringList::Iterator end = contents.find( "# End of Smb4K user entries." ); for ( TQStringList::Iterator it = begin; it != end; ++it ) { if ( (*it).startsWith( "User_Alias\tSMB4KUSERS" ) && (*it).contains( getpwuid( getuid() )->pw_name, true ) == 0 ) { (*it).append( ","+TQString( getpwuid( getuid() )->pw_name ) ); write = true; break; } else { continue; } } } break; } case Remove: { // Find the beginning and the end of the entries in // the sudoers file: TQStringList::Iterator begin = contents.find( "# Entries for Smb4K users." ); TQStringList::Iterator end = contents.find( "# End of Smb4K user entries." ); // Now, check if the user is in the list of users. If he is, // remove him from there or remove all if he is the only one: for ( TQStringList::Iterator it = begin; it != end; ++it ) { if ( (*it).startsWith( "User_Alias\tSMB4KUSERS" ) ) { TQString users = (*it).section( "=", 1, 1 ).stripWhiteSpace(); if ( users.contains( "," ) == 0 ) { // In this case, there is only one user in the list. Check if // it is the user who requested the removal: if ( TQString::compare( users, getpwuid( getuid() )->pw_name ) == 0 ) { // They are equal. Remove everything: contents.erase( begin, end ); contents.remove( end ); write = true; break; } else { // They are not equal: Do nothing. break; } } else { // In this case there is more than one user in the list. // Remove the user who requested the removal: TQStringList list = TQStringList::split( ",", users, false ); list.remove( getpwuid( getuid() )->pw_name ); (*it).replace( users, list.join( "," ) ); write = true; break; } } else { continue; } } break; } default: { file.close(); emit failed(); emit finished(); removeLockFile(); return ok; // false } }; if ( write ) { // Prepare the contents: remove empty lines from the end. TQStringList::Iterator it = contents.end(); // Move the iterator to the last entry in the list: --it; while ( (*it).stripWhiteSpace().isEmpty() ) { it = contents.remove( it ); --it; } // Now write the contents to the file. The permissions // will be preserved by this action. ts << contents.join( "\n" ) << endl; file.close(); } else { // The entries are already in the file. } ok = true; emit finished(); removeLockFile(); return ok; } else { Smb4KError::error( ERROR_OPENING_FILE, canonical_path, file.errorString() ); emit failed(); emit finished(); removeLockFile(); return ok; // false } } else { // The user does not have enough access rights to perform // the modification of the sudoers file. So, we need to use // tdesu to get the contents of the file. // Compose the command: TQString command; command.append( "tdesu -t -c \"smb4k_cat " ); command.append( canonical_path+"\"" ); command.append( " ; sleep 2" ); m_state = ReadSudoers; ok = true; *m_proc << command; m_proc->start( TDEProcess::NotifyOnExit, TDEProcess::AllOutput ); // The process is not finished, so finished() will be emitted // later and the lock file will also be removed at the end. return ok; } } else { Smb4KError::error( ERROR_FILE_NOT_FOUND, canonical_path ); emit failed(); emit finished(); removeLockFile(); return ok; // false } } else { // The error message has already been shown by // Smb4KFileIO::createLockFile() emit failed(); emit finished(); // We need not remove the lock file here. return ok; // false } return ok; } bool Smb4KFileIO::writeSuperTab( Smb4KFileIO::Operation operation ) { m_operation = operation; bool ok = false; // Stop here if nothing has changed: if ( m_operation == NoOperation ) { emit finished(); ok = true; return ok; } TQString file = "super.tab"; if ( createLockFile( file ) ) { // Find the file first: TQCString canonical_path = findFile( file ); if ( !canonical_path.isEmpty() ) { // Stat the file, so that we know that it is safe to // read from and write to it and whether we need to // ask for the super user's password: struct stat buf; if ( lstat( canonical_path, &buf ) == -1 ) { int error_number = errno; Smb4KError::error( ERROR_GETTING_PERMISSIONS, canonical_path, strerror( error_number ) ); emit failed(); emit finished(); removeLockFile(); return ok; // false } // Look for the groups the user is in: long ngroups_max; ngroups_max = sysconf(_SC_NGROUPS_MAX); gid_t list[ngroups_max]; if ( getgroups( ngroups_max, list ) == -1 ) { int error_number = errno; Smb4KError::error( ERROR_GETTING_GIDS, TQString(), strerror( error_number ) ); emit failed(); emit finished(); return ok; // false } gid_t sup_gid = 65534; // set this to gid 'nobody' for initialization bool found_gid = false; int i = 0; while ( list[i] ) { if ( list[i] == buf.st_gid ) { sup_gid = list[i]; found_gid = true; } i++; } // Error out if the file is irregular. // Yes, yes, I know that this is normally done in a different // way and that there might be a race here, but, hey, right here // I don't care! if ( !S_ISREG( buf.st_mode ) || S_ISFIFO( buf.st_mode ) || S_ISLNK( buf.st_mode ) ) { Smb4KError::error( ERROR_FILE_IS_IRREGULAR, canonical_path, TQString() ); emit failed(); emit finished(); removeLockFile(); return ok; // false } // Check access rights: if ( (buf.st_uid == getuid() && (buf.st_mode & 00600) == (S_IWUSR | S_IRUSR)) /* user */ || (found_gid && buf.st_gid == sup_gid && (buf.st_mode & 00060) == (S_IWGRP | S_IRGRP)) /* group */ || ((buf.st_mode & 00006) == (S_IWOTH | S_IROTH)) /* others */ ) { // The user has read and write access. TQFile file ( canonical_path ); TQStringList contents; if ( file.open( IO_ReadWrite ) ) { TQTextStream ts( &file ); ts.setEncoding( TQTextStream::Locale ); contents = TQStringList::split( "\n", ts.read(), true ); bool write = false; switch ( m_operation ) { case Insert: { size_t hostnamelen = 255; char *hn = new char[hostnamelen]; if ( gethostname( hn, hostnamelen ) == -1 ) { int error_number = errno; Smb4KError::error( ERROR_GETTING_HOSTNAME, TQString(), strerror( error_number ) ); emit failed(); emit finished(); removeLockFile(); return ok; // false } TQString hostname( hn ); delete [] hn; if ( contents.grep( "# Entries for Smb4K users." ).count() == 0 ) { contents.append( "# Entries for Smb4K users." ); contents.append( "# Generated by Smb4K. Please do not modify!" ); contents.append( ":define Smb4KUsers "+TQString( "%1" ).arg( getpwuid( getuid() )->pw_name ) ); #ifndef __FreeBSD__ contents.append( "smb4k_kill\t"+Smb4KSettings::smb4k_kill()+ "\t$(Smb4KUsers)\tuid=root\tgid=root" ); contents.append( "smb4k_umount\t"+Smb4KSettings::smb4k_umount()+ "\t$(Smb4KUsers)\tuid=root\tgid=root" ); contents.append( "smb4k_mount\t"+Smb4KSettings::smb4k_mount()+ "\t$(Smb4KUsers)\tuid=root\tgid=root\tenv=PASSWD,USER" ); #else contents.append( "smb4k_kill\t"+Smb4KSettings::smb4k_kill()+ "\t$(Smb4KUsers)\tuid=root\tgid=wheel" ); contents.append( "smb4k_umount\t"+Smb4KSettings::smb4k_umount()+ "\t$(Smb4KUsers)\tuid=root\tgid=wheel" ); contents.append( "smb4k_mount\t"+Smb4KSettings::smb4k_mount()+ "\t$(Smb4KUsers)\tuid=root\tgid=wheel\tsetenv=HOME=$CALLER_HOME\tenv=PASSWD,USER" ); #endif contents.append( "# End of Smb4K user entries." ); write = true; } else { // Find the beginning and the end of the entries in // the super.tab file: TQStringList::Iterator begin = contents.find( "# Entries for Smb4K users." ); TQStringList::Iterator end = contents.find( "# End of Smb4K user entries." ); for ( TQStringList::Iterator it = begin; it != end; ++it ) { if ( (*it).startsWith( ":define Smb4KUsers" ) && (*it).contains( getpwuid( getuid() )->pw_name, true ) == 0 ) { (*it).append( ","+TQString( getpwuid( getuid() )->pw_name ) ); write = true; break; } else { continue; } } } break; } case Remove: { // Find the beginning and the end of the entries in // the super.tab file: TQStringList::Iterator begin = contents.find( "# Entries for Smb4K users." ); TQStringList::Iterator end = contents.find( "# End of Smb4K user entries." ); // Now, check if the user is in the list of users. If he is, // remove him from there or remove all if he is the only one: for ( TQStringList::Iterator it = begin; it != end; ++it ) { if ( (*it).startsWith( ":define Smb4KUsers" ) ) { TQString users = (*it).section( "Smb4KUsers", 1, 1 ).stripWhiteSpace(); if ( users.contains( "," ) == 0 ) { // In this case, there is only one user in the list. Check if // it is the user who requested the removal: if ( TQString::compare( users, getpwuid( getuid() )->pw_name ) == 0 ) { // They are equal. Remove everything: contents.erase( begin, end ); contents.remove( end ); write = true; break; } else { // They are not equal: Do nothing. break; } } else { // In this case there is more than one user in the list. // Remove the user who requested the removal: TQStringList list = TQStringList::split( ",", users, false ); list.remove( getpwuid( getuid() )->pw_name ); (*it).replace( users, list.join( "," ) ); write = true; break; } } else { continue; } } break; } default: { file.close(); emit failed(); emit finished(); removeLockFile(); return ok; // false } }; if ( write ) { // Prepare the contents: remove empty lines from the end. TQStringList::Iterator it = contents.end(); // Move the iterator to the last entry in the list: --it; while ( (*it).stripWhiteSpace().isEmpty() ) { it = contents.remove( it ); --it; } // Now write the contents to the file. The permissions // will be preserved by this action. ts << contents.join( "\n" ) << endl; file.close(); } else { // The entries are already in the file. } ok = true; emit finished(); removeLockFile(); return ok; } else { Smb4KError::error( ERROR_OPENING_FILE, canonical_path, file.errorString() ); emit failed(); emit finished(); removeLockFile(); return ok; // false } } else { // The user does not have enough access rights to perform // the modification of the sudoers file. So, we need to use // tdesu to get the contents of the file. // Compose the command: TQString command; command.append( "tdesu -t -c \"smb4k_cat " ); command.append( canonical_path+"\"" ); command.append( " ; sleep 2" ); m_state = ReadSuperTab; ok = true; *m_proc << command; m_proc->start( TDEProcess::NotifyOnExit, TDEProcess::AllOutput ); // The process is not finished, so finished() will be emitted // later and the lock file will also be removed at the end. return ok; } } else { Smb4KError::error( ERROR_FILE_NOT_FOUND, canonical_path ); emit failed(); emit finished(); removeLockFile(); return ok; // false } } else { // The error message has already been shown by // Smb4KFileIO::createLockFile() emit failed(); emit finished(); // We need not remove the lock file here. return ok; // false } return ok; } bool Smb4KFileIO::createLockFile( const TQString &filename ) { bool ok = false; // Determine the directory where to write the lock file. First, try // /var/lock and than /var/tmp. If that does not work either, fall // back to /tmp. if ( m_lock_file.isEmpty() ) { TQValueList dirs; dirs << "/var/lock" << "/var/tmp" << "/tmp"; struct stat buf; for ( TQValueList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it ) { // First check if the directory is available and writable if ( lstat( *it, &buf ) == -1 ) { int error_number = errno; if ( error_number != EACCES && error_number != ENOENT ) { Smb4KError::error( ERROR_GETTING_PERMISSIONS, *it, strerror( error_number ) ); return ok; // false } } else { // Look for the groups the user is in: long ngroups_max; ngroups_max = sysconf(_SC_NGROUPS_MAX); gid_t list[ngroups_max]; if ( getgroups( ngroups_max, list ) == -1 ) { int error_number = errno; Smb4KError::error( ERROR_GETTING_GIDS, TQString(), strerror( error_number ) ); return ok; // false } gid_t sup_gid = 65534; // set this to gid 'nobody' for initialization bool found_gid = false; int i = 0; while ( list[i] ) { if ( list[i] == buf.st_gid ) { sup_gid = list[i]; found_gid = true; } i++; } // Check whether we are stat'ing a directory and that the // user has read/write permissions. if ( S_ISDIR( buf.st_mode ) /* is directory */ && (buf.st_uid == getuid() && (buf.st_mode & 00600) == (S_IWUSR | S_IRUSR)) /* user */ || (found_gid && buf.st_gid == sup_gid && (buf.st_mode & 00060) == (S_IWGRP | S_IRGRP)) /* group */ || ((buf.st_mode & 00006) == (S_IWOTH | S_IROTH)) /* others */ ) { m_lock_file = *it+"/smb4k.lock"; break; } else { continue; } } } } int file_descriptor; // Create the lock file if necessary and open it: if ( (file_descriptor = open( m_lock_file, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH )) == -1 ) { // Error out if the opening failed: int error_number = errno; Smb4KError::error( ERROR_OPENING_FILE, m_lock_file, strerror( error_number ) ); return ok; // false } else { // Check what we actually opened: struct stat file_stat; if ( fstat( file_descriptor, &file_stat ) == -1 ) { // Error out if we could not get the information about the file: int error_number = errno; // FIXME for >= 0.8.x: Change error code to ERROR_GETTING_STAT Smb4KError::error( ERROR_GETTING_PERMISSIONS, TQString(), strerror( error_number ) ); return ok; } if ( !S_ISREG( file_stat.st_mode ) || S_ISFIFO( file_stat.st_mode ) || S_ISLNK( file_stat.st_mode ) ) { // Close the file and error out, if we have opened an // "irregular" file (i.e. a symlink, a fifo, etc.). Smb4KError::error( ERROR_FILE_IS_IRREGULAR, m_lock_file ); if ( close( file_descriptor ) == -1 ) { int error_number = errno; Smb4KError::error( ERROR_CLOSING_FILE, m_lock_file, strerror( error_number ) ); } return ok; // false } else { // Continue if the file is regular. char buffer[1000]; ssize_t size; if ( (size = read( file_descriptor, buffer, 1000 )) == -1 ) { int error_number = errno; Smb4KError::error( ERROR_READING_FILE, m_lock_file, strerror( error_number ) ); return ok; // false } if ( size >= 1000 ) { // FIXME for >= 0.8.x: Change error code to ERROR_BUFFER_EXCEEDED Smb4KError::error( ERROR_UNKNOWN, TQString(), i18n( "Buffer size exceeded" ) ); return ok; // false } TQStringList contents = TQStringList::split( '\n', TQString::fromLocal8Bit( buffer, size ), false ); TQString test_string = ":"+filename; TQString entry = contents.grep( test_string, true ).join( "\n" ).stripWhiteSpace(); if ( !entry.isEmpty() ) { Smb4KError::error( ERROR_LOCKED, entry ); return ok; // false } else { contents << TQString( "%1:%2" ).arg( getpwuid( getuid() )->pw_name ).arg( filename ); TQCString out = contents.join( "\n" ).local8Bit(); if ( write( file_descriptor, out, out.length() ) == -1 ) { int error_number = errno; Smb4KError::error( ERROR_WRITING_FILE, m_lock_file, strerror( error_number ) ); return ok; // false } if ( close( file_descriptor ) == -1 ) { int error_number = errno; Smb4KError::error( ERROR_CLOSING_FILE, m_lock_file, strerror( error_number ) ); return ok; // false } ok = true; } } } return ok; } bool Smb4KFileIO::removeLockFile( const bool error_message ) { // We already have the name and location of the lock // file, so we do not need to define it here. int file_descriptor; bool ok = false; // Open the lock file: if ( (file_descriptor = open( m_lock_file, O_RDWR )) == -1 ) { // Error out if the opening failed: int error_number = errno; if ( error_message && error_number != ENOENT ) { Smb4KError::error( ERROR_OPENING_FILE, m_lock_file, strerror( error_number ) ); } return ok; // false } else { // Check what we actually opened: struct stat file_stat; if ( fstat( file_descriptor, &file_stat ) == -1 ) { // Error out if we could not get the information about the file: int error_number = errno; if ( error_message ) { // FIXME for >= 0.8.x: Change error code to ERROR_GETTING_STAT Smb4KError::error( ERROR_GETTING_PERMISSIONS, TQString(), strerror( error_number ) ); } return ok; } if ( !S_ISREG( file_stat.st_mode ) || S_ISFIFO( file_stat.st_mode ) || S_ISLNK( file_stat.st_mode ) ) { // Close the file and error out, if we have opened an // "irregular" file (i.e. a symlink, a fifo, etc.). if ( error_message ) { Smb4KError::error( ERROR_FILE_IS_IRREGULAR, m_lock_file ); } if ( close( file_descriptor ) == -1 ) { int error_number = errno; if ( error_message ) { Smb4KError::error( ERROR_CLOSING_FILE, m_lock_file, strerror( error_number ) ); } } return ok; // false } else { // Continue if the file is regular. char buffer[1000]; ssize_t size; if ( (size = read( file_descriptor, buffer, 1000 )) == -1 ) { int error_number = errno; if ( error_message ) { Smb4KError::error( ERROR_READING_FILE, m_lock_file, strerror( error_number ) ); } return ok; // false } if ( size >= 1000 ) { if ( error_message ) { // FIXME for >= 0.8.x: Change error code to ERROR_BUFFER_EXCEEDED Smb4KError::error( ERROR_UNKNOWN, TQString(), i18n( "Buffer size exceeded" ) ); } return ok; // false } TQStringList contents = TQStringList::split( '\n', TQString::fromLocal8Bit( buffer, size ), false ); // Prepare the contents of the file and write it to the disk. // It it should be empty, remove the lock file. for ( TQStringList::Iterator it = contents.begin(); it != contents.end(); it++ ) { if ( (*it).startsWith( TQString( getpwuid( getuid() )->pw_name )+":" ) ) { *it = TQString(); continue; } else { continue; } } contents.remove( TQString() ); if ( !contents.isEmpty() ) { // Write the remaining contents to the lock file: TQCString out = contents.join( "\n" ).local8Bit(); if ( write( file_descriptor, out, out.length() ) == -1 ) { int error_number = errno; if ( error_message ) { Smb4KError::error( ERROR_WRITING_FILE, m_lock_file, strerror( error_number ) ); } return ok; // false } if ( close( file_descriptor ) == -1 ) { int error_number = errno; if ( error_message ) { Smb4KError::error( ERROR_CLOSING_FILE, m_lock_file, strerror( error_number ) ); } return ok; // false } ok = true; } else { // Close and remove the lock file: if ( close( file_descriptor ) == -1 ) { int error_number = errno; if ( error_message ) { Smb4KError::error( ERROR_CLOSING_FILE, m_lock_file, strerror( error_number ) ); } return ok; // false } if ( unlink( m_lock_file ) == -1 ) { int error_number = errno; if ( error_message ) { // FIXME for > 0.8.x: Replace error code with ERROR_REMOVING_FILE Smb4KError::error( ERROR_UNKNOWN, m_lock_file, strerror( error_number ) ); } return ok; // false } ok = true; } } } return ok; } const TQCString Smb4KFileIO::findFile( const TQString &filename ) { TQStringList paths; paths << "/etc"; paths << "/etc/samba"; paths << "/usr/local/etc"; paths << "/usr/local/etc/samba"; TQString canonical_path = TQString(); for ( TQStringList::ConstIterator it = paths.begin(); it != paths.end(); it++ ) { TQDir::setCurrent( *it ); if ( TQFile::exists( filename ) ) { canonical_path = TQDir::current().canonicalPath()+"/"+filename; break; } else { continue; } } return canonical_path.local8Bit(); } void Smb4KFileIO::processSudoers() { // If the output buffer is empty, we stop, because // that most likely means, the user cancelled the // tdesu dialog. if ( m_buffer.stripWhiteSpace().isEmpty() ) { emit failed(); emit finished(); removeLockFile(); return; } TQStringList contents = TQStringList::split( "\n", m_buffer, true ); bool write = false; switch ( m_operation ) { case Insert: { size_t hostnamelen = 255; char *hn = new char[hostnamelen]; if ( gethostname( hn, hostnamelen ) == -1 ) { int error_number = errno; Smb4KError::error( ERROR_GETTING_HOSTNAME, TQString(), strerror( error_number ) ); emit failed(); emit finished(); removeLockFile(); } TQString hostname( hn ); delete [] hn; if ( contents.grep( "# Entries for Smb4K users." ).count() == 0 ) { contents.append( "# Entries for Smb4K users." ); contents.append( "# Generated by Smb4K. Please do not modify!" ); contents.append( "User_Alias\tSMB4KUSERS = "+TQString( "%1" ).arg( getpwuid( getuid() )->pw_name ) ); contents.append( "Defaults:SMB4KUSERS\tenv_keep += \"PASSWD USER\"" ); contents.append( "SMB4KUSERS\t"+hostname+" = NOPASSWD: "+Smb4KSettings::smb4k_kill() ); contents.append( "SMB4KUSERS\t"+hostname+" = NOPASSWD: "+Smb4KSettings::smb4k_umount() ); contents.append( "SMB4KUSERS\t"+hostname+" = NOPASSWD: "+Smb4KSettings::smb4k_mount() ); contents.append( "# End of Smb4K user entries." ); write = true; } else { // Find the beginning and the end of the entries in // the sudoers file: TQStringList::Iterator begin = contents.find( "# Entries for Smb4K users." ); TQStringList::Iterator end = contents.find( "# End of Smb4K user entries." ); for ( TQStringList::Iterator it = begin; it != end; ++it ) { if ( (*it).startsWith( "User_Alias\tSMB4KUSERS" ) && (*it).contains( getpwuid( getuid() )->pw_name, true ) == 0 ) { (*it).append( ","+TQString( getpwuid( getuid() )->pw_name ) ); write = true; break; } else { continue; } } } break; } case Remove: { // Find the beginning and the end of the entries in // the sudoers file: TQStringList::Iterator begin = contents.find( "# Entries for Smb4K users." ); TQStringList::Iterator end = contents.find( "# End of Smb4K user entries." ); // Now, check if the user is in the list of users. If he is, // remove him from there or remove all if he is the only one: for ( TQStringList::Iterator it = begin; it != end; ++it ) { if ( (*it).startsWith( "User_Alias\tSMB4KUSERS" ) ) { TQString users = (*it).section( "=", 1, 1 ).stripWhiteSpace(); if ( users.contains( "," ) == 0 ) { // In this case, there is only one user in the list. Check if // it is the user who requested the removal: if ( TQString::compare( users, getpwuid( getuid() )->pw_name ) == 0 ) { // They are equal. Remove everything: contents.erase( begin, end ); contents.remove( end ); write = true; break; } else { // They are not equal: Do nothing. break; } } else { // In this case there is more than one user in the list. // Remove the user who requested the removal: TQStringList list = TQStringList::split( ",", users, false ); list.remove( getpwuid( getuid() )->pw_name ); (*it).replace( users, list.join( "," ) ); write = true; break; } } else { continue; } } break; } default: { emit failed(); emit finished(); removeLockFile(); return; } } if ( write ) { // Prepare the contents: remove empty lines from the end. TQStringList::Iterator it = contents.end(); // Move the iterator to the last entry in the list: --it; while ( (*it).stripWhiteSpace().isEmpty() ) { it = contents.remove( it ); --it; } // Create a temporary file and write the data to it: TQCString template_string = tempDir().local8Bit()+"/XXXXXX"; char tmp[template_string.length()+1]; (void) tqstrncpy( tmp, template_string, template_string.length()+1 ); TQFile temp_file; int file_descriptor; if ( (file_descriptor = mkstemp( tmp )) == -1 ) { int err = errno; Smb4KError::error( ERROR_CREATING_TEMP_FILE, tmp, strerror( err ) ); emit failed(); emit finished(); removeLockFile(); return; } if ( temp_file.open( IO_WriteOnly, file_descriptor ) ) { TQTextStream ts( &temp_file ); ts.setEncoding( TQTextStream::Locale ); ts << contents.join( "\n" ) << endl; temp_file.close(); } else { Smb4KError::error( ERROR_WRITING_FILE, temp_file.name() ); emit failed(); emit finished(); removeLockFile(); return; } // Now move the file to the right location. Preserve the permissions // and the owner: TQString canonical_path = findFile( "sudoers" ); struct stat file_stat; if ( stat( canonical_path.local8Bit(), &file_stat ) == -1 ) { int error_number = errno; Smb4KError::error( ERROR_GETTING_PERMISSIONS, TQString(), strerror( error_number ) ); emit failed(); emit finished(); removeLockFile(); return; } TQString perms = TQString( "%1" ).arg( (int)file_stat.st_mode, 0, 8 ); perms = perms.right( 4 ); TQString owner = TQString( "%1" ).arg( (int)file_stat.st_uid ); TQString group = TQString( "%1" ).arg( (int)file_stat.st_gid ); TQString temp_file_name = TQString( tmp ); // Assemble the command. TQString command; command.append( "tdesu -n -c \"smb4k_mv "+owner+":"+group+" "+perms+" " ); command.append( temp_file_name+" " ); command.append( canonical_path+"\" ; " ); command.append( "rm -f "+temp_file_name ); m_state = WriteSudoers; *m_proc << command; m_proc->start( TDEProcess::NotifyOnExit, TDEProcess::AllOutput ); } else { // Everything OK. emit finished(); removeLockFile(); } } void Smb4KFileIO::processSuperTab() { // If the output buffer is empty, we stop, because // that most likely means, the user cancelled the // tdesu dialog. if ( m_buffer.stripWhiteSpace().isEmpty() ) { emit failed(); emit finished(); removeLockFile(); return; } TQStringList contents = TQStringList::split( "\n", m_buffer, true ); bool write = false; switch ( m_operation ) { case Insert: { size_t hostnamelen = 255; char *hn = new char[hostnamelen]; if ( gethostname( hn, hostnamelen ) == -1 ) { int error_number = errno; Smb4KError::error( ERROR_GETTING_HOSTNAME, TQString(), strerror( error_number ) ); emit failed(); emit finished(); removeLockFile(); } TQString hostname( hn ); delete [] hn; if ( contents.grep( "# Entries for Smb4K users." ).count() == 0 ) { contents.append( "# Entries for Smb4K users." ); contents.append( "# Generated by Smb4K. Please do not modify!" ); contents.append( ":define Smb4KUsers "+TQString( "%1" ).arg( getpwuid( getuid() )->pw_name ) ); #ifndef __FreeBSD__ contents.append( "smb4k_kill\t"+Smb4KSettings::smb4k_kill()+ "\t$(Smb4KUsers)\tuid=root\tgid=root" ); contents.append( "smb4k_umount\t"+Smb4KSettings::smb4k_umount()+ "\t$(Smb4KUsers)\tuid=root\tgid=root" ); contents.append( "smb4k_mount\t"+Smb4KSettings::smb4k_mount()+ "\t$(Smb4KUsers)\tuid=root\tgid=root\tenv=PASSWD,USER" ); #else contents.append( "smb4k_kill\t"+Smb4KSettings::smb4k_kill()+ "\t$(Smb4KUsers)\tuid=root\tgid=wheel" ); contents.append( "smb4k_umount\t"+Smb4KSettings::smb4k_umount()+ "\t$(Smb4KUsers)\tuid=root\tgid=wheel" ); contents.append( "smb4k_mount\t"+Smb4KSettings::smb4k_mount()+ "\t$(Smb4KUsers)\tuid=root\tgid=wheel\tsetenv=HOME=$CALLER_HOME\tenv=PASSWD,USER" ); #endif contents.append( "# End of Smb4K user entries." ); write = true; } else { // Find the beginning and the end of the entries in // the super.tab file: TQStringList::Iterator begin = contents.find( "# Entries for Smb4K users." ); TQStringList::Iterator end = contents.find( "# End of Smb4K user entries." ); for ( TQStringList::Iterator it = begin; it != end; ++it ) { if ( (*it).startsWith( ":define Smb4KUsers" ) && (*it).contains( getpwuid( getuid() )->pw_name, true ) == 0 ) { (*it).append( ","+TQString( getpwuid( getuid() )->pw_name ) ); write = true; break; } else { continue; } } } break; } case Remove: { // Find the beginning and the end of the entries in // the super.tab file: TQStringList::Iterator begin = contents.find( "# Entries for Smb4K users." ); TQStringList::Iterator end = contents.find( "# End of Smb4K user entries." ); // Now, check if the user is in the list of users. If he is, // remove him from there or remove all if he is the only one: for ( TQStringList::Iterator it = begin; it != end; ++it ) { if ( (*it).startsWith( ":define Smb4KUsers" ) ) { TQString users = (*it).section( "Smb4KUsers", 1, 1 ).stripWhiteSpace(); if ( users.contains( "," ) == 0 ) { // In this case, there is only one user in the list. Check if // it is the user who requested the removal: if ( TQString::compare( users, getpwuid( getuid() )->pw_name ) == 0 ) { // They are equal. Remove everything: contents.erase( begin, end ); contents.remove( end ); write = true; break; } else { // They are not equal: Do nothing. break; } } else { // In this case there is more than one user in the list. // Remove the user who requested the removal: TQStringList list = TQStringList::split( ",", users, false ); list.remove( getpwuid( getuid() )->pw_name ); (*it).replace( users, list.join( "," ) ); write = true; break; } } else { continue; } } break; } default: { emit failed(); emit finished(); removeLockFile(); return; } } if ( write ) { // Prepare the contents: remove empty lines from the end. TQStringList::Iterator it = contents.end(); // Move the iterator to the last entry in the list: --it; while ( (*it).stripWhiteSpace().isEmpty() ) { it = contents.remove( it ); --it; } // Create a temporary file and write the data to it: TQCString template_string = tempDir().local8Bit()+"/XXXXXX"; char tmp[template_string.length()+1]; (void) tqstrncpy( tmp, template_string, template_string.length()+1 ); TQFile temp_file; int file_descriptor; if ( (file_descriptor = mkstemp( tmp )) == -1 ) { int err = errno; Smb4KError::error( ERROR_CREATING_TEMP_FILE, tmp, strerror( err ) ); emit failed(); emit finished(); removeLockFile(); return; } if ( temp_file.open( IO_WriteOnly, file_descriptor ) ) { TQTextStream ts( &temp_file ); ts.setEncoding( TQTextStream::Locale ); ts << contents.join( "\n" ) << endl; temp_file.close(); } else { Smb4KError::error( ERROR_WRITING_FILE, temp_file.name() ); emit failed(); emit finished(); removeLockFile(); return; } // Now move the file to the right location. Preserve the permissions // and the owner: TQString canonical_path = findFile( "super.tab" ); struct stat file_stat; if ( stat( canonical_path.local8Bit(), &file_stat ) == -1 ) { int error_number = errno; Smb4KError::error( ERROR_GETTING_PERMISSIONS, TQString(), strerror( error_number ) ); emit failed(); emit finished(); removeLockFile(); return; } TQString perms = TQString( "%1" ).arg( (int)file_stat.st_mode, 0, 8 ); perms = perms.right( 4 ); TQString owner = TQString( "%1" ).arg( (int)file_stat.st_uid ); TQString group = TQString( "%1" ).arg( (int)file_stat.st_gid ); TQString temp_file_name = TQString( tmp ); // Assemble the command. TQString command; command.append( "tdesu -n -c \"smb4k_mv "+owner+":"+group+" "+perms+" " ); command.append( temp_file_name+" " ); command.append( canonical_path+"\" ; " ); command.append( "rm -f "+temp_file_name ); m_state = WriteSuperTab; *m_proc << command; m_proc->start( TDEProcess::NotifyOnExit, TDEProcess::AllOutput ); } else { // Everything OK. emit finished(); removeLockFile(); } } ///////////////////////////////////////////////////////////////////////////// // SLOT IMPLEMENTATIONS ///////////////////////////////////////////////////////////////////////////// void Smb4KFileIO::slotShutdown() { removeLockFile( false ); } void Smb4KFileIO::slotReceivedStderr( TDEProcess *, char *buf, int len ) { TQString error_output = TQString::fromLocal8Bit( buf, len ); if ( error_output.contains( "smb4k_mv" ) != 0 ) { m_error_occurred = true; TQString canonical_path = findFile( (m_state == WriteSudoers ? "sudoers" : "super.tab") ); Smb4KError::error( ERROR_WRITING_FILE, canonical_path, m_buffer ); emit failed(); emit finished(); removeLockFile(); } else if ( error_output.contains( "smb4k_cat" ) != 0 ) { m_error_occurred = true; TQString canonical_path = findFile( (m_state == ReadSudoers ? "sudoers" : "super.tab") ); Smb4KError::error( ERROR_READING_FILE, canonical_path, m_buffer ); emit failed(); emit finished(); removeLockFile(); } } void Smb4KFileIO::slotReceivedStdout( TDEProcess *, char *buf, int len ) { m_buffer.append( TQString::fromLocal8Bit( buf, len ) ); } void Smb4KFileIO::slotProcessExited( TDEProcess * ) { m_proc->clearArguments(); if ( !m_error_occurred ) { switch ( m_state ) { case ReadSudoers: { processSudoers(); break; } case WriteSudoers: { emit finished(); removeLockFile(); break; } case ReadSuperTab: { processSuperTab(); break; } default: { emit finished(); removeLockFile(); break; } } } else { // Smb4KFileIO::slotReceivedStderr() has already done the // necessary things. } m_buffer = TQString(); m_state = Idle; m_error_occurred = false; } #include "smb4tdefileio.moc"