/************************************************************************* * * 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 * * 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 rtl; using namespace std; using namespace com::sun::star::uno; using namespace com::sun::star::plugin; 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(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(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(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 __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(bundlename) ); rtl::OUString aBundleVersion; CFTypeRef bundleversion = CFBundleGetValueForInfoDictionaryKey( i_xBundle, CFSTR("CFBundleVersion")); if( bundleversion && CFGetTypeID(bundleversion) == CFStringGetTypeID() ) aBundleVersion = getString( static_cast(bundleversion) ); bool bReject = false; // #i102735# VLC plugin prior to 1.0 tends to crash if( aBundleName.equalsAscii( "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.equalsAscii( "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(name) ); CFTypeRef description = CFBundleGetValueForInfoDictionaryKey( i_xBundle, CFSTR("WebPluginDescription")); if( description && CFGetTypeID(description) == CFStringGetTypeID() ) aPlugDescription = getString( static_cast(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 XPluginManager_Impl::impl_getPluginDescriptions() throw() { static Sequence aDescriptions; static BOOL bHavePlugins = FALSE; if( ! bHavePlugins ) { std::list 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( aPlugins.size() ); #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "found %d plugins\n", (int)aPlugins.size() ); #endif list::iterator iter; sal_Int32 nPlug = 0; for( iter = aPlugins.begin(); iter != aPlugins.end(); ++iter ) { aDescriptions.getArray()[ nPlug++ ] = **iter; delete *iter; } aPlugins.clear(); bHavePlugins = TRUE; } return aDescriptions; }