office-gobmx/extensions/source/plugin/aqua/macmgr.mm
Stephan Bergmann 720243baae -Werror,-Wundef
Change-Id: Ia0782d64e23f67b533e57dad587951f061a7c1d2
2013-03-20 16:13:18 +01:00

654 lines
23 KiB
Text

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright 2000, 2010 Oracle and/or its affiliates.
*
* OpenOffice.org - a multi-platform office productivity suite
*
* This file is part of OpenOffice.org.
*
* OpenOffice.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenOffice.org 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 Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenOffice.org. If not, see
* <http://www.openoffice.org/license.html>
* for a copy of the LGPLv3 License.
*
************************************************************************/
#include "rtl/ustrbuf.hxx"
#include "rtl/strbuf.hxx"
#include "plugin/impl.hxx"
#include "osl/file.h"
#include "osl/module.hxx"
using namespace std;
using namespace com::sun::star::uno;
using namespace com::sun::star::plugin;
using ::rtl::OUString;
using ::rtl::OString;
using ::rtl::OUStringBuffer;
using ::rtl::OStringBuffer;
using ::rtl::OUStringToOString;
using ::rtl::OStringToOUString;
namespace plugstringhelper
{
rtl::OUString getString( CFStringRef i_xString )
{
rtl::OUStringBuffer aBuf;
if( i_xString )
{
CFIndex nChars = CFStringGetLength( i_xString );
CFRange aRange = { 0, nChars };
aBuf.setLength( nChars );
CFStringGetCharacters( i_xString, aRange, static_cast< UniChar* >(const_cast<sal_Unicode*>(aBuf.getStr())) );
}
return aBuf.makeStringAndClear();
}
rtl::OUString getString( CFURLRef i_xURL )
{
CFStringRef xString = CFURLGetString( i_xURL );
return getString( xString );
}
CFMutableStringRef createString( const rtl::OUString& i_rString )
{
CFMutableStringRef xString = CFStringCreateMutable( NULL, 0 );
if( xString )
CFStringAppendCharacters( xString, i_rString.getStr(), i_rString.getLength() );
return xString;
}
CFURLRef createURL( const rtl::OUString& i_rString )
{
CFMutableStringRef xMutableString = createString( i_rString );
CFURLRef xURL = CFURLCreateWithString( NULL, xMutableString, NULL );
CFRelease( xMutableString );
return xURL;
}
rtl::OUString getURLFromPath( const rtl::OUString& i_rPath )
{
CFMutableStringRef xMutableString = createString( i_rPath );
CFURLRef xURL = CFURLCreateWithFileSystemPath( NULL, xMutableString, kCFURLPOSIXPathStyle, true );
CFRelease( xMutableString );
CFStringRef xString = CFURLGetString( xURL );
rtl::OUString aRet = getString( xString );
CFRelease( xURL );
return aRet;
}
CFURLRef createURLFromPath( const rtl::OUString& i_rPath )
{
CFMutableStringRef xMutableString = createString( i_rPath );
CFURLRef xURL = CFURLCreateWithFileSystemPath( NULL, xMutableString, kCFURLPOSIXPathStyle, true );
return xURL;
}
rtl::OUString CFURLtoOSLURL( CFURLRef i_xURL )
{
// make URL absolute
CFURLRef xAbsURL = CFURLCopyAbsoluteURL( i_xURL );
// copy system path
CFStringRef xSysPath = CFURLCopyFileSystemPath( xAbsURL ? xAbsURL : i_xURL, kCFURLPOSIXPathStyle );
if( xAbsURL )
CFRelease( xAbsURL );
rtl::OUString aSysPath( getString( xSysPath ) );
CFRelease( xSysPath );
rtl::OUString aFileURL;
osl_getFileURLFromSystemPath( aSysPath.pData, &aFileURL.pData );
return aFileURL;
}
}
using namespace plugstringhelper;
static int parsePlist( CFBundleRef i_xBundle, const rtl::OUString& i_rBundleURL , list< PluginDescription* >& io_rDescriptions )
{
CFTypeRef xMimeDict = CFBundleGetValueForInfoDictionaryKey( i_xBundle, CFSTR("WebPluginMIMETypes"));
int nMimetypes = 0;
if( xMimeDict == 0 ||
CFGetTypeID(xMimeDict) != CFDictionaryGetTypeID() ||
(nMimetypes = CFDictionaryGetCount( static_cast<CFDictionaryRef>(xMimeDict))) <= 0 )
{
return 0;
}
// prepare an array of key and value refs
std::vector< CFTypeRef > aKeys( nMimetypes, CFTypeRef(NULL) );
std::vector< CFTypeRef > aValues( nMimetypes, CFTypeRef(NULL) );
CFDictionaryGetKeysAndValues(static_cast<CFDictionaryRef>(xMimeDict), &aKeys[0], &aValues[0] );
int nAdded = 0;
for( int i = 0; i < nMimetypes; i++ )
{
// get the mimetype
CFTypeRef xKey = aKeys[i];
if( ! xKey || CFGetTypeID(xKey) != CFStringGetTypeID() )
continue;
rtl::OUString aMimetype = getString( (CFStringRef)xKey );
// the correspoding value should be a dictionary
CFTypeRef xDict = aValues[i];
if( ! xDict || CFGetTypeID( xDict ) != CFDictionaryGetTypeID() )
continue;
// get the extension list
CFTypeRef xExtArray = CFDictionaryGetValue( (CFDictionaryRef)xDict, CFSTR("WebPluginExtensions" ) );
if( !xExtArray || CFGetTypeID( xExtArray ) != CFArrayGetTypeID() )
continue;
OUStringBuffer aExtBuf;
int nExtensions = CFArrayGetCount( (CFArrayRef)xExtArray );
for( int n = 0; n < nExtensions; n++ )
{
CFTypeRef xExt = CFArrayGetValueAtIndex( (CFArrayRef)xExtArray, n );
if( xExt && CFGetTypeID( xExt ) == CFStringGetTypeID() )
{
if( aExtBuf.getLength() > 0 )
aExtBuf.append( sal_Unicode(';') );
OUString aExt( getString( (CFStringRef)xExt ) );
if( aExt.indexOfAsciiL( "*.", 2 ) != 0 )
aExtBuf.appendAscii( "*." );
aExtBuf.append( aExt );
}
}
// get the description string
CFTypeRef xDescString = CFDictionaryGetValue( (CFDictionaryRef)xDict, CFSTR("WebPluginTypeDescription" ) );
if( !xDescString || CFGetTypeID( xDescString ) != CFStringGetTypeID() )
continue;
rtl::OUString aDescription = getString( (CFStringRef)xDescString );
PluginDescription* pNew = new PluginDescription;
// set plugin name (path to library)
pNew->PluginName = i_rBundleURL;
// set mimetype
pNew->Mimetype = aMimetype;
// set extension line
pNew->Extension = aExtBuf.makeStringAndClear();
// set description
pNew->Description= aDescription;
io_rDescriptions.push_back( pNew );
nAdded++;
#if OSL_DEBUG_LEVEL > 1
fprintf( stderr,
"Inserting from PList:\n"
" Mimetype: %s\n"
" Extension: %s\n"
" Description: %s\n",
OUStringToOString( pNew->Mimetype, RTL_TEXTENCODING_UTF8 ).getStr(),
OUStringToOString( pNew->Extension, RTL_TEXTENCODING_UTF8 ).getStr(),
OUStringToOString( pNew->Description, RTL_TEXTENCODING_UTF8 ).getStr()
);
#endif
}
return nAdded;
}
static int parseMimeString( const rtl::OUString& i_rBundleURL , list< PluginDescription* >& io_rDescriptions, const char* i_pMime )
{
if( ! i_pMime )
return 0;
rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
OStringBuffer aMIME;
aMIME.append( i_pMime );
if( aMIME.getLength() < 1 )
return 0;
OString aLine = aMIME.makeStringAndClear();
int nAdded = 0;
sal_Int32 nIndex = 0;
while( nIndex != -1 )
{
OString aType = aLine.getToken( 0, ';', nIndex );
sal_Int32 nTypeIndex = 0;
OString aMimetype = aType.getToken( 0, ':', nTypeIndex );
OString aExtLine = aType.getToken( 0, ':', nTypeIndex );
if( nTypeIndex < 0 ) // ensure at least three tokens
continue;
OString aDesc = aType.getToken( 0, ':', nTypeIndex );
// create extension list string
sal_Int32 nExtIndex = 0;
OStringBuffer aExtension;
while( nExtIndex != -1 )
{
OString aExt = aExtLine.getToken( 0, ',', nExtIndex);
if( aExt.indexOf( "*." ) != 0 )
aExtension.append( "*." );
aExtension.append( aExt );
if( nExtIndex != -1 )
aExtension.append( ';' );
}
PluginDescription* pNew = new PluginDescription;
// set plugin name (path to library)
pNew->PluginName = i_rBundleURL;
// set mimetype
pNew->Mimetype = OStringToOUString( aMimetype, aEncoding );
// set extension line
pNew->Extension = OStringToOUString( aExtension.makeStringAndClear(), aEncoding );
// set description
pNew->Description= OStringToOUString( aDesc, aEncoding );
io_rDescriptions.push_back( pNew );
nAdded++;
#if OSL_DEBUG_LEVEL > 1
fprintf( stderr,
"Inserting from mime string:\n"
" Mimetype: %s\n"
" Extension: %s\n"
" Description: %s\n",
OUStringToOString( pNew->Mimetype, aEncoding ).getStr(),
OUStringToOString( pNew->Extension, aEncoding ).getStr(),
OUStringToOString( pNew->Description, aEncoding ).getStr()
);
#endif
}
return nAdded;
}
// this is so ugly it you want to tear your eyes out
static rtl::OUString GetNextPluginStringFromHandle(Handle h, short *index)
{
char* pPascalBytes = (*h + *index);
sal_uInt32 nLen = (unsigned char)pPascalBytes[0];
rtl::OStringBuffer aBuf( nLen );
aBuf.append( pPascalBytes+1, nLen );
*index += nLen + 1;
return rtl::OStringToOUString( aBuf.makeStringAndClear(), RTL_TEXTENCODING_UTF8 );
}
static int parseMimeResource( CFBundleRef i_xBundle,
oslModule& i_rMod,
const rtl::OUString& i_rBundleURL,
list< PluginDescription* >& io_rDescriptions )
{
int nAdded = 0;
// just to hurt our eyes more there is an alternative mimetype function plus the possibility
// of a resource fork. Must be a case of think different.
#if defined __LP64__
int
#else
SInt16
#endif
xRes = 0;
BPSupportedMIMETypes aMIMETypesStrangeStruct = {kBPSupportedMIMETypesStructVers_1, NULL, NULL};
BP_GetSupportedMIMETypesUPP pBPGetSupp = (BP_GetSupportedMIMETypesUPP)osl_getAsciiFunctionSymbol( i_rMod, "BP_GetSupportedMIMETypes" );
if( pBPGetSupp &&
noErr == pBPGetSupp( &aMIMETypesStrangeStruct, 0 ) &&
aMIMETypesStrangeStruct.typeStrings )
{
HLock( aMIMETypesStrangeStruct.typeStrings );
if( aMIMETypesStrangeStruct.infoStrings ) // it's possible some plugins have infoStrings missing
HLock( aMIMETypesStrangeStruct.infoStrings );
}
else // Try to get data from the resource fork
{
xRes = CFBundleOpenBundleResourceMap( i_xBundle );
if( xRes > 0 )
{
aMIMETypesStrangeStruct.typeStrings = Get1Resource('STR#', 128);
if( aMIMETypesStrangeStruct.typeStrings )
{
DetachResource( aMIMETypesStrangeStruct.typeStrings );
HLock( aMIMETypesStrangeStruct.typeStrings );
aMIMETypesStrangeStruct.infoStrings = Get1Resource('STR#', 127);
if( aMIMETypesStrangeStruct.infoStrings )
{
DetachResource( aMIMETypesStrangeStruct.infoStrings );
HLock( aMIMETypesStrangeStruct.infoStrings );
}
}
}
}
if( aMIMETypesStrangeStruct.typeStrings && aMIMETypesStrangeStruct.infoStrings )
{
short nVariantCount = (**(short**)aMIMETypesStrangeStruct.typeStrings) / 2;
// Fill in the info struct based on the data in the BPSupportedMIMETypes struct
// this is an array of pascal string of unknown (!) encoding
// whoever thought of this deserves a fair beating
short mimeIndex = 2;
short descriptionIndex = 2;
for( int i = 0; i < nVariantCount; i++ )
{
rtl::OUString aMimetype = GetNextPluginStringFromHandle( aMIMETypesStrangeStruct.typeStrings, &mimeIndex );
rtl::OUString aExtLine = GetNextPluginStringFromHandle( aMIMETypesStrangeStruct.typeStrings, &mimeIndex );
rtl::OUString aDescription;
if( aMIMETypesStrangeStruct.infoStrings )
aDescription = GetNextPluginStringFromHandle( aMIMETypesStrangeStruct.infoStrings, &descriptionIndex );
// create extension list string
sal_Int32 nExtIndex = 0;
OUStringBuffer aExtension;
while( nExtIndex != -1 )
{
OUString aExt = aExtLine.getToken( 0, ',', nExtIndex);
if( aExt.indexOfAsciiL( "*.", 2 ) != 0 )
aExtension.appendAscii( "*." );
aExtension.append( aExt );
if( nExtIndex != -1 )
aExtension.append( sal_Unicode(';') );
}
PluginDescription* pNew = new PluginDescription;
// set plugin name (path to library)
pNew->PluginName = i_rBundleURL;
// set mimetype
pNew->Mimetype = aMimetype;
// set extension line
pNew->Extension = aExtension.makeStringAndClear();
// set description
pNew->Description= aDescription;
io_rDescriptions.push_back( pNew );
nAdded++;
#if OSL_DEBUG_LEVEL > 1
fprintf( stderr,
"Inserting from resource:\n"
" Mimetype: %s\n"
" Extension: %s\n"
" Description: %s\n",
OUStringToOString( pNew->Mimetype, RTL_TEXTENCODING_UTF8 ).getStr(),
OUStringToOString( pNew->Extension, RTL_TEXTENCODING_UTF8 ).getStr(),
OUStringToOString( pNew->Description, RTL_TEXTENCODING_UTF8 ).getStr()
);
#endif
}
}
// clean up
if( aMIMETypesStrangeStruct.typeStrings )
{
HUnlock( aMIMETypesStrangeStruct.typeStrings );
DisposeHandle( aMIMETypesStrangeStruct.typeStrings );
}
if( aMIMETypesStrangeStruct.infoStrings )
{
HUnlock( aMIMETypesStrangeStruct.infoStrings );
DisposeHandle( aMIMETypesStrangeStruct.infoStrings );
}
if( xRes )
CFBundleCloseBundleResourceMap( i_xBundle, xRes );
return nAdded;
}
// check some known bad plugins to avoid crashes
static bool checkBlackList( CFBundleRef i_xBundle )
{
rtl::OUString aBundleName;
CFTypeRef bundlename = CFBundleGetValueForInfoDictionaryKey( i_xBundle, CFSTR("CFBundleName"));
if( bundlename && CFGetTypeID(bundlename) == CFStringGetTypeID() )
aBundleName = getString( static_cast<CFStringRef>(bundlename) );
rtl::OUString aBundleVersion;
CFTypeRef bundleversion = CFBundleGetValueForInfoDictionaryKey( i_xBundle, CFSTR("CFBundleVersion"));
if( bundleversion && CFGetTypeID(bundleversion) == CFStringGetTypeID() )
aBundleVersion = getString( static_cast<CFStringRef>(bundleversion) );
bool bReject = false;
// #i102735# VLC plugin prior to 1.0 tends to crash
if( aBundleName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "VLC Plug-in" ) ) )
{
sal_Int32 nIndex = 0;
rtl::OUString aMajor( aBundleVersion.getToken( 0, '.', nIndex ) );
if( aMajor.toInt32() < 1 )
{
bReject = true;
}
}
// #i103674# Garmin Communicator Plugin crashes
else if( aBundleName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Garmin Communicator Plugin" ) ) )
{
bReject = true;
}
#if OSL_DEBUG_LEVEL > 1
if( bReject )
fprintf( stderr, "rejecting plugin \"%s\" version %s\n",
rtl::OUStringToOString( aBundleName, RTL_TEXTENCODING_UTF8 ).getStr(),
rtl::OUStringToOString( aBundleVersion, RTL_TEXTENCODING_UTF8 ).getStr()
);
#endif
return bReject;
}
static int getPluginDescriptions( CFBundleRef i_xBundle , list< PluginDescription* >& io_rDescriptions )
{
int nDescriptions = 0;
if( ! i_xBundle )
return nDescriptions;
if( checkBlackList( i_xBundle ) )
return 0;
rtl::OUString aPlugURL;
CFURLRef xURL = CFBundleCopyBundleURL( i_xBundle );
aPlugURL = getString( xURL );
CFRelease( xURL );
#if OSL_DEBUG_LEVEL > 1
rtl::OUString aPlugName, aPlugDescription;
CFTypeRef name = CFBundleGetValueForInfoDictionaryKey( i_xBundle, CFSTR("WebPluginName"));
if( name && CFGetTypeID(name) == CFStringGetTypeID() )
aPlugName = getString( static_cast<CFStringRef>(name) );
CFTypeRef description = CFBundleGetValueForInfoDictionaryKey( i_xBundle, CFSTR("WebPluginDescription"));
if( description && CFGetTypeID(description) == CFStringGetTypeID() )
aPlugDescription = getString( static_cast<CFStringRef>(description) );
fprintf( stderr, "URL: %s\nname: %s\ndescription: %s\n",
rtl::OUStringToOString( aPlugURL, RTL_TEXTENCODING_UTF8 ).getStr(),
rtl::OUStringToOString( aPlugName, RTL_TEXTENCODING_UTF8 ).getStr(),
rtl::OUStringToOString( aPlugDescription, RTL_TEXTENCODING_UTF8 ).getStr()
);
#endif
// get location of plugin library
CFURLRef xLibURL = CFBundleCopyExecutableURL( i_xBundle );
if( ! xLibURL )
return 0;
// get the file system path
rtl::OUString aModuleURL( CFURLtoOSLURL( xLibURL ) );
CFRelease( xLibURL );
#if OSL_DEBUG_LEVEL > 1
fprintf( stderr, "exec URL = %s\n", rtl::OUStringToOString( aModuleURL, RTL_TEXTENCODING_UTF8 ).getStr() );
#endif
/* TODO: originally the C++ wrapper for oslModule was used here, but that led to
mysterious crashes in the event loop (pointing to heap corruption). Why using
the C style oslModule should fix this is completely unknown. It may be that
we have just hidden the heap corruption a little more.
*/
oslModule aMod = osl_loadModule( aModuleURL.pData, SAL_LOADMODULE_DEFAULT );
if( ! aMod )
return 0;
// check for at least the init function of a plugin
if( ! osl_getAsciiFunctionSymbol( aMod, "NP_Initialize") &&
! osl_getAsciiFunctionSymbol( aMod, "NP_GetEntryPoints" ) )
{
return 0;
}
// ask the plist of the bundle for mimetypes
nDescriptions = parsePlist( i_xBundle, aPlugURL, io_rDescriptions );
if( nDescriptions )
{
osl_unloadModule( aMod );
return nDescriptions;
}
// resolve the symbol that might get us the mimetypes
const char* (*pGetMimeDescription)() = (const char*(*)())osl_getAsciiFunctionSymbol( aMod, "_NP_GetMIMEDescription" );
if( pGetMimeDescription )
{
const char* pMime = pGetMimeDescription();
if( pMime )
{
nDescriptions = parseMimeString( aPlugURL, io_rDescriptions, pMime );
if( nDescriptions )
{
osl_unloadModule( aMod );
return nDescriptions;
}
}
}
// and as last resort check the resource of the bundle
nDescriptions = parseMimeResource( i_xBundle, aMod, aPlugURL, io_rDescriptions );
osl_unloadModule( aMod );
return nDescriptions;
}
// Unix specific implementation
static bool CheckPlugin( const rtl::OUString& rPath, list< PluginDescription* >& rDescriptions )
{
#if OSL_DEBUG_LEVEL > 1
fprintf( stderr, "Trying path %s ... ", rtl::OUStringToOString( rPath, RTL_TEXTENCODING_UTF8 ).getStr() );
#endif
CFURLRef xURL = createURL( rPath );
CFArrayRef xBundles = CFBundleCreateBundlesFromDirectory( NULL, xURL, CFSTR("plugin") );
if( ! xBundles )
return false;
CFIndex nBundles = CFArrayGetCount( xBundles );
#if OSL_DEBUG_LEVEL > 1
fprintf( stderr, "got %d bundles\n", (int)nBundles );
#endif
int nDescriptions = 0;
for( CFIndex i = 0; i < nBundles; i++ )
{
CFBundleRef xBundle = (CFBundleRef)CFArrayGetValueAtIndex( xBundles, i );
nDescriptions += getPluginDescriptions( xBundle, rDescriptions );
CFRelease( xBundle );
}
CFRelease( xBundles );
return nDescriptions > 0;
}
static rtl::OUString FindFolderURL( FSVolumeRefNum vRefNum, OSType folderType )
{
rtl::OUString aRet;
FSRef aFSRef;
OSErr err = FSFindFolder( vRefNum, folderType, kDontCreateFolder, &aFSRef );
if( err == noErr )
{
CFURLRef xURL = CFURLCreateFromFSRef( NULL, &aFSRef );
aRet = getString( xURL );
CFRelease( xURL );
}
return aRet;
}
Sequence<PluginDescription> XPluginManager_Impl::impl_getPluginDescriptions() throw()
{
static Sequence<PluginDescription> aDescriptions;
static sal_Bool bHavePlugins = sal_False;
if( ! bHavePlugins )
{
std::list<PluginDescription*> aPlugins;
static const char* pNPXPluginPath = getenv( "MOZ_PLUGIN_PATH" );
// get directories
std::list< rtl::OUString > aPaths;
if( pNPXPluginPath )
{
CFMutableStringRef xMutableString = CFStringCreateMutable( NULL, 0 );
CFStringAppendCString( xMutableString, pNPXPluginPath, kCFStringEncodingUTF8 );
CFURLRef xURL = CFURLCreateWithFileSystemPath( NULL, xMutableString, kCFURLPOSIXPathStyle, true );
CFRelease( xMutableString );
aPaths.push_back( getString( xURL ) );
CFRelease( xURL );
}
rtl::OUString aPath = FindFolderURL( kUserDomain, kInternetPlugInFolderType );
if( aPath.getLength() )
aPaths.push_back( aPath );
aPath = FindFolderURL( kLocalDomain, kInternetPlugInFolderType );
if( aPath.getLength() )
aPaths.push_back( aPath );
aPath = FindFolderURL( kOnAppropriateDisk, kInternetPlugInFolderType );
if( aPath.getLength() )
aPaths.push_back( aPath );
const Sequence< ::rtl::OUString >& rPaths( PluginManager::getAdditionalSearchPaths() );
for( sal_Int32 i = 0; i < rPaths.getLength(); i++ )
{
aPaths.push_back( getURLFromPath( rPaths.getConstArray()[i] ) );
}
for( std::list< rtl::OUString >::const_iterator it = aPaths.begin(); it != aPaths.end(); ++it )
{
rtl::OUString aPath( *it );
#if OSL_DEBUG_LEVEL > 1
fprintf( stderr, "check path %s\n", rtl::OUStringToOString( *it, RTL_TEXTENCODING_UTF8 ).getStr() );
#endif
CheckPlugin( aPath, aPlugins );
}
// create return value
aDescriptions = Sequence<PluginDescription>( aPlugins.size() );
#if OSL_DEBUG_LEVEL > 1
fprintf( stderr, "found %d plugins\n", (int)aPlugins.size() );
#endif
list<PluginDescription*>::iterator iter;
sal_Int32 nPlug = 0;
for( iter = aPlugins.begin(); iter != aPlugins.end(); ++iter )
{
aDescriptions.getArray()[ nPlug++ ] = **iter;
delete *iter;
}
aPlugins.clear();
bHavePlugins = sal_True;
}
return aDescriptions;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */