[GRASS] fixed test if current user is not owner of source

This commit is contained in:
Radim Blazek 2015-09-21 11:54:44 +02:00
parent 992bacc921
commit fae065494a
3 changed files with 193 additions and 46 deletions

View File

@ -52,8 +52,9 @@
extern "C"
{
#ifndef _MSC_VER
#ifndef Q_OS_WIN
#include <unistd.h>
#include <sys/types.h>
#endif
#include <grass/version.h>
#include <grass/gprojects.h>
@ -707,6 +708,8 @@ QString QgsGrass::mTmp;
QMutex QgsGrass::sMutex;
bool QgsGrass::mMute = false;
int QgsGrass::error_routine( char *msg, int fatal )
{
return error_routine(( const char* ) msg, fatal );
@ -760,6 +763,22 @@ QString QgsGrass::errorMessage( void )
return error_message;
}
bool QgsGrass::isOwner( const QString& gisdbase, const QString& location, const QString& mapset )
{
QString mapsetPath = gisdbase + "/" + location + "/" + mapset;
// G_mapset_permissions() (check_owner() in GRASS 7) on Windows consider all mapsets to be owned by user
// There is more complex G_owner() but that is not used in G_gisinit() (7.1.svn).
// On Windows and on systems where files do not have owners ownerId() returns ((uint) -2).
#ifndef Q_OS_WIN
bool owner = QFileInfo( mapsetPath ).ownerId() == getuid();
#else
bool owner = true;
#endif
QgsDebugMsg( QString( "%1 : owner = %2" ).arg( mapsetPath ).arg( owner ) );
return owner;
}
QString QgsGrass::openMapset( const QString& gisdbase,
const QString& location, const QString& mapset )
{
@ -1726,8 +1745,7 @@ bool QgsGrass::mapRegion( QgsGrassObject::Type type, QString gisdbase,
map.toUtf8().data(),
mapset.toUtf8().data() ) != NULL )
{
QMessageBox::warning( 0, QObject::tr( "Warning" ),
QObject::tr( "Cannot read region" ) );
warning( tr( "Cannot read region" ) );
return false;
}
#else
@ -1768,15 +1786,34 @@ QProcess *QgsGrass::startModule( const QString& gisdbase, const QString& locati
throw QgsGrass::Exception( QObject::tr( "Cannot open GISRC file" ) );
}
QString error = tr( "Cannot start module" ) + "\n" + tr( "command: %1 %2" ).arg( module ).arg( arguments.join( " " ) );
// Modules must be run in a mapset owned by user, because each module calls G_gisinit()
// which checks if G_mapset() is owned by user.
QString ownedMapset = mapset;
if ( ownedMapset.isEmpty() )
{
Q_FOREACH ( QString ms, mapsets( gisdbase, location ) )
{
if ( isOwner( gisdbase, location, ms ) )
{
ownedMapset = ms;
}
}
}
if ( ownedMapset.isEmpty() )
{
throw QgsGrass::Exception( error + "\n" + tr( "Cannot find a mapset owned by current user" ) );
}
QTextStream out( &gisrcFile );
out << "GISDBASE: " << gisdbase << "\n";
out << "LOCATION_NAME: " << location << "\n";
//out << "MAPSET: PERMANENT\n";
out << "MAPSET: " << mapset << "\n";
out << "MAPSET: " << ownedMapset << "\n";
out.flush();
QgsDebugMsg( gisrcFile.fileName() );
gisrcFile.close();
QStringList environment = QProcess::systemEnvironment();
environment.append( "GISRC=" + gisrcFile.fileName() );
@ -1787,9 +1824,7 @@ QProcess *QgsGrass::startModule( const QString& gisdbase, const QString& locati
process->start( module, arguments );
if ( !process->waitForStarted() )
{
throw QgsGrass::Exception( QObject::tr( "Cannot start module" ) + "\n"
+ QObject::tr( "command: %1 %2" )
.arg( module ).arg( arguments.join( " " ) ) );
throw QgsGrass::Exception( error );
}
return process;
}
@ -1801,7 +1836,7 @@ QByteArray QgsGrass::runModule( const QString& gisdbase, const QString& locatio
QgsDebugMsg( QString( "gisdbase = %1 location = %2 timeOut = %3" ).arg( gisdbase ).arg( location ).arg( timeOut ) );
QTemporaryFile gisrcFile;
QProcess *process = QgsGrass::startModule( gisdbase, location, mapset, moduleName, arguments, gisrcFile, qgisModule );
QProcess *process = startModule( gisdbase, location, mapset, moduleName, arguments, gisrcFile, qgisModule );
if ( !process->waitForFinished( timeOut )
|| ( process->exitCode() != 0 && process->exitCode() != 255 ) )
@ -1864,7 +1899,9 @@ QString QgsGrass::getInfo( const QString& info, const QString& gisdbase,
arguments.append( QString( "cols=%1" ).arg( sampleCols ) );
}
QByteArray data = QgsGrass::runModule( gisdbase, location, mapset, cmd, arguments, timeOut );
//QByteArray data = runModule( gisdbase, location, mapset, cmd, arguments, timeOut );
// Run module with empty mapset so that it tries to find a mapset owned by user
QByteArray data = runModule( gisdbase, location, "", cmd, arguments, timeOut );
QgsDebugMsg( data );
return QString( data );
}
@ -1876,7 +1913,7 @@ QgsCoordinateReferenceSystem QgsGrass::crs( const QString& gisdbase, const QStri
QgsCoordinateReferenceSystem crs = QgsCoordinateReferenceSystem();
try
{
QString wkt = QgsGrass::getInfo( "proj", gisdbase, location );
QString wkt = getInfo( "proj", gisdbase, location );
QgsDebugMsg( "wkt: " + wkt );
crs.createFromWkt( wkt );
QgsDebugMsg( "crs.toWkt: " + crs.toWkt() );
@ -1885,8 +1922,7 @@ QgsCoordinateReferenceSystem QgsGrass::crs( const QString& gisdbase, const QStri
{
if ( interactive )
{
QMessageBox::warning( 0, QObject::tr( "Warning" ),
QObject::tr( "Cannot get projection " ) + "\n" + e.what() );
warning( tr( "Cannot get projection " ) + "\n" + e.what() );
}
}
@ -1940,7 +1976,7 @@ QgsRectangle QgsGrass::extent( const QString& gisdbase, const QString& location,
try
{
QString str = QgsGrass::getInfo( "window", gisdbase, location, mapset, map, type );
QString str = getInfo( "window", gisdbase, location, mapset, map, type );
QStringList list = str.split( "," );
if ( list.size() != 4 )
{
@ -1952,8 +1988,7 @@ QgsRectangle QgsGrass::extent( const QString& gisdbase, const QString& location,
{
if ( interactive )
{
QMessageBox::warning( 0, QObject::tr( "Warning" ),
QObject::tr( "Cannot get raster extent" ) + "\n" + e.what() );
warning( tr( "Cannot get raster extent" ) + "\n" + e.what() );
}
}
return QgsRectangle( 0, 0, 0, 0 );
@ -1968,7 +2003,7 @@ void QgsGrass::size( const QString& gisdbase, const QString& location,
*rows = 0;
try
{
QString str = QgsGrass::getInfo( "size", gisdbase, location, mapset, map, QgsGrassObject::Raster );
QString str = getInfo( "size", gisdbase, location, mapset, map, QgsGrassObject::Raster );
QStringList list = str.split( "," );
if ( list.size() != 2 )
{
@ -1979,8 +2014,7 @@ void QgsGrass::size( const QString& gisdbase, const QString& location,
}
catch ( QgsGrass::Exception &e )
{
QMessageBox::warning( 0, QObject::tr( "Warning" ),
QObject::tr( "Cannot get raster extent" ) + "\n" + e.what() );
warning( tr( "Cannot get raster extent" ) + "\n" + e.what() );
}
QgsDebugMsg( QString( "raster size = %1 %2" ).arg( *cols ).arg( *rows ) );
@ -1999,7 +2033,7 @@ QHash<QString, QString> QgsGrass::info( const QString& gisdbase, const QString&
try
{
QString str = QgsGrass::getInfo( info, gisdbase, location, mapset, map, type, 0, 0, extent, sampleRows, sampleCols, timeOut );
QString str = getInfo( info, gisdbase, location, mapset, map, type, 0, 0, extent, sampleRows, sampleCols, timeOut );
QgsDebugMsg( str );
QStringList list = str.split( "\n" );
for ( int i = 0; i < list.size(); i++ )
@ -2018,8 +2052,7 @@ QHash<QString, QString> QgsGrass::info( const QString& gisdbase, const QString&
{
if ( interactive )
{
QMessageBox::warning( 0, QObject::tr( "Warning" ),
QObject::tr( "Cannot get map info" ) + "\n" + e.what() );
warning( tr( "Cannot get map info" ) + "\n" + e.what() );
}
}
return inf;
@ -2032,7 +2065,7 @@ QList<QgsGrass::Color> QgsGrass::colors( QString gisdbase, QString location, QSt
try
{
QString str = QgsGrass::getInfo( "colors", gisdbase, location, mapset, map, QgsGrassObject::Raster );
QString str = getInfo( "colors", gisdbase, location, mapset, map, QgsGrassObject::Raster );
QgsDebugMsg( str );
QStringList list = str.split( "\n" );
for ( int i = 0; i < list.size(); i++ )
@ -2049,8 +2082,7 @@ QList<QgsGrass::Color> QgsGrass::colors( QString gisdbase, QString location, QSt
}
catch ( QgsGrass::Exception &e )
{
QMessageBox::warning( 0, QObject::tr( "Warning" ),
QObject::tr( "Cannot get colors" ) + "\n" + e.what() );
warning( tr( "Cannot get colors" ) + "\n" + e.what() );
}
return ct;
}
@ -2063,7 +2095,7 @@ QMap<QString, QString> QgsGrass::query( QString gisdbase, QString location, QStr
// TODO: multiple values (more rows)
try
{
QString str = QgsGrass::getInfo( "query", gisdbase, location, mapset, map, type, x, y );
QString str = getInfo( "query", gisdbase, location, mapset, map, type, x, y );
QStringList list = str.trimmed().split( ":" );
if ( list.size() == 2 )
{
@ -2072,8 +2104,7 @@ QMap<QString, QString> QgsGrass::query( QString gisdbase, QString location, QStr
}
catch ( QgsGrass::Exception &e )
{
QMessageBox::warning( 0, QObject::tr( "Warning" ),
QObject::tr( "Cannot query raster " ) + "\n" + e.what() );
warning( tr( "Cannot query raster " ) + "\n" + e.what() );
}
return result;
}
@ -2088,7 +2119,7 @@ void QgsGrass::renameObject( const QgsGrassObject & object, const QString& newNa
int timeout = -1; // What timeout to use? It can take long time on network or database
// throws QgsGrass::Exception
QgsGrass::runModule( object.gisdbase(), object.location(), object.mapset(), cmd, arguments, timeout, false );
runModule( object.gisdbase(), object.location(), object.mapset(), cmd, arguments, timeout, false );
}
void QgsGrass::copyObject( const QgsGrassObject & srcObject, const QgsGrassObject & destObject )
@ -2109,7 +2140,7 @@ void QgsGrass::copyObject( const QgsGrassObject & srcObject, const QgsGrassObjec
int timeout = -1; // What timeout to use? It can take long time on network or database
// throws QgsGrass::Exception
// TODO: g.copy does not seem to return error code if fails (6.4.3RC1)
QgsGrass::runModule( destObject.gisdbase(), destObject.location(), destObject.mapset(), cmd, arguments, timeout, false );
runModule( destObject.gisdbase(), destObject.location(), destObject.mapset(), cmd, arguments, timeout, false );
}
bool QgsGrass::deleteObject( const QgsGrassObject & object )
@ -2138,13 +2169,11 @@ bool QgsGrass::deleteObject( const QgsGrassObject & object )
try
{
QgsGrass::runModule( object.gisdbase(), object.location(), object.mapset(), cmd, arguments, 5000, false );
runModule( object.gisdbase(), object.location(), object.mapset(), cmd, arguments, 5000, false );
}
catch ( QgsGrass::Exception &e )
{
QMessageBox::warning( 0, QObject::tr( "Warning" ),
QObject::tr( "Cannot delete" ) + " " + object.elementName()
+ " " + object.name() + ": " + e.what() );
warning( tr( "Cannot delete" ) + " " + object.elementName() + " " + object.name() + ": " + e.what() );
return false;
}
return true;
@ -2523,12 +2552,20 @@ void QgsGrass::setModulesDebug( bool debug )
void QgsGrass::warning( const QString &message )
{
QMessageBox::warning( 0, QObject::tr( "Warning" ), message );
if ( !mMute )
{
QMessageBox::warning( 0, QObject::tr( "Warning" ), message );
}
else
{
error_message = message;
QgsDebugMsg( message );
}
}
void QgsGrass::warning( QgsGrass::Exception &e )
{
QMessageBox::warning( 0, QObject::tr( "Warning" ), e.what() );
warning( e.what() );
}
struct Map_info *QgsGrass::vectNewMapStruct()

View File

@ -222,6 +222,9 @@ class GRASS_LIB_EXPORT QgsGrass : public QObject
//! Get last error message
static QString errorMessage( void );
/** Test is current user is owner of mapset */
static bool isOwner( const QString& gisdbase, const QString& location, const QString& mapset );
/** Open existing GRASS mapset.
* Emits signal mapsetChanged().
* \return Empty string or error message
@ -355,8 +358,11 @@ class GRASS_LIB_EXPORT QgsGrass : public QObject
// ! Get current gisrc path
static QString gisrcFilePath();
// ! Start a GRASS module in any gisdbase/location/mapset
// @param qgisModule append GRASS major version (for modules built in qgis)
/** Start a GRASS module in any gisdbase/location/mapset.
* @param mapset if empty a first mapset owned by user will be used, if no mapset is owned
* by user, exception is thrown.
* @param qgisModule append GRASS major version (for modules built in qgis)
* @throws QgsGrass::Exception */
static QProcess *startModule( const QString& gisdbase, const QString& location,
const QString& mapset, const QString& moduleName,
const QStringList& arguments, QTemporaryFile &gisrcFile,
@ -528,6 +534,10 @@ class GRASS_LIB_EXPORT QgsGrass : public QObject
/** Show warning dialog with exception message */
static void warning( QgsGrass::Exception &e );
/** Set mute mode, if set, warning() does not open dialog but prints only
* debug message and sets the error which returns errorMessage() */
static void setMute() { mMute = true; }
/** Allocate struct Map_info. Call to this function may result in G_fatal_error
* and must be surrounded by G_TRY/G_CATCH. */
static struct Map_info * vectNewMapStruct();
@ -588,6 +598,8 @@ class GRASS_LIB_EXPORT QgsGrass : public QObject
static QString mTmp;
// Mutex for common locking when calling GRASS functions which are mostly non thread safe
static QMutex sMutex;
// Mute mode, do not show warning dialogs.
static bool mMute;
};
#endif // QGSGRASS_H

View File

@ -74,6 +74,8 @@ class TestQgsGrassProvider: public QObject
bool compare( QStringList expected, QStringList got, bool& ok );
// compare with tolerance
bool compare( double expected, double got, bool& ok );
bool copyRecursively( const QString &srcFilePath, const QString &tgtFilePath, QString *error );
bool copyLocation( QString& tmpGisdbase );
bool createTmpLocation( QString& tmpGisdbase, QString& tmpLocation, QString& tmpMapset );
QString mGisdbase;
QString mLocation;
@ -115,6 +117,7 @@ void TestQgsGrassProvider::initTestCase()
reportRow( "PATH: " + QString( getenv( "PATH" ) ) );
#endif
QgsGrass::setMute();
QgsGrass::init();
//create some objects that will be used in all tests...
@ -224,13 +227,24 @@ void TestQgsGrassProvider::mapsets()
{
reportHeader( "TestQgsGrassProvider::mapsets" );
bool ok = true;
// User must be owner of mapset if it has to be opened (locked)
// -> make copy because the source may have different user
QString tmpGisdbase;
if ( !copyLocation( tmpGisdbase ) )
{
reportRow( "cannot copy location" );
GVERIFY( false );
return;
}
QStringList expectedMapsets;
expectedMapsets << "PERMANENT" << "test" << "test6" << "test7";
QStringList mapsets = QgsGrass::mapsets( mGisdbase, mLocation );
QStringList mapsets = QgsGrass::mapsets( tmpGisdbase, mLocation );
reportRow( "expectedMapsets: " + expectedMapsets.join( ", " ) );
reportRow( "mapsets: " + mapsets.join( ", " ) );
compare( expectedMapsets, mapsets, ok );
QgsGrass::setLocation( mGisdbase, mLocation ); // for G_is_mapset_in_search_path
QgsGrass::setLocation( tmpGisdbase, mLocation ); // for G_is_mapset_in_search_path
// Disabled because adding of all mapsets to search path was disabled in setLocation()
#if 0
foreach ( QString expectedMapset, expectedMapsets )
@ -248,7 +262,7 @@ void TestQgsGrassProvider::mapsets()
{
reportRow( "" );
reportRow( "Open/close mapset " + mBuildMapset + " for the " + QString::number( i ) + ". time" );
QString error = QgsGrass::openMapset( mGisdbase, mLocation, mBuildMapset );
QString error = QgsGrass::openMapset( tmpGisdbase, mLocation, mBuildMapset );
if ( !error.isEmpty() )
{
reportRow( "QgsGrass::openMapset() failed: " + error );
@ -375,6 +389,16 @@ void TestQgsGrassProvider::info()
// info() -> getInfo() -> runModule() -> startModule()
reportHeader( "TestQgsGrassProvider::info" );
bool ok = true;
// GRASS modules must be run in a mapset owned by user, the source code may have different user.
QString tmpGisdbase;
if ( !copyLocation( tmpGisdbase ) )
{
reportRow( "cannot copy location" );
GVERIFY( false );
return;
}
QgsRectangle expectedExtent( -5, -5, 5, 5 );
QMap<QString, QgsRasterBandStats> expectedStats;
QgsRasterBandStats es;
@ -391,7 +415,7 @@ void TestQgsGrassProvider::info()
{
es = expectedStats.value( map );
// TODO: QgsGrass::info() may open dialog window on error which blocks tests
QHash<QString, QString> info = QgsGrass::info( mGisdbase, mLocation, "test", map, QgsGrassObject::Raster, "stats",
QHash<QString, QString> info = QgsGrass::info( tmpGisdbase, mLocation, "test", map, QgsGrassObject::Raster, "stats",
expectedExtent, 10, 10, 5000, false );
reportRow( "map: " + map );
QgsRasterBandStats s;
@ -403,7 +427,7 @@ void TestQgsGrassProvider::info()
compare( es.minimumValue, s.minimumValue, ok );
compare( es.maximumValue, s.maximumValue, ok );
QgsRectangle extent = QgsGrass::extent( mGisdbase, mLocation, "test", map, QgsGrassObject::Raster, false );
QgsRectangle extent = QgsGrass::extent( tmpGisdbase, mLocation, "test", map, QgsGrassObject::Raster, false );
reportRow( "expectedExtent: " + expectedExtent.toString() );
reportRow( "extent: " + extent.toString() );
if ( extent != expectedExtent )
@ -415,17 +439,91 @@ void TestQgsGrassProvider::info()
reportRow( "" );
QgsCoordinateReferenceSystem expectedCrs;
expectedCrs.createFromOgcWmsCrs( "EPSG:4326" );
QgsCoordinateReferenceSystem crs = QgsGrass::crs( mGisdbase, mLocation );
reportRow( "expectedCrs: " + expectedCrs.toWkt() );
reportRow( "crs: " + crs.toWkt() );
if ( crs != expectedCrs )
QgsCoordinateReferenceSystem crs = QgsGrass::crs( tmpGisdbase, mLocation );
if ( !crs.isValid() )
{
reportRow( "crs: cannot read crs: " + QgsGrass::errorMessage() );
ok = false;
}
else
{
reportRow( "crs: " + crs.toWkt() );
if ( crs != expectedCrs )
{
ok = false;
}
}
GVERIFY( ok );
}
// From Qt creator
bool TestQgsGrassProvider::copyRecursively( const QString &srcFilePath, const QString &tgtFilePath, QString *error )
{
QFileInfo srcFileInfo( srcFilePath );
if ( srcFileInfo.isDir() )
{
QDir targetDir( tgtFilePath );
targetDir.cdUp();
if ( !targetDir.mkdir( QFileInfo( tgtFilePath ).fileName() ) )
{
if ( error )
{
*error = QCoreApplication::translate( "Utils::FileUtils", "Failed to create directory '%1'." )
.arg( QDir::toNativeSeparators( tgtFilePath ) );
return false;
}
}
QDir sourceDir( srcFilePath );
QStringList fileNames = sourceDir.entryList( QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System );
foreach ( const QString &fileName, fileNames )
{
const QString newSrcFilePath
= srcFilePath + QLatin1Char( '/' ) + fileName;
const QString newTgtFilePath
= tgtFilePath + QLatin1Char( '/' ) + fileName;
if ( !copyRecursively( newSrcFilePath, newTgtFilePath, error ) )
return false;
}
}
else
{
if ( !QFile::copy( srcFilePath, tgtFilePath ) )
{
if ( error )
{
*error = QCoreApplication::translate( "Utils::FileUtils", "Could not copy file '%1' to '%2'." )
.arg( QDir::toNativeSeparators( srcFilePath ),
QDir::toNativeSeparators( tgtFilePath ) );
}
return false;
}
}
return true;
}
// copy test location to temporary
bool TestQgsGrassProvider::copyLocation( QString& tmpGisdbase )
{
// use QTemporaryFile to generate name (QTemporaryDir since 5.0)
QTemporaryFile* tmpFile = new QTemporaryFile( QDir::tempPath() + "/qgis-grass-test" );
tmpFile->open();
tmpGisdbase = tmpFile->fileName();
delete tmpFile;
reportRow( "tmpGisdbase: " + tmpGisdbase );
QString error;
if ( !copyRecursively( mGisdbase, tmpGisdbase, &error ) )
{
reportRow( "cannot copy location " + mGisdbase + " to " + tmpGisdbase + " : " + error );
return false;
}
return true;
}
// create temporary output location
bool TestQgsGrassProvider::createTmpLocation( QString& tmpGisdbase, QString& tmpLocation, QString& tmpMapset )
{