1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 #include "rtl/ustrbuf.hxx"
29 #include "rtl/strbuf.hxx"
31 #include "plugin/impl.hxx"
33 #include "osl/module.hxx"
37 using namespace com::sun::star::uno
;
38 using namespace com::sun::star::plugin
;
40 namespace plugstringhelper
43 rtl::OUString
getString( CFStringRef i_xString
)
45 rtl::OUStringBuffer aBuf
;
48 CFIndex nChars
= CFStringGetLength( i_xString
);
49 CFRange aRange
= { 0, nChars
};
50 aBuf
.setLength( nChars
);
51 CFStringGetCharacters( i_xString
, aRange
, static_cast< UniChar
* >(const_cast<sal_Unicode
*>(aBuf
.getStr())) );
53 return aBuf
.makeStringAndClear();
56 rtl::OUString
getString( CFURLRef i_xURL
)
58 CFStringRef xString
= CFURLGetString( i_xURL
);
59 return getString( xString
);
62 CFMutableStringRef
createString( const rtl::OUString
& i_rString
)
64 CFMutableStringRef xString
= CFStringCreateMutable( NULL
, 0 );
66 CFStringAppendCharacters( xString
, i_rString
.getStr(), i_rString
.getLength() );
70 CFURLRef
createURL( const rtl::OUString
& i_rString
)
73 CFMutableStringRef xMutableString
= createString( i_rString
);
74 CFURLRef xURL
= CFURLCreateWithString( NULL
, xMutableString
, NULL
);
75 CFRelease( xMutableString
);
79 rtl::OUString
getURLFromPath( const rtl::OUString
& i_rPath
)
81 CFMutableStringRef xMutableString
= createString( i_rPath
);
82 CFURLRef xURL
= CFURLCreateWithFileSystemPath( NULL
, xMutableString
, kCFURLPOSIXPathStyle
, true );
83 CFRelease( xMutableString
);
84 CFStringRef xString
= CFURLGetString( xURL
);
85 rtl::OUString aRet
= getString( xString
);
90 CFURLRef
createURLFromPath( const rtl::OUString
& i_rPath
)
92 CFMutableStringRef xMutableString
= createString( i_rPath
);
93 CFURLRef xURL
= CFURLCreateWithFileSystemPath( NULL
, xMutableString
, kCFURLPOSIXPathStyle
, true );
97 rtl::OUString
CFURLtoOSLURL( CFURLRef i_xURL
)
100 CFURLRef xAbsURL
= CFURLCopyAbsoluteURL( i_xURL
);
102 CFStringRef xSysPath
= CFURLCopyFileSystemPath( xAbsURL
? xAbsURL
: i_xURL
, kCFURLPOSIXPathStyle
);
104 CFRelease( xAbsURL
);
105 rtl::OUString
aSysPath( getString( xSysPath
) );
106 CFRelease( xSysPath
);
107 rtl::OUString aFileURL
;
108 osl_getFileURLFromSystemPath( aSysPath
.pData
, &aFileURL
.pData
);
114 using namespace plugstringhelper
;
116 static int parsePlist( CFBundleRef i_xBundle
, const rtl::OUString
& i_rBundleURL
, list
< PluginDescription
* >& io_rDescriptions
)
118 CFTypeRef xMimeDict
= CFBundleGetValueForInfoDictionaryKey( i_xBundle
, CFSTR("WebPluginMIMETypes"));
120 if( xMimeDict
== 0 ||
121 CFGetTypeID(xMimeDict
) != CFDictionaryGetTypeID() ||
122 (nMimetypes
= CFDictionaryGetCount( static_cast<CFDictionaryRef
>(xMimeDict
))) <= 0 )
127 // prepare an array of key and value refs
128 std::vector
< CFTypeRef
> aKeys( nMimetypes
, CFTypeRef(NULL
) );
129 std::vector
< CFTypeRef
> aValues( nMimetypes
, CFTypeRef(NULL
) );
130 CFDictionaryGetKeysAndValues(static_cast<CFDictionaryRef
>(xMimeDict
), &aKeys
[0], &aValues
[0] );
133 for( int i
= 0; i
< nMimetypes
; i
++ )
136 CFTypeRef xKey
= aKeys
[i
];
137 if( ! xKey
|| CFGetTypeID(xKey
) != CFStringGetTypeID() )
139 rtl::OUString aMimetype
= getString( (CFStringRef
)xKey
);
141 // the correspoding value should be a dictionary
142 CFTypeRef xDict
= aValues
[i
];
143 if( ! xDict
|| CFGetTypeID( xDict
) != CFDictionaryGetTypeID() )
146 // get the extension list
147 CFTypeRef xExtArray
= CFDictionaryGetValue( (CFDictionaryRef
)xDict
, CFSTR("WebPluginExtensions" ) );
148 if( !xExtArray
|| CFGetTypeID( xExtArray
) != CFArrayGetTypeID() )
151 OUStringBuffer aExtBuf
;
152 int nExtensions
= CFArrayGetCount( (CFArrayRef
)xExtArray
);
153 for( int n
= 0; n
< nExtensions
; n
++ )
155 CFTypeRef xExt
= CFArrayGetValueAtIndex( (CFArrayRef
)xExtArray
, n
);
156 if( xExt
&& CFGetTypeID( xExt
) == CFStringGetTypeID() )
158 if( aExtBuf
.getLength() > 0 )
159 aExtBuf
.append( sal_Unicode(';') );
160 OUString
aExt( getString( (CFStringRef
)xExt
) );
161 if( aExt
.indexOfAsciiL( "*.", 2 ) != 0 )
162 aExtBuf
.appendAscii( "*." );
163 aExtBuf
.append( aExt
);
167 // get the description string
168 CFTypeRef xDescString
= CFDictionaryGetValue( (CFDictionaryRef
)xDict
, CFSTR("WebPluginTypeDescription" ) );
169 if( !xDescString
|| CFGetTypeID( xDescString
) != CFStringGetTypeID() )
171 rtl::OUString aDescription
= getString( (CFStringRef
)xDescString
);
173 PluginDescription
* pNew
= new PluginDescription
;
174 // set plugin name (path to library)
175 pNew
->PluginName
= i_rBundleURL
;
177 pNew
->Mimetype
= aMimetype
;
178 // set extension line
179 pNew
->Extension
= aExtBuf
.makeStringAndClear();
181 pNew
->Description
= aDescription
;
183 io_rDescriptions
.push_back( pNew
);
186 #if OSL_DEBUG_LEVEL > 1
188 "Inserting from PList:\n"
191 " Description: %s\n",
192 OUStringToOString( pNew
->Mimetype
, RTL_TEXTENCODING_UTF8
).getStr(),
193 OUStringToOString( pNew
->Extension
, RTL_TEXTENCODING_UTF8
).getStr(),
194 OUStringToOString( pNew
->Description
, RTL_TEXTENCODING_UTF8
).getStr()
203 static int parseMimeString( const rtl::OUString
& i_rBundleURL
, list
< PluginDescription
* >& io_rDescriptions
, const char* i_pMime
)
208 rtl_TextEncoding aEncoding
= osl_getThreadTextEncoding();
211 aMIME
.append( i_pMime
);
213 if( aMIME
.getLength() < 1 )
216 OString aLine
= aMIME
.makeStringAndClear();
219 sal_Int32 nIndex
= 0;
220 while( nIndex
!= -1 )
222 OString aType
= aLine
.getToken( 0, ';', nIndex
);
224 sal_Int32 nTypeIndex
= 0;
225 OString aMimetype
= aType
.getToken( 0, ':', nTypeIndex
);
226 OString aExtLine
= aType
.getToken( 0, ':', nTypeIndex
);
227 if( nTypeIndex
< 0 ) // ensure at least three tokens
229 OString aDesc
= aType
.getToken( 0, ':', nTypeIndex
);
231 // create extension list string
232 sal_Int32 nExtIndex
= 0;
233 OStringBuffer aExtension
;
234 while( nExtIndex
!= -1 )
236 OString aExt
= aExtLine
.getToken( 0, ',', nExtIndex
);
237 if( aExt
.indexOf( "*." ) != 0 )
238 aExtension
.append( "*." );
239 aExtension
.append( aExt
);
240 if( nExtIndex
!= -1 )
241 aExtension
.append( ';' );
244 PluginDescription
* pNew
= new PluginDescription
;
245 // set plugin name (path to library)
246 pNew
->PluginName
= i_rBundleURL
;
248 pNew
->Mimetype
= OStringToOUString( aMimetype
, aEncoding
);
249 // set extension line
250 pNew
->Extension
= OStringToOUString( aExtension
.makeStringAndClear(), aEncoding
);
252 pNew
->Description
= OStringToOUString( aDesc
, aEncoding
);
253 io_rDescriptions
.push_back( pNew
);
256 #if OSL_DEBUG_LEVEL > 1
258 "Inserting from mime string:\n"
261 " Description: %s\n",
262 OUStringToOString( pNew
->Mimetype
, aEncoding
).getStr(),
263 OUStringToOString( pNew
->Extension
, aEncoding
).getStr(),
264 OUStringToOString( pNew
->Description
, aEncoding
).getStr()
271 // this is so ugly it you want to tear your eyes out
272 static rtl::OUString
GetNextPluginStringFromHandle(Handle h
, short *index
)
274 char* pPascalBytes
= (*h
+ *index
);
275 sal_uInt32 nLen
= (unsigned char)pPascalBytes
[0];
276 rtl::OStringBuffer
aBuf( nLen
);
277 aBuf
.append( pPascalBytes
+1, nLen
);
279 return rtl::OStringToOUString( aBuf
.makeStringAndClear(), RTL_TEXTENCODING_UTF8
);
282 static int parseMimeResource( CFBundleRef i_xBundle
,
284 const rtl::OUString
& i_rBundleURL
,
285 list
< PluginDescription
* >& io_rDescriptions
)
288 // just to hurt our eyes more there is an alternative mimetype function plus the possibility
289 // of a resource fork. Must be a case of think different.
296 BPSupportedMIMETypes aMIMETypesStrangeStruct
= {kBPSupportedMIMETypesStructVers_1
, NULL
, NULL
};
298 BP_GetSupportedMIMETypesUPP pBPGetSupp
= (BP_GetSupportedMIMETypesUPP
)osl_getAsciiFunctionSymbol( i_rMod
, "BP_GetSupportedMIMETypes" );
300 noErr
== pBPGetSupp( &aMIMETypesStrangeStruct
, 0 ) &&
301 aMIMETypesStrangeStruct
.typeStrings
)
303 HLock( aMIMETypesStrangeStruct
.typeStrings
);
304 if( aMIMETypesStrangeStruct
.infoStrings
) // it's possible some plugins have infoStrings missing
305 HLock( aMIMETypesStrangeStruct
.infoStrings
);
307 else // Try to get data from the resource fork
309 xRes
= CFBundleOpenBundleResourceMap( i_xBundle
);
312 aMIMETypesStrangeStruct
.typeStrings
= Get1Resource('STR#', 128);
313 if( aMIMETypesStrangeStruct
.typeStrings
)
315 DetachResource( aMIMETypesStrangeStruct
.typeStrings
);
316 HLock( aMIMETypesStrangeStruct
.typeStrings
);
317 aMIMETypesStrangeStruct
.infoStrings
= Get1Resource('STR#', 127);
318 if( aMIMETypesStrangeStruct
.infoStrings
)
320 DetachResource( aMIMETypesStrangeStruct
.infoStrings
);
321 HLock( aMIMETypesStrangeStruct
.infoStrings
);
327 if( aMIMETypesStrangeStruct
.typeStrings
&& aMIMETypesStrangeStruct
.infoStrings
)
329 short nVariantCount
= (**(short**)aMIMETypesStrangeStruct
.typeStrings
) / 2;
330 // Fill in the info struct based on the data in the BPSupportedMIMETypes struct
331 // this is an array of pascal string of unknown (!) encoding
332 // whoever thought of this deserves a fair beating
334 short descriptionIndex
= 2;
335 for( int i
= 0; i
< nVariantCount
; i
++ )
337 rtl::OUString aMimetype
= GetNextPluginStringFromHandle( aMIMETypesStrangeStruct
.typeStrings
, &mimeIndex
);
338 rtl::OUString aExtLine
= GetNextPluginStringFromHandle( aMIMETypesStrangeStruct
.typeStrings
, &mimeIndex
);
339 rtl::OUString aDescription
;
340 if( aMIMETypesStrangeStruct
.infoStrings
)
341 aDescription
= GetNextPluginStringFromHandle( aMIMETypesStrangeStruct
.infoStrings
, &descriptionIndex
);
343 // create extension list string
344 sal_Int32 nExtIndex
= 0;
345 OUStringBuffer aExtension
;
346 while( nExtIndex
!= -1 )
348 OUString aExt
= aExtLine
.getToken( 0, ',', nExtIndex
);
349 if( aExt
.indexOfAsciiL( "*.", 2 ) != 0 )
350 aExtension
.appendAscii( "*." );
351 aExtension
.append( aExt
);
352 if( nExtIndex
!= -1 )
353 aExtension
.append( sal_Unicode(';') );
356 PluginDescription
* pNew
= new PluginDescription
;
357 // set plugin name (path to library)
358 pNew
->PluginName
= i_rBundleURL
;
360 pNew
->Mimetype
= aMimetype
;
361 // set extension line
362 pNew
->Extension
= aExtension
.makeStringAndClear();
364 pNew
->Description
= aDescription
;
365 io_rDescriptions
.push_back( pNew
);
368 #if OSL_DEBUG_LEVEL > 1
370 "Inserting from resource:\n"
373 " Description: %s\n",
374 OUStringToOString( pNew
->Mimetype
, RTL_TEXTENCODING_UTF8
).getStr(),
375 OUStringToOString( pNew
->Extension
, RTL_TEXTENCODING_UTF8
).getStr(),
376 OUStringToOString( pNew
->Description
, RTL_TEXTENCODING_UTF8
).getStr()
384 if( aMIMETypesStrangeStruct
.typeStrings
)
386 HUnlock( aMIMETypesStrangeStruct
.typeStrings
);
387 DisposeHandle( aMIMETypesStrangeStruct
.typeStrings
);
389 if( aMIMETypesStrangeStruct
.infoStrings
)
391 HUnlock( aMIMETypesStrangeStruct
.infoStrings
);
392 DisposeHandle( aMIMETypesStrangeStruct
.infoStrings
);
395 CFBundleCloseBundleResourceMap( i_xBundle
, xRes
);
400 // check some known bad plugins to avoid crashes
401 static bool checkBlackList( CFBundleRef i_xBundle
)
403 rtl::OUString aBundleName
;
404 CFTypeRef bundlename
= CFBundleGetValueForInfoDictionaryKey( i_xBundle
, CFSTR("CFBundleName"));
405 if( bundlename
&& CFGetTypeID(bundlename
) == CFStringGetTypeID() )
406 aBundleName
= getString( static_cast<CFStringRef
>(bundlename
) );
408 rtl::OUString aBundleVersion
;
409 CFTypeRef bundleversion
= CFBundleGetValueForInfoDictionaryKey( i_xBundle
, CFSTR("CFBundleVersion"));
410 if( bundleversion
&& CFGetTypeID(bundleversion
) == CFStringGetTypeID() )
411 aBundleVersion
= getString( static_cast<CFStringRef
>(bundleversion
) );
413 bool bReject
= false;
414 // #i102735# VLC plugin prior to 1.0 tends to crash
415 if( aBundleName
.equalsAscii( "VLC Plug-in" ) )
417 sal_Int32 nIndex
= 0;
418 rtl::OUString
aMajor( aBundleVersion
.getToken( 0, '.', nIndex
) );
419 if( aMajor
.toInt32() < 1 )
424 // #i103674# Garmin Communicator Plugin crashes
425 else if( aBundleName
.equalsAscii( "Garmin Communicator Plugin" ) )
430 #if OSL_DEBUG_LEVEL > 1
432 fprintf( stderr
, "rejecting plugin \"%s\" version %s\n",
433 rtl::OUStringToOString( aBundleName
, RTL_TEXTENCODING_UTF8
).getStr(),
434 rtl::OUStringToOString( aBundleVersion
, RTL_TEXTENCODING_UTF8
).getStr()
441 static int getPluginDescriptions( CFBundleRef i_xBundle
, list
< PluginDescription
* >& io_rDescriptions
)
443 int nDescriptions
= 0;
445 return nDescriptions
;
447 if( checkBlackList( i_xBundle
) )
450 rtl::OUString aPlugURL
;
451 CFURLRef xURL
= CFBundleCopyBundleURL( i_xBundle
);
452 aPlugURL
= getString( xURL
);
455 #if OSL_DEBUG_LEVEL > 1
456 rtl::OUString aPlugName
, aPlugDescription
;
457 CFTypeRef name
= CFBundleGetValueForInfoDictionaryKey( i_xBundle
, CFSTR("WebPluginName"));
458 if( name
&& CFGetTypeID(name
) == CFStringGetTypeID() )
459 aPlugName
= getString( static_cast<CFStringRef
>(name
) );
461 CFTypeRef description
= CFBundleGetValueForInfoDictionaryKey( i_xBundle
, CFSTR("WebPluginDescription"));
462 if( description
&& CFGetTypeID(description
) == CFStringGetTypeID() )
463 aPlugDescription
= getString( static_cast<CFStringRef
>(description
) );
465 fprintf( stderr
, "URL: %s\nname: %s\ndescription: %s\n",
466 rtl::OUStringToOString( aPlugURL
, RTL_TEXTENCODING_UTF8
).getStr(),
467 rtl::OUStringToOString( aPlugName
, RTL_TEXTENCODING_UTF8
).getStr(),
468 rtl::OUStringToOString( aPlugDescription
, RTL_TEXTENCODING_UTF8
).getStr()
473 // get location of plugin library
474 CFURLRef xLibURL
= CFBundleCopyExecutableURL( i_xBundle
);
477 // get the file system path
478 rtl::OUString
aModuleURL( CFURLtoOSLURL( xLibURL
) );
479 CFRelease( xLibURL
);
481 #if OSL_DEBUG_LEVEL > 1
482 fprintf( stderr
, "exec URL = %s\n", rtl::OUStringToOString( aModuleURL
, RTL_TEXTENCODING_UTF8
).getStr() );
485 /* TODO: originally the C++ wrapper for oslModule was used here, but that led to
486 mysterious crashes in the event loop (pointing to heap corruption). Why using
487 the C style oslModule should fix this is completely unknown. It may be that
488 we have just hidden the heap corruption a little more.
490 oslModule aMod
= osl_loadModule( aModuleURL
.pData
, SAL_LOADMODULE_DEFAULT
);
494 // check for at least the init function of a plugin
495 if( ! osl_getAsciiFunctionSymbol( aMod
, "NP_Initialize") &&
496 ! osl_getAsciiFunctionSymbol( aMod
, "NP_GetEntryPoints" ) )
501 // ask the plist of the bundle for mimetypes
502 nDescriptions
= parsePlist( i_xBundle
, aPlugURL
, io_rDescriptions
);
505 osl_unloadModule( aMod
);
506 return nDescriptions
;
509 // resolve the symbol that might get us the mimetypes
510 const char* (*pGetMimeDescription
)() = (const char*(*)())osl_getAsciiFunctionSymbol( aMod
, "_NP_GetMIMEDescription" );
511 if( pGetMimeDescription
)
513 const char* pMime
= pGetMimeDescription();
516 nDescriptions
= parseMimeString( aPlugURL
, io_rDescriptions
, pMime
);
519 osl_unloadModule( aMod
);
520 return nDescriptions
;
525 // and as last resort check the resource of the bundle
526 nDescriptions
= parseMimeResource( i_xBundle
, aMod
, aPlugURL
, io_rDescriptions
);
527 osl_unloadModule( aMod
);
529 return nDescriptions
;
532 // Unix specific implementation
533 static bool CheckPlugin( const rtl::OUString
& rPath
, list
< PluginDescription
* >& rDescriptions
)
535 #if OSL_DEBUG_LEVEL > 1
536 fprintf( stderr
, "Trying path %s ... ", rtl::OUStringToOString( rPath
, RTL_TEXTENCODING_UTF8
).getStr() );
538 CFURLRef xURL
= createURL( rPath
);
540 CFArrayRef xBundles
= CFBundleCreateBundlesFromDirectory( NULL
, xURL
, CFSTR("plugin") );
544 CFIndex nBundles
= CFArrayGetCount( xBundles
);
546 #if OSL_DEBUG_LEVEL > 1
547 fprintf( stderr
, "got %d bundles\n", (int)nBundles
);
550 int nDescriptions
= 0;
551 for( CFIndex i
= 0; i
< nBundles
; i
++ )
553 CFBundleRef xBundle
= (CFBundleRef
)CFArrayGetValueAtIndex( xBundles
, i
);
554 nDescriptions
+= getPluginDescriptions( xBundle
, rDescriptions
);
556 CFRelease( xBundle
);
558 CFRelease( xBundles
);
561 return nDescriptions
> 0;
564 static rtl::OUString
FindFolderURL( FSVolumeRefNum vRefNum
, OSType folderType
)
569 OSErr err
= FSFindFolder( vRefNum
, folderType
, kDontCreateFolder
, &aFSRef
);
572 CFURLRef xURL
= CFURLCreateFromFSRef( NULL
, &aFSRef
);
573 aRet
= getString( xURL
);
580 Sequence
<PluginDescription
> XPluginManager_Impl::impl_getPluginDescriptions() throw()
582 static Sequence
<PluginDescription
> aDescriptions
;
583 static BOOL bHavePlugins
= FALSE
;
586 std::list
<PluginDescription
*> aPlugins
;
588 static const char* pNPXPluginPath
= getenv( "MOZ_PLUGIN_PATH" );
591 std::list
< rtl::OUString
> aPaths
;
594 CFMutableStringRef xMutableString
= CFStringCreateMutable( NULL
, 0 );
595 CFStringAppendCString( xMutableString
, pNPXPluginPath
, kCFStringEncodingUTF8
);
596 CFURLRef xURL
= CFURLCreateWithFileSystemPath( NULL
, xMutableString
, kCFURLPOSIXPathStyle
, true );
597 CFRelease( xMutableString
);
598 aPaths
.push_back( getString( xURL
) );
602 rtl::OUString aPath
= FindFolderURL( kUserDomain
, kInternetPlugInFolderType
);
603 if( aPath
.getLength() )
604 aPaths
.push_back( aPath
);
605 aPath
= FindFolderURL( kLocalDomain
, kInternetPlugInFolderType
);
606 if( aPath
.getLength() )
607 aPaths
.push_back( aPath
);
608 aPath
= FindFolderURL( kOnAppropriateDisk
, kInternetPlugInFolderType
);
609 if( aPath
.getLength() )
610 aPaths
.push_back( aPath
);
613 const Sequence
< ::rtl::OUString
>& rPaths( PluginManager::getAdditionalSearchPaths() );
614 for( sal_Int32 i
= 0; i
< rPaths
.getLength(); i
++ )
616 aPaths
.push_back( getURLFromPath( rPaths
.getConstArray()[i
] ) );
619 for( std::list
< rtl::OUString
>::const_iterator it
= aPaths
.begin(); it
!= aPaths
.end(); ++it
)
621 rtl::OUString
aPath( *it
);
622 #if OSL_DEBUG_LEVEL > 1
623 fprintf( stderr
, "check path %s\n", rtl::OUStringToOString( *it
, RTL_TEXTENCODING_UTF8
).getStr() );
625 CheckPlugin( aPath
, aPlugins
);
629 // create return value
630 aDescriptions
= Sequence
<PluginDescription
>( aPlugins
.size() );
631 #if OSL_DEBUG_LEVEL > 1
632 fprintf( stderr
, "found %d plugins\n", (int)aPlugins
.size() );
634 list
<PluginDescription
*>::iterator iter
;
636 for( iter
= aPlugins
.begin(); iter
!= aPlugins
.end(); ++iter
)
638 aDescriptions
.getArray()[ nPlug
++ ] = **iter
;
644 return aDescriptions
;