You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
smb4k/smb4k/core/smb4kfileio.cpp

1796 lines
48 KiB

/***************************************************************************
smb4kfileio - 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 "smb4kfileio.h"
#include "smb4kdefs.h"
#include "smb4kerror.h"
#include "smb4kglobal.h"
#include "smb4ksettings.h"
using namespace Smb4KGlobal;
Smb4KFileIO::Smb4KFileIO( TQObject *tqparent, const char *name ) : TQObject( tqparent, name )
{
m_operation = NoOperation;
m_state = Idle;
m_error_occurred = false;
m_proc = new KProcess( this, "FileIOProcess" );
m_proc->setUseShell( true );
connect( m_proc, TQT_SIGNAL( receivedStderr( KProcess *, char *, int ) ),
this, TQT_SLOT( slotReceivedStderr( KProcess *, char *, int ) ) );
connect( m_proc, TQT_SIGNAL( receivedStdout( KProcess *, char *, int ) ),
this, TQT_SLOT( slotReceivedStdout( KProcess *, char *, int ) ) );
connect( m_proc, TQT_SIGNAL( processExited( KProcess * ) ),
this, TQT_SLOT( slotProcessExited( KProcess * ) ) );
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" ).tqarg( 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
// kdesu to get the contents of the file.
// Compose the command:
TQString command;
command.append( "kdesu -t -c \"smb4k_cat " );
command.append( canonical_path+"\"" );
command.append( " ; sleep 2" );
m_state = ReadSudoers;
ok = true;
*m_proc << command;
m_proc->start( KProcess::NotifyOnExit, KProcess::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" ).tqarg( 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
// kdesu to get the contents of the file.
// Compose the command:
TQString command;
command.append( "kdesu -t -c \"smb4k_cat " );
command.append( canonical_path+"\"" );
command.append( " ; sleep 2" );
m_state = ReadSuperTab;
ok = true;
*m_proc << command;
m_proc->start( KProcess::NotifyOnExit, KProcess::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" ).tqarg( getpwuid( getuid() )->pw_name ).tqarg( 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
// kdesu 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" ).tqarg( 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) qstrncpy( 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" ).tqarg( (int)file_stat.st_mode, 0, 8 );
perms = perms.right( 4 );
TQString owner = TQString( "%1" ).tqarg( (int)file_stat.st_uid );
TQString group = TQString( "%1" ).tqarg( (int)file_stat.st_gid );
TQString temp_file_name = TQString( tmp );
// Assemble the command.
TQString command;
command.append( "kdesu -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( KProcess::NotifyOnExit, KProcess::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
// kdesu 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" ).tqarg( 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) qstrncpy( 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" ).tqarg( (int)file_stat.st_mode, 0, 8 );
perms = perms.right( 4 );
TQString owner = TQString( "%1" ).tqarg( (int)file_stat.st_uid );
TQString group = TQString( "%1" ).tqarg( (int)file_stat.st_gid );
TQString temp_file_name = TQString( tmp );
// Assemble the command.
TQString command;
command.append( "kdesu -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( KProcess::NotifyOnExit, KProcess::AllOutput );
}
else
{
// Everything OK.
emit finished();
removeLockFile();
}
}
/////////////////////////////////////////////////////////////////////////////
// TQT_SLOT IMPLEMENTATIONS
/////////////////////////////////////////////////////////////////////////////
void Smb4KFileIO::slotShutdown()
{
removeLockFile( false );
}
void Smb4KFileIO::slotReceivedStderr( KProcess *, 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( KProcess *, char *buf, int len )
{
m_buffer.append( TQString::fromLocal8Bit( buf, len ) );
}
void Smb4KFileIO::slotProcessExited( KProcess * )
{
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 "smb4kfileio.moc"