diff options
Diffstat (limited to 'smb4k/core/smb4tdefileio.cpp')
-rw-r--r-- | smb4k/core/smb4tdefileio.cpp | 1795 |
1 files changed, 1795 insertions, 0 deletions
diff --git a/smb4k/core/smb4tdefileio.cpp b/smb4k/core/smb4tdefileio.cpp new file mode 100644 index 0000000..617763c --- /dev/null +++ b/smb4k/core/smb4tdefileio.cpp @@ -0,0 +1,1795 @@ +/*************************************************************************** + 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 <tqdir.h> +#include <tqfile.h> +#include <tqtextstream.h> + +// KDE includes +#include <kapplication.h> +#include <kdebug.h> +#include <klocale.h> + +// system specific includes +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <grp.h> +#include <pwd.h> + +// application specific includes +#include "smb4tdefileio.h" +#include "smb4kdefs.h" +#include "smb4kerror.h" +#include "smb4kglobal.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, TQT_SIGNAL( receivedStderr( TDEProcess *, char *, int ) ), + this, TQT_SLOT( slotReceivedStderr( TDEProcess *, char *, int ) ) ); + + connect( m_proc, TQT_SIGNAL( receivedStdout( TDEProcess *, char *, int ) ), + this, TQT_SLOT( slotReceivedStdout( TDEProcess *, char *, int ) ) ); + + connect( m_proc, TQT_SIGNAL( processExited( TDEProcess * ) ), + this, TQT_SLOT( slotProcessExited( TDEProcess * ) ) ); + + connect( kapp, TQT_SIGNAL( shutDown() ), + this, TQT_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<TQCString> dirs; + dirs << "/var/lock" << "/var/tmp" << "/tmp"; + + struct stat buf; + + for ( TQValueList<TQCString>::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(); + } +} + +///////////////////////////////////////////////////////////////////////////// +// TQT_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" |