diff options
Diffstat (limited to 'extensions/source/plugin/unx')
-rw-r--r-- | extensions/source/plugin/unx/makefile.mk | 110 | ||||
-rw-r--r-- | extensions/source/plugin/unx/mediator.cxx | 309 | ||||
-rw-r--r-- | extensions/source/plugin/unx/npnapi.cxx | 898 | ||||
-rw-r--r-- | extensions/source/plugin/unx/nppapi.cxx | 602 | ||||
-rw-r--r-- | extensions/source/plugin/unx/npwrap.cxx | 513 | ||||
-rw-r--r-- | extensions/source/plugin/unx/plugcon.cxx | 282 | ||||
-rw-r--r-- | extensions/source/plugin/unx/sysplug.cxx | 131 | ||||
-rw-r--r-- | extensions/source/plugin/unx/unxmgr.cxx | 315 |
8 files changed, 3160 insertions, 0 deletions
diff --git a/extensions/source/plugin/unx/makefile.mk b/extensions/source/plugin/unx/makefile.mk new file mode 100644 index 000000000000..669aaa556e62 --- /dev/null +++ b/extensions/source/plugin/unx/makefile.mk @@ -0,0 +1,110 @@ +#************************************************************************* +# +# 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. +# +#************************************************************************* +PRJ=..$/..$/.. + +PRJNAME=extensions +TARGET=plunx +TARGETTYPE=CUI +ENABLE_EXCEPTIONS=TRUE + +.INCLUDE : ..$/util$/makefile.pmk + +.IF "$(GUIBASE)"=="aqua" +dummy: + @echo "Nothing to build for GUIBASE aqua." + +.ELSE + +# --- Files -------------------------------------------------------- + +INCPRE+=-I$(SOLARINCDIR)$/mozilla$/plugin +.IF "$(SOLAR_JAVA)" != "" +INCPRE+=-I$(SOLARINCDIR)$/mozilla$/java +INCPRE+=-I$(SOLARINCDIR)$/mozilla$/nspr +CDEFS+=-DOJI +.ENDIF + +.IF "$(WITH_MOZILLA)" != "NO" + +.IF "$(DISABLE_XAW)" == "TRUE" +CDEFS+=-DDISABLE_XAW +.ENDIF + +SLOFILES=\ + $(SLO)$/nppapi.obj \ + $(SLO)$/sysplug.obj \ + $(SLO)$/mediator.obj \ + $(SLO)$/plugcon.obj \ + $(SLO)$/unxmgr.obj + +OBJFILES=\ + $(OBJ)$/npwrap.obj \ + $(OBJ)$/npnapi.obj \ + $(OBJ)$/mediator.obj \ + $(OBJ)$/plugcon.obj + +APP1TARGET=pluginapp.bin +APP1OBJS=$(OBJFILES) +APP1STDLIBS=\ + $(VOSLIB) \ + $(SALLIB) +.IF "$(OS)"=="SOLARIS" || "$(OS)"=="SCO" || "$(OS)"=="HPUX" +APP1STDLIBS+=-lXm -lXt $(X11LINK_DYNAMIC) -ldl +.ELSE +.IF "$(DISABLE_XAW)" != "TRUE" +APP1STDLIBS+=-lXaw +.ENDIF +.IF "$(OS)"=="FREEBSD" || "$(OS)"=="NETBSD" +APP1STDLIBS+= -lXt -lXext -lX11 +.ELSE +APP1STDLIBS+= -lXt $(X11LINK_DYNAMIC) -ldl +.ENDIF +.ENDIF + +.IF "$(ENABLE_GTK)" == "TRUE" +# libs for gtk plugin +APP1STDLIBS+=$(PKGCONFIG_LIBS:s/ -lpangoxft-1.0//) +# hack for faked SO environment +.IF "$(PKGCONFIG_ROOT)"!="" +.IF "$(OS)" == "SOLARIS" +# don't ask, it's ugly +DIRECT :=-z nodefs $(DIRECT) +.ENDIF +.ENDIF # "$(PKGCONFIG_ROOT)"!="" +.ENDIF + + +APP1DEF= $(MISC)$/$(TARGET).def + +.ENDIF # $(WITH_MOZILLA) != "NO" + +.ENDIF # $(GUIBASE)==aqua + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + diff --git a/extensions/source/plugin/unx/mediator.cxx b/extensions/source/plugin/unx/mediator.cxx new file mode 100644 index 000000000000..6191fa5e897a --- /dev/null +++ b/extensions/source/plugin/unx/mediator.cxx @@ -0,0 +1,309 @@ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_extensions.hxx" +#include <errno.h> +#include <unistd.h> + +#include <plugin/unx/mediator.hxx> +#include <vcl/svapp.hxx> + +#define MEDIATOR_MAGIC 0xf7a8d2f4 + +Mediator::Mediator( int nSocket ) : + m_nSocket( nSocket ), + m_pListener( NULL ), + m_nCurrentID( 1 ), + m_bValid( true ) +{ + m_pListener = new MediatorListener( this ); + m_pListener->create(); +} + +Mediator::~Mediator() +{ + if( m_pListener ) + { + { + ::vos::OGuard aGuard( m_pListener->m_aMutex ); + m_pListener->m_pMediator = NULL; + } + m_pListener = NULL; + if( m_bValid ) + { + ULONG aHeader[3]; + aHeader[0] = 0; + aHeader[1] = 0; + aHeader[2] = MEDIATOR_MAGIC; + write( m_nSocket, aHeader, sizeof( aHeader ) ); + } + // kick the thread out of its run method; it deletes itself + close( m_nSocket ); + } + else + close( m_nSocket ); + for( std::vector< MediatorMessage* >::iterator it = m_aMessageQueue.begin(); + it != m_aMessageQueue.end(); ++it ) + { + delete *it; + } +} + + +ULONG Mediator::SendMessage( ULONG nBytes, const char* pBytes, ULONG nMessageID ) +{ + if( ! m_pListener ) + return 0; + + NAMESPACE_VOS(OGuard) aGuard( m_aSendMutex ); + if( ! nMessageID ) + nMessageID = m_nCurrentID; + + m_nCurrentID++; + if( m_nCurrentID >= 1 << 24 ) // protection against overflow + m_nCurrentID = 1; + + if( ! m_bValid ) + return nMessageID; + + ULONG* pBuffer = new ULONG[ (nBytes/sizeof(ULONG)) + 4 ]; + pBuffer[ 0 ] = nMessageID; + pBuffer[ 1 ] = nBytes; + pBuffer[ 2 ] = MEDIATOR_MAGIC; + memcpy( &pBuffer[3], pBytes, (size_t)nBytes ); + write( m_nSocket, pBuffer, nBytes + 3*sizeof( ULONG ) ); + delete [] pBuffer; + + return nMessageID; +} + +BOOL Mediator::WaitForMessage( ULONG nTimeOut ) +{ + if( ! m_pListener ) + return FALSE; + + size_t nItems = m_aMessageQueue.size(); + + if( ! nTimeOut && nItems > 0 ) + return TRUE; + + TimeValue aValue; + aValue.Seconds = nTimeOut/1000; + aValue.Nanosec = ( nTimeOut % 1000 ) * 1000; + + while( m_aMessageQueue.size() == nItems ) + { + m_aNewMessageCdtn.wait( & aValue ); + m_aNewMessageCdtn.reset(); + if( nTimeOut && m_aMessageQueue.size() == nItems ) + return FALSE; + } + return TRUE; +} + +MediatorMessage* Mediator::WaitForAnswer( ULONG nMessageID ) +{ + nMessageID &= 0x00ffffff; + while( m_pListener ) + { + { + NAMESPACE_VOS(OGuard) aGuard( m_aQueueMutex ); + for( size_t i = 0; i < m_aMessageQueue.size(); i++ ) + { + MediatorMessage* pMessage = m_aMessageQueue[ i ]; + ULONG nID = pMessage->m_nID; + if( ( nID & 0xff000000 ) && + ( ( nID & 0x00ffffff ) == nMessageID ) ) + { + m_aMessageQueue.erase( m_aMessageQueue.begin() + i ); + return pMessage; + } + } + } + WaitForMessage( 10 ); + } + return NULL; +} + +MediatorMessage* Mediator::GetNextMessage( BOOL bWait ) +{ + while( m_pListener ) + { + { + // guard must be after WaitForMessage, else the listener + // cannot insert a new one -> deadlock + NAMESPACE_VOS(OGuard) aGuard( m_aQueueMutex ); + for( size_t i = 0; i < m_aMessageQueue.size(); i++ ) + { + MediatorMessage* pMessage = m_aMessageQueue[ i ]; + if( ! ( pMessage->m_nID & 0xff000000 ) ) + { + m_aMessageQueue.erase( m_aMessageQueue.begin() + i ); + return pMessage; + } + } + if( ! bWait ) + return NULL; + } + WaitForMessage(); + } + return NULL; +} + +MediatorMessage* Mediator::TransactMessage( ULONG nBytes, char* pBytes ) +{ + ULONG nID = SendMessage( nBytes, pBytes ); + return WaitForAnswer( nID ); +} + +MediatorListener::MediatorListener( Mediator* pMediator ) : + m_pMediator( pMediator ) +{ +} + +MediatorListener::~MediatorListener() +{ +} + +void MediatorListener::run() +{ + bool bRun = true; + while( schedule() && m_pMediator && bRun ) + { + ULONG nHeader[ 3 ]; + int nBytes; + + if( m_pMediator && ( nBytes = read( m_pMediator->m_nSocket, nHeader, sizeof( nHeader ) ) ) == sizeof( nHeader ) && nHeader[2] == MEDIATOR_MAGIC) + { + if( nHeader[ 0 ] == 0 && nHeader[ 1 ] == 0 ) + return; + char* pBuffer = new char[ nHeader[ 1 ] ]; + if( m_pMediator && (ULONG)read( m_pMediator->m_nSocket, pBuffer, nHeader[ 1 ] ) == nHeader[ 1 ] ) + { + ::vos::OGuard aMyGuard( m_aMutex ); + { + NAMESPACE_VOS(OGuard) + aGuard( m_pMediator->m_aQueueMutex ); + MediatorMessage* pMessage = + new MediatorMessage( nHeader[ 0 ], nHeader[ 1 ], pBuffer ); + m_pMediator->m_aMessageQueue.push_back( pMessage ); + } + m_pMediator->m_aNewMessageCdtn.set(); + m_pMediator->m_aNewMessageHdl.Call( m_pMediator ); + } + else + { + medDebug( 1, "got incomplete MediatorMessage: { %d, %d, %*s }\n", + nHeader[0], nHeader[1], nHeader[1], pBuffer ); + bRun = false; + } + delete [] pBuffer; + } + else + { + medDebug( 1, "got incomplete message header of %d bytes ( nHeader = [ %u, %u ] ), errno is %d\n", + nBytes, nHeader[ 0 ], nHeader[ 1 ], (int)errno ); + bRun = false; + } + } +} + +void MediatorListener::onTerminated() +{ + if( m_pMediator ) + { + m_pMediator->m_aConnectionLostHdl.Call( m_pMediator ); + m_pMediator->m_pListener = NULL; + } + delete this; +} + +ULONG MediatorMessage::ExtractULONG() +{ + if( ! m_pRun ) + m_pRun = m_pBytes; + + medDebug( (ULONG)(m_pRun - m_pBytes) >= m_nBytes, "Overflow in MediatorMessage::ExtractULONG\n" ); + ULONG nCount; + memcpy( &nCount, m_pRun, sizeof( ULONG ) ); + m_pRun += sizeof( ULONG ); + return nCount; +} + +void* MediatorMessage::GetBytes( ULONG& rBytes ) +{ + if( ! m_pRun ) + m_pRun = m_pBytes; + + medDebug( (ULONG)(m_pRun - m_pBytes) >= m_nBytes, "Overflow in MediatorMessage::GetBytes\n" ); + ULONG nBytes = ExtractULONG(); + + if( nBytes == 0 ) + return NULL; + + medDebug( (ULONG)(m_pRun - m_pBytes) >= m_nBytes, "Overflow in MediatorMessage::GetBytes\n" ); + char* pBuffer = new char[ nBytes ]; + memcpy( pBuffer, m_pRun, nBytes ); + m_pRun += nBytes; + rBytes = nBytes; + return pBuffer; +} + +char* MediatorMessage::GetString() +{ + if( ! m_pRun ) + m_pRun = m_pBytes; + + medDebug( (ULONG)(m_pRun - m_pBytes) >= m_nBytes, "Overflow in MediatorMessage::GetString\n" ); + ULONG nBytes = ExtractULONG(); + + if( nBytes == 0 ) + return NULL; + + medDebug( (ULONG)(m_pRun - m_pBytes) >= m_nBytes, "Overflow in MediatorMessage::GetString\n" ); + char* pBuffer = new char[ nBytes+1 ]; + memcpy( pBuffer, m_pRun, nBytes ); + pBuffer[ nBytes ] = 0; + m_pRun += nBytes; + return pBuffer; +} + +UINT32 MediatorMessage::GetUINT32() +{ + if( ! m_pRun ) + m_pRun = m_pBytes; + + medDebug( (ULONG)(m_pRun - m_pBytes) >= m_nBytes, "Overflow in MediatorMessage::GetUINT32\n" ); + ULONG nBytes = ExtractULONG(); + medDebug( nBytes != sizeof( UINT32 ), "No UINT32 in MediatorMessage::GetUINT32\n" ); + medDebug( (ULONG)(m_pRun - m_pBytes) >= m_nBytes, "Overflow in MediatorMessage::GetUINT32\n" ); + UINT32 nRet; + memcpy( &nRet, m_pRun, sizeof( nRet ) ); + m_pRun += sizeof( UINT32 ); + return nRet; +} diff --git a/extensions/source/plugin/unx/npnapi.cxx b/extensions/source/plugin/unx/npnapi.cxx new file mode 100644 index 000000000000..003b4d0943a5 --- /dev/null +++ b/extensions/source/plugin/unx/npnapi.cxx @@ -0,0 +1,898 @@ +/************************************************************************* + + Source Code Control System - Header + + $Header: /zpool/svn/migration/cvs_rep_09_09_08/code/extensions/source/plugin/unx/npnapi.cxx,v 1.11 2008-01-14 14:53:25 ihi Exp $ + +*************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_extensions.hxx" +#include <plugin/unx/plugcon.hxx> + +#include <unistd.h> +#include <dlfcn.h> + +#include <osl/module.h> + +extern PluginConnector* pConnector; +extern XtAppContext app_context; +extern int wakeup_fd[]; +extern Widget topLevel, topBox; +extern Display* pAppDisplay; +extern Display* pXtAppDisplay; +extern int nAppArguments; +extern char** pAppArguments; + +void* CreateNewShell( void**, XLIB_Window ); + +// begin Netscape plugin api calls +extern "C" { + +static void* l_NPN_MemAlloc( uint32 nBytes ) +{ + void* pMem = new char[nBytes]; + return pMem; +} + +static void l_NPN_MemFree( void* pMem ) +{ + delete [] (char*)pMem; +} + +static uint32 l_NPN_MemFlush( uint32 /*nSize*/ ) +{ + return 0; +} + +static NPError l_NPN_DestroyStream( NPP instance, NPStream* stream, NPError reason ) +{ + UINT32 nInstance = pConnector->GetNPPID( instance ); + if( nInstance == PluginConnector::UnknownNPPID ) + return NPERR_GENERIC_ERROR; + + UINT32 nFileID = pConnector->GetStreamID( stream ); + MediatorMessage* pMes= + pConnector-> + Transact( eNPN_DestroyStream, + &nInstance, sizeof( nInstance ), + &nFileID, sizeof( nFileID ), + POST_STRING( stream->url ), + &reason, sizeof( reason ), + NULL ); + + if( ! pMes ) + return NPERR_GENERIC_ERROR; + + for( std::vector< NPStream* >::iterator it = pConnector->getStreamList().begin(); + it != pConnector->getStreamList().end(); ++it ) + { + if( *it == stream ) + { + pConnector->getStreamList().erase( it ); + break; + } + } + delete [] stream->url; + delete stream; + // returns NPError + NPError aRet = pConnector->GetNPError( pMes ); + delete pMes; + return aRet; +} + +#ifdef OJI +static JRIEnv* l_NPN_GetJavaEnv() +{ + // no java in this program + medDebug( 1, "SNI: NPN_GetJavaEnv\n" ); + return NULL; +} + +static jref l_NPN_GetJavaPeer( NPP /*instance*/ ) +{ + medDebug( 1, "SNI: NPN_GetJavaPeer\n" ); + return NULL; +} +#endif + +static NPError l_NPN_GetURL( NPP instance, const char* url, const char* window ) +{ + UINT32 nInstance = pConnector->GetNPPID( instance ); + if( nInstance == PluginConnector::UnknownNPPID ) + return NPERR_GENERIC_ERROR; + + MediatorMessage* pMes= + pConnector-> + Transact( eNPN_GetURL, + &nInstance, sizeof( nInstance ), + POST_STRING(url), + POST_STRING(window), + NULL ); + medDebug( !pMes, "geturl: message unaswered\n" ); + if( ! pMes ) + return NPERR_GENERIC_ERROR; + + // returns NPError + NPError aRet = pConnector->GetNPError( pMes ); + medDebug( aRet, "geturl returns %d\n", (int)aRet ); + delete pMes; + return aRet; +} + +static NPError l_NPN_GetURLNotify( NPP instance, const char* url, const char* target, + void* notifyData ) +{ + UINT32 nInstance = pConnector->GetNPPID( instance ); + if( nInstance == PluginConnector::UnknownNPPID ) + return NPERR_GENERIC_ERROR; + + MediatorMessage* pMes= + pConnector-> + Transact( eNPN_GetURLNotify, + &nInstance, sizeof( nInstance ), + POST_STRING(url), + POST_STRING(target), + ¬ifyData, sizeof( void* ), // transmit the actual pointer + // since it is a pointer to private data fed back + // by NPP_URLNotify; this can be thought of as an ID + NULL ); + if( ! pMes ) + return NPERR_GENERIC_ERROR; + + // returns NPError + NPError aRet = pConnector->GetNPError( pMes ); + delete pMes; + return aRet; +} + +static NPError l_NPN_NewStream( NPP instance, NPMIMEType type, const char* target, + NPStream** stream ) + // stream is a return value +{ + UINT32 nInstance = pConnector->GetNPPID( instance ); + if( nInstance == PluginConnector::UnknownNPPID ) + return NPERR_GENERIC_ERROR; + + MediatorMessage* pMes= + pConnector-> + Transact( eNPN_NewStream, + &nInstance, sizeof( nInstance ), + POST_STRING(type), + POST_STRING(target), + NULL ); + if( ! pMes ) + return NPERR_GENERIC_ERROR; + + // returns a new NPStream and an error + NPError aRet = pConnector->GetNPError( pMes ); + if( ! aRet ) + { + NPStream* pStream = new NPStream; + pStream->url = pMes->GetString(); + pStream->end = pMes->GetUINT32(); + pStream->lastmodified = pMes->GetUINT32(); + pStream->ndata = pStream->pdata = pStream->notifyData = NULL; + + pConnector->getStreamList().push_back( pStream ); + *stream = pStream; + } + + delete pMes; + return aRet; +} + +static NPError l_NPN_PostURLNotify( NPP instance, const char* url, const char* target, uint32 len, const char* buf, NPBool file, void* notifyData ) +{ + UINT32 nInstance = pConnector->GetNPPID( instance ); + if( nInstance == PluginConnector::UnknownNPPID ) + return NPERR_GENERIC_ERROR; + + MediatorMessage* pMes = pConnector-> + Transact( eNPN_PostURLNotify, + &nInstance, sizeof( nInstance ), + POST_STRING( url ), + POST_STRING( target ), + &len, sizeof( len ), + buf, len, + &file, sizeof( NPBool ), + ¬ifyData, sizeof( void* ), // send the real pointer + NULL ); + + if( ! pMes ) + return NPERR_GENERIC_ERROR; + + NPError aRet = pConnector->GetNPError( pMes ); + delete pMes; + return aRet; +} + +static NPError l_NPN_PostURL( NPP instance, const char* url, const char* window, uint32 len, const char* buf, NPBool file ) +{ + UINT32 nInstance = pConnector->GetNPPID( instance ); + if( nInstance == PluginConnector::UnknownNPPID ) + return NPERR_GENERIC_ERROR; + + MediatorMessage* pMes = pConnector-> + Transact( eNPN_PostURL, + &nInstance, sizeof( nInstance ), + POST_STRING( url ), + POST_STRING( window ), + &len, sizeof( len ), + buf, len, + &file, sizeof( NPBool ), + NULL ); + if( ! pMes ) + return NPERR_GENERIC_ERROR; + + NPError aRet = pConnector->GetNPError( pMes ); + delete pMes; + return aRet; +} + +static NPError l_NPN_RequestRead( NPStream* stream, NPByteRange* rangeList ) +{ + medDebug( 1, "pluginapp: NPN_RequestRead\n" ); + + NPByteRange* pRange = rangeList; + UINT32 nRanges = 0; + while( pRange ) + { + nRanges++; + pRange = pRange->next; + } + + UINT32* pArray = new UINT32[ 2 * nRanges ]; + pRange = rangeList; + UINT32 n = 0; + while( pRange ) + { + pArray[ 2*n ] = (UINT32)pRange->offset; + pArray[ 2*n + 1] = (UINT32)pRange->length; + n++; + pRange = pRange->next; + } + UINT32 nFileID = pConnector->GetStreamID( stream ); + MediatorMessage* pMes = pConnector-> + Transact( eNPN_RequestRead, + &nFileID, sizeof( nFileID ), + &nRanges, sizeof( nRanges ), + pArray, sizeof( UINT32 ) * 2 * nRanges, + NULL ); + + if( ! pMes ) + return NPERR_GENERIC_ERROR; + + NPError aRet = pConnector->GetNPError( pMes ); + delete [] pArray; + delete pMes; + return aRet; +} + +static void l_NPN_Status( NPP instance, const char* message ) +{ + UINT32 nInstance = pConnector->GetNPPID( instance ); + if( nInstance == PluginConnector::UnknownNPPID ) + return; + + pConnector->Send( eNPN_Status, + &nInstance, sizeof( nInstance ), + POST_STRING( message ), + NULL ); +} + +static const char* l_NPN_UserAgent( NPP instance ) +{ + static char* pAgent = NULL; + + UINT32 nInstance = pConnector->GetNPPID( instance ); + if( nInstance == PluginConnector::UnknownNPPID ) + { + if( instance ) + return "Mozilla 3.0"; + else // e.g. flashplayer calls NPN_UserAgent with NULL + nInstance = 0; + } + + MediatorMessage* pMes = pConnector-> + Transact( eNPN_UserAgent, + &nInstance, sizeof( nInstance ), + NULL ); + + if( ! pMes ) + return pAgent; + + if( pAgent ) + delete [] pAgent; + pAgent = pMes->GetString(); + + delete pMes; + + medDebug( 1, "NPN_UserAgent returns %s\n", pAgent ); + + return pAgent; +} + +#if 0 +static void l_NPN_Version( int* major, int* minor, int* net_major, int* net_minor ) +{ + MediatorMessage* pMes = pConnector-> + Transact( eNPN_Version, + NULL ); + + if( ! pMes ) + return; + + *major = pMes->GetUINT32(); + *minor = pMes->GetUINT32(); + *net_major = pMes->GetUINT32(); + *net_minor = pMes->GetUINT32(); + + medDebug( 1, "pluginapp: NPN_Version: results %d %d, %d %d\n", *major, *minor, *net_major, *net_minor ); + + delete pMes; +} +#endif + +static int32 l_NPN_Write( NPP instance, NPStream* stream, int32 len, void* buffer ) +{ + UINT32 nFileID = pConnector->GetStreamID( stream ); + if( nFileID == PluginConnector::UnknownStreamID ) + return NPERR_GENERIC_ERROR; + UINT32 nInstance = pConnector->GetNPPID( instance ); + if( nInstance == PluginConnector::UnknownNPPID ) + return NPERR_GENERIC_ERROR; + + MediatorMessage* pMes = pConnector-> + Transact( eNPN_Write, + &nInstance, sizeof( nInstance ), + &nFileID, sizeof( nFileID ), + &len, sizeof( len ), + buffer, len, + NULL ); + + if( ! pMes ) + return 0; + + INT32 nRet = pMes->GetUINT32(); + return nRet; +} + +static void l_NPN_ReloadPlugins( NPBool /*reloadPages*/ ) +{ + medDebug( 1, "NPN_ReloadPlugins: SNI\n" ); +} + +static NPError l_NPN_GetValue( NPP, NPNVariable variable, void* value ) +{ + /* + * We want to handle values injected into a NPNVariable which aren't in + * the old enum we build against, but that we know are in the new enum + * we want to support + */ + switch( (int)variable ) + { + case NPNVxDisplay: + *((Display**)value) = pXtAppDisplay; + medDebug( 1, "Display requested\n" ); + break; + case NPNVxtAppContext: + *((XtAppContext*)value) = app_context; + medDebug( 1, "AppContext requested\n" ); + break; + case NPNVjavascriptEnabledBool: + // no javascript + *(NPBool*)value = false; + medDebug( 1, "javascript enabled requested\n" ); + break; + case NPNVasdEnabledBool: + // no SmartUpdate + *(NPBool*)value = false; + medDebug( 1, "smart update enabled requested\n" ); + break; + case NPNVisOfflineBool: + // no offline browsing + *(NPBool*)value = false; + medDebug( 1, "offline browsing requested\n" ); + break; + case NPNVSupportsXEmbedBool: + // asking xembed + *(int*)value = true; + medDebug( 1, "xembed requested\n" ); + break; + case NPNVToolkit: +# ifdef ENABLE_GTK + *(int*)value = NPNVGtk2; +# else + *(int*)value = 0; +# endif + medDebug( 1, "toolkit requested\n" ); + break; + default: + medDebug( 1, "unknown NPNVariable %x requested\n", variable ); + return NPERR_INVALID_PARAM; + } + return NPERR_NO_ERROR; +} + +static NPError l_NPN_SetValue(NPP /*instance*/, NPPVariable variable, void *value) +{ + medDebug( 1, "NPN_SetValue %d=%p\n", variable, value ); + return 0; +} + +static void l_NPN_InvalidateRect(NPP /*instance*/, NPRect* /*invalidRect*/) +{ + medDebug( 1, "NPN_InvalidateRect\n" ); +} + +static void l_NPN_InvalidateRegion(NPP /*instance*/, NPRegion /*invalidRegion*/) +{ + medDebug( 1, "NPN_InvalidateRegion\n" ); +} + +static void l_NPN_ForceRedraw(NPP /*instance*/) +{ + medDebug( 1, "NPN_ForceRedraw\n" ); +} + +} + +static NPNetscapeFuncs aNetscapeFuncs = +{ + sizeof(aNetscapeFuncs), + (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR, + l_NPN_GetURL, + l_NPN_PostURL, + l_NPN_RequestRead, + l_NPN_NewStream, + l_NPN_Write, + l_NPN_DestroyStream, + l_NPN_Status, + l_NPN_UserAgent, + l_NPN_MemAlloc, + l_NPN_MemFree, + l_NPN_MemFlush, + l_NPN_ReloadPlugins, +# ifdef OJI + l_NPN_GetJavaEnv, + l_NPN_GetJavaPeer, +# else + NULL, + NULL, +# endif + l_NPN_GetURLNotify, + l_NPN_PostURLNotify, + l_NPN_GetValue, + l_NPN_SetValue, + l_NPN_InvalidateRect, + l_NPN_InvalidateRegion, + l_NPN_ForceRedraw +}; + +static NPPluginFuncs aPluginFuncs = +{ + sizeof(aPluginFuncs), + (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + + +oslModule pPluginLib = NULL; +char*(*pNPP_GetMIMEDescription)() = NULL; +NPError (*pNP_Initialize)(NPNetscapeFuncs*,NPPluginFuncs*) = NULL; +NPError (*pNP_Shutdown)() = NULL; + +std::vector< PluginConnector* > PluginConnector::allConnectors; + +PluginConnector::PluginConnector( int nSocket ) : + Mediator( nSocket ) +{ + SetNewMessageHdl( LINK( this, PluginConnector, NewMessageHdl ) ); +} + +PluginConnector::~PluginConnector() +{ +} + +IMPL_LINK( PluginConnector, WorkOnNewMessageHdl, Mediator*, /*pMediator*/ ) +{ + MediatorMessage* pMessage; + CommandAtoms nCommand; + while( (pMessage = GetNextMessage( FALSE )) ) + { + nCommand = (CommandAtoms)pMessage->GetUINT32(); + medDebug( 1, "pluginapp: %s\n", GetCommandName( nCommand ) ); + switch( nCommand ) + { + case eNPP_DestroyStream: + { + UINT32 nInstance = pMessage->GetUINT32(); + NPP instance = m_aInstances[ nInstance ]->instance; + UINT32 nFileID = pMessage->GetUINT32(); + NPStream* pStream = m_aNPWrapStreams[ nFileID ]; + NPError aReason = GetNPError( pMessage ); + m_aNPWrapStreams.erase( m_aNPWrapStreams.begin() + nFileID ); + + aReason = aPluginFuncs.destroystream( instance, pStream, aReason ); + Respond( pMessage->m_nID, + (char*)&aReason, sizeof( aReason ), + NULL ); + + delete [] pStream->url; + delete pStream; + } + break; + case eNPP_Destroy: + { + UINT32 nInstance = pMessage->GetUINT32(); + ConnectorInstance* pInst= m_aInstances[ nInstance ]; + + // some plugin rely on old netscapes behaviour + // to first destroy the widget and then destroy + // the instance, so mimic that behaviour here + if( pInst->pShell ) + XtDestroyWidget( (Widget)pInst->pShell ); + + pInst->pWidget = pInst->pShell = NULL; + + // the other side will call eNPP_DestroyPhase2 after this + NPError aReason = NPERR_NO_ERROR; + Respond( pMessage->m_nID, (char*)&aReason, sizeof( aReason ), NULL ); + } + break; + case eNPP_DestroyPhase2: + { + // now really destroy the instance + UINT32 nInstance = pMessage->GetUINT32(); + ConnectorInstance* pInst= m_aInstances[ nInstance ]; + NPP instance = pInst->instance; + NPSavedData* pSave = NULL; + + NPError aRet = aPluginFuncs.destroy( instance, &pSave ); + if( pSave ) + { + Respond( pMessage->m_nID, + (char*)&aRet, sizeof( aRet ), + pSave->buf, pSave->len, + NULL ); + delete [] (char*)pSave->buf; + } + else + Respond( pMessage->m_nID, + (char*)&aRet, sizeof( aRet ), + "0000", 4, + NULL ); + + #ifdef ENABLE_GTK + if( pInst->pGtkWindow ) + g_object_unref( G_OBJECT(pInst->pGtkWindow) ); + if( pInst->pGtkWidget ) + g_object_unref( G_OBJECT(pInst->pGtkWidget) ); + #endif + + m_aInstances.erase( m_aInstances.begin() + nInstance ); + delete pInst; + delete instance; + medDebug( 1, "destroyed instance (returning %d)\n", aRet ); + } + break; + case eNPP_NewStream: + { + UINT32 nInstance = pMessage->GetUINT32(); + NPP instance = m_aInstances[ nInstance ]->instance; + char* pType = pMessage->GetString(); + NPStream* pStream = new NPStream; + pStream->url = pMessage->GetString(); + pStream->end = pMessage->GetUINT32(); + pStream->lastmodified = pMessage->GetUINT32(); + pStream->pdata = pStream->ndata = pStream->notifyData = NULL; + NPBool* pSeekable = (NPBool*)pMessage->GetBytes(); + m_aNPWrapStreams.push_back( pStream ); + uint16 nStype = NP_ASFILE; + NPError aRet = aPluginFuncs.newstream( instance, pType, pStream, + *pSeekable, &nStype ); + medDebug( 1, "pluginapp: NPP_NewStream( %p, %s, %p, %s, %p ) returns %d\n" + "stream = { pdata = %p, ndata = %p, url = %s, end = %d, lastmodified = %d, notifyData = %p }\n", + instance, pType, pStream, *pSeekable ? "seekable" : "not seekable", &nStype, (int)aRet, + pStream->pdata, pStream->ndata, pStream->url, pStream->end, pStream->lastmodified, pStream->notifyData ); + Respond( pMessage->m_nID, + (char*)&aRet, sizeof( aRet ), + &nStype, sizeof( nStype ), + NULL ); + delete [] pType; + delete [] pSeekable; + } + break; + case eNPP_New: + { + char* pType = pMessage->GetString(); + uint16* pMode = (uint16*)pMessage->GetBytes(); + int16* pArgc = (int16*)pMessage->GetBytes(); + NPP instance = new NPP_t; + instance->pdata = instance->ndata = NULL; + ULONG nArgnBytes, nArgvBytes; + char* pArgn = (char*)pMessage->GetBytes( nArgnBytes ); + char* pArgv = (char*)pMessage->GetBytes( nArgvBytes ); + ULONG nSaveBytes; + char* pSavedData = (char*)pMessage->GetBytes( nSaveBytes ); + ConnectorInstance* pInst = + new ConnectorInstance( instance, pType, + *pArgc, + pArgn, nArgnBytes, + pArgv, nArgvBytes, + pSavedData, nSaveBytes ); + m_aInstances.push_back( pInst ); + NPError aRet; + aRet = aPluginFuncs.newp( pInst->pMimeType, instance, *pMode, *pArgc, + pInst->nArg ? pInst->argn : NULL, + pInst->nArg ? pInst->argv : NULL, + ( nSaveBytes == 4 && *(UINT32*)pSavedData == 0 ) ? + &(pInst->aData) : NULL ); + medDebug( 1, "pluginapp: NPP_New( %s, %p, %d, %d, %p, %p, %p ) returns %d\n", + pInst->pMimeType, + instance, *pMode, pInst->nArg, pInst->argn, pInst->argv, &pInst->aData, + (int) aRet ); +#if OSL_DEBUG_LEVEL > 1 + for( int i = 0; i < pInst->nArg; i++ ) + medDebug( 1, " \"%s\"=\"%s\"\n", pInst->argn[i], pInst->argv[i] ); +#endif + + #ifdef ENABLE_GTK + // check if XEMBED is to be used + // ask for Bool. there seems to be no clear definition whether the + // return value should be an int or unsigned char + // int can hold both and will be nonzero in case of "true" + if( aPluginFuncs.getvalue ) + { + int bNeedsXEmbed = 0; + NPError error = aPluginFuncs.getvalue( instance, NPPVpluginNeedsXEmbed, (void *)&bNeedsXEmbed ); + if( error == NPERR_NO_ERROR ) + pInst->bShouldUseXEmbed = (bNeedsXEmbed != 0); + medDebug( 1, "should use xembed = %s\n", pInst->bShouldUseXEmbed ? "true" : "false" ); + } + #endif + + Respond( pMessage->m_nID, + (char*)&aRet, sizeof( aRet ), + NULL ); + delete [] pMode; + delete [] pArgc; + delete [] pType; + } + break; + case eNPP_SetWindow: + { + UINT32 nInstance = pMessage->GetUINT32(); + ConnectorInstance* pInst= m_aInstances[ nInstance ]; + NPWindow* pWindow = (NPWindow*)pMessage->GetBytes(); + + if( pWindow->width < 1 ) + pWindow->width = 1; + if( pWindow->height < 1 ) + pWindow->height = 1; + + #ifdef ENABLE_GTK + if( pInst->bShouldUseXEmbed ) + { + if( ! pInst->pGtkWidget ) + { + medDebug( 1, "creating gtk plug and socket\n" ); + + pInst->pGtkWindow = gtk_plug_new((GdkNativeWindow)reinterpret_cast<sal_uIntPtr>(pWindow->window)); + gtk_widget_show( pInst->pGtkWindow ); + pInst->pGtkWidget = gtk_socket_new(); + gtk_widget_show( pInst->pGtkWidget ); + gtk_container_add( GTK_CONTAINER(pInst->pGtkWindow), pInst->pGtkWidget ); + gtk_widget_show_all( pInst->pGtkWindow ); + pInst->window.window = (void *)gtk_socket_get_id( GTK_SOCKET(pInst->pGtkWidget ) ); + + XSync( pAppDisplay, False ); + + XMapWindow( pAppDisplay, GDK_WINDOW_XWINDOW(pInst->pGtkWindow->window) ); + + XSync( pAppDisplay, False ); + } + + // update widget size; alas out parent is not yet really XEMBED conformant + gtk_widget_set_size_request( pInst->pGtkWidget, pWindow->width, pWindow->height ); + gtk_window_resize( GTK_WINDOW(pInst->pGtkWindow), pWindow->width, pWindow->height ); + + GdkScreen* pGdkScreen = gtk_widget_get_screen( pInst->pGtkWidget ); + Screen* pScreen = ScreenOfDisplay( pAppDisplay, gdk_screen_get_number( pGdkScreen ) ); + + pInst->window.x = 0; + pInst->window.y = 0; + pInst->window.width = pWindow->width; + pInst->window.height = pWindow->height; + pInst->window.clipRect.left = 0; + pInst->window.clipRect.top = 0; + pInst->window.clipRect.right = pWindow->width; + pInst->window.clipRect.bottom = pWindow->height; + pInst->window.ws_info = &pInst->ws_info; + pInst->window.type = NPWindowTypeWindow; + pInst->ws_info.type = NP_SETWINDOW; + pInst->ws_info.display = pAppDisplay; + pInst->ws_info.visual = DefaultVisualOfScreen( pScreen ); + pInst->ws_info.colormap = DefaultColormapOfScreen( pScreen ); + pInst->ws_info.depth = DefaultDepthOfScreen( pScreen ); + } + else + #endif + { + if( ! pInst->pWidget ) + { + pInst->pWidget = CreateNewShell( &(pInst->pShell), (XLIB_Window)pWindow->window ); + } + + // fill in NPWindow and NPCallbackStruct + pInst->window.window = (void*)XtWindow( (Widget)pInst->pWidget ); + pInst->window.x = 0; + pInst->window.y = 0; + pInst->window.width = pWindow->width; + pInst->window.height = pWindow->height; + pInst->window.clipRect.left = 0; + pInst->window.clipRect.top = 0; + pInst->window.clipRect.right = pWindow->width; + pInst->window.clipRect.bottom = pWindow->height; + pInst->window.ws_info = &pInst->ws_info; + pInst->window.type = NPWindowTypeWindow; + pInst->ws_info.type = NP_SETWINDOW; + pInst->ws_info.display = XtDisplay( (Widget)pInst->pWidget ); + pInst->ws_info.visual = DefaultVisualOfScreen( XtScreen( (Widget)pInst->pWidget ) ); + pInst->ws_info.colormap = DefaultColormapOfScreen( XtScreen( (Widget)pInst->pWidget ) ); + pInst->ws_info.depth = DefaultDepthOfScreen( XtScreen( (Widget)pInst->pWidget ) ); + + XtResizeWidget( (Widget)pInst->pShell, + pInst->window.width, + pInst->window.height, + 0 ); + XtResizeWidget( (Widget)pInst->pWidget, + pInst->window.width, + pInst->window.height, + 0 ); + } + + NPError aRet = aPluginFuncs.setwindow( pInst->instance, &pInst->window ); + medDebug( 1, "pluginapp: NPP_SetWindow returns %d\n", (int) aRet ); + Respond( pMessage->m_nID, + (char*)&aRet, sizeof( aRet ), + NULL ); + delete [] (char*)pWindow; + } + break; + case eNPP_StreamAsFile: + { + UINT32 nInstance = pMessage->GetUINT32(); + NPP instance = m_aInstances[ nInstance ]->instance; + UINT32 nFileID = pMessage->GetUINT32(); + NPStream* pStream = m_aNPWrapStreams[ nFileID ]; + char* fname = pMessage->GetString(); + medDebug( 1, "pluginapp: NPP_StreamAsFile %s\n", fname ); + aPluginFuncs.asfile( instance, pStream, fname ); + delete [] fname; + } + break; + case eNPP_URLNotify: + { + UINT32 nInstance = pMessage->GetUINT32(); + NPP instance = m_aInstances[ nInstance ]->instance; + char* url = pMessage->GetString(); + NPReason* pReason = (NPReason*)pMessage->GetBytes(); + void** notifyData = (void**)pMessage->GetBytes(); + aPluginFuncs.urlnotify( instance, url, *pReason, *notifyData ); + delete [] url; + delete [] pReason; + delete [] notifyData; + } + break; + case eNPP_WriteReady: + { + UINT32 nInstance = pMessage->GetUINT32(); + NPP instance = m_aInstances[ nInstance ]->instance; + UINT32 nFileID = pMessage->GetUINT32(); + NPStream* pStream = m_aNPWrapStreams[ nFileID ]; + int32 nRet = aPluginFuncs.writeready( instance, pStream ); + + medDebug( 1, "pluginapp: NPP_WriteReady( %p, %p ) (stream id = %d) returns %d\n", + instance, pStream, nFileID, nRet ); + + Respond( pMessage->m_nID, + (char*)&nRet, sizeof( nRet ), + NULL ); + } + break; + case eNPP_Write: + { + UINT32 nInstance = pMessage->GetUINT32(); + NPP instance = m_aInstances[ nInstance ]->instance; + UINT32 nFileID = pMessage->GetUINT32(); + NPStream* pStream = m_aNPWrapStreams[ nFileID ]; + int32 offset = pMessage->GetUINT32(); + ULONG len; + char* buffer = (char*)pMessage->GetBytes( len ); + int32 nRet = aPluginFuncs.write( instance, pStream, offset, len, buffer ); + + medDebug( 1,"pluginapp: NPP_Write( %p, %p, %d, %d, %p ) returns %d\n" + "stream = { pdata = %p, ndata = %p, url = %s, end = %d, lastmodified = %d, notifyData = %p }\n", + instance, pStream, offset, len, buffer, nRet, + pStream->pdata, pStream->ndata, pStream->url, pStream->end, pStream->lastmodified, pStream->notifyData ); + + Respond( pMessage->m_nID, + (char*)&nRet, sizeof( nRet ), + NULL ); + delete [] buffer; + } + break; + case eNPP_GetMIMEDescription: + { + if( ! pNPP_GetMIMEDescription ) + pNPP_GetMIMEDescription = (char*(*)()) + osl_getAsciiFunctionSymbol( pPluginLib, "NPP_GetMIMEDescription" ); + char* pMIME = pNPP_GetMIMEDescription(); + Respond( pMessage->m_nID, + POST_STRING( pMIME ), + NULL ); + } + break; + case eNPP_Initialize: + { + + pNP_Initialize = + (NPError(*)(NPNetscapeFuncs*, NPPluginFuncs*)) + osl_getAsciiFunctionSymbol( pPluginLib, "NP_Initialize" ); + medDebug( !pNP_Initialize, "no NP_Initialize, %s\n", dlerror() ); + pNP_Shutdown = (NPError(*)()) + osl_getAsciiFunctionSymbol( pPluginLib, "NP_Shutdown" ); + medDebug( !pNP_Initialize, "no NP_Shutdown, %s\n", dlerror() ); + + medDebug( 1, "entering NP_Initialize\n" ); + NPError aRet = pNP_Initialize( &aNetscapeFuncs, &aPluginFuncs ); + medDebug( 1, "pluginapp: NP_Initialize returns %d\n", (int) aRet ); + Respond( pMessage->m_nID, (char*)&aRet, sizeof( aRet ), NULL ); + } + break; + case eNPP_Shutdown: + { + write( wakeup_fd[1], "xxxx", 4 ); + } + break; + default: + medDebug( 1, "caught unknown NPP request %d\n", nCommand ); + break; + } + delete pMessage; + } + return 0; +} + +void LoadAdditionalLibs( const char* _pPluginLib ) +{ + medDebug( 1, "LoadAdditionalLibs %s\n", _pPluginLib ); + + if( ! strncmp( _pPluginLib, "libflashplayer.so", 17 ) ) + { + /* #b4951312# flash 7 implicitly assumes a gtk application + * if the API version is greater or equal to 12 (probably + * because they think they run in mozilla then). In that + * case they try to find gtk within the process and crash + * when they don't find it. + */ + aNetscapeFuncs.version = 11; + aPluginFuncs.version = 11; + } +} + diff --git a/extensions/source/plugin/unx/nppapi.cxx b/extensions/source/plugin/unx/nppapi.cxx new file mode 100644 index 000000000000..6dc1b7a30407 --- /dev/null +++ b/extensions/source/plugin/unx/nppapi.cxx @@ -0,0 +1,602 @@ +/************************************************************************* + + Source Code Control System - Header + + $Header: /zpool/svn/migration/cvs_rep_09_09_08/code/extensions/source/plugin/unx/nppapi.cxx,v 1.7 2008-01-14 14:53:38 ihi Exp $ + +*************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_extensions.hxx" + +#if STLPORT_VERSION>=321 +#include <cstdarg> +#endif + +#include <plugin/impl.hxx> +#include <vcl/svapp.hxx> + +std::vector<PluginConnector*> PluginConnector::allConnectors; + +PluginConnector::PluginConnector( int nSocket ) : + Mediator( nSocket ) +{ + allConnectors.push_back( this ); + SetNewMessageHdl( LINK( this, PluginConnector, NewMessageHdl ) ); +} + +PluginConnector::~PluginConnector() +{ + NAMESPACE_VOS(OGuard) aGuard( m_aUserEventMutex ); + for( std::vector< PluginConnector* >::iterator it = allConnectors.begin(); + it != allConnectors.end(); ++it ) + { + if( *it == this ) + { + allConnectors.erase( it ); + break; + } + } +} + +IMPL_LINK( PluginConnector, NewMessageHdl, Mediator*, /*pMediator*/ ) +{ + NAMESPACE_VOS(OGuard) aGuard( m_aUserEventMutex ); + bool bFound = false; + for( std::vector< PluginConnector* >::iterator it = allConnectors.begin(); + it != allConnectors.end() && bFound == false; ++it ) + { + if( *it == this ) + bFound = true; + } + if( ! bFound ) + return 0; + Application::PostUserEvent( LINK( this, PluginConnector, WorkOnNewMessageHdl ) ); + return 0; +} + +IMPL_LINK( PluginConnector, WorkOnNewMessageHdl, Mediator*, /*pMediator*/ ) +{ + bool bFound = false; + for( std::vector< PluginConnector* >::iterator it = allConnectors.begin(); + it != allConnectors.end() && bFound == false; ++it ) + { + if( *it == this ) + bFound = true; + } + if( ! bFound ) + return 0; +/* + { + NAMESPACE_VOS(OGuard) aGuard( m_aUserEventMutex ); + m_aUserEventIDs.pop_front(); + } +*/ + + MediatorMessage* pMessage; + CommandAtoms nCommand; + while( (pMessage = GetNextMessage( FALSE )) ) + { + nCommand = (CommandAtoms)pMessage->GetUINT32(); + medDebug( 1, "%s\n", GetCommandName( nCommand ) ); + switch( nCommand ) + { + case eNPN_GetURL: + { + UINT32 nInstance = pMessage->GetUINT32(); + NPP instance = m_aInstances[ nInstance ]->instance; + char* pUrl = pMessage->GetString(); + char* pWindow = pMessage->GetString(); + NPError aRet = NPN_GetURL( instance, pUrl, pWindow ); + Respond( pMessage->m_nID, + (char*)(&aRet), sizeof( NPError ), NULL ); + delete [] pUrl; + delete [] pWindow; + } + break; + case eNPN_GetURLNotify: + { + UINT32 nInstance = pMessage->GetUINT32(); + NPP instance = m_aInstances[ nInstance ]->instance; + char* pUrl = pMessage->GetString(); + char* pWindow = pMessage->GetString(); + void** pNotifyData = (void**)pMessage->GetBytes(); + NPError aRet = NPN_GetURLNotify( instance, pUrl, pWindow, + *pNotifyData ); + Respond( pMessage->m_nID, + (char*)(&aRet), sizeof( NPError ), NULL ); + delete [] pUrl; + delete [] pWindow; + delete [] pNotifyData; + } + break; + case eNPN_DestroyStream: + { + UINT32 nInstance = pMessage->GetUINT32(); + NPP instance = m_aInstances[ nInstance ]->instance; + UINT32 nFileID = pMessage->GetUINT32(); + char* pUrl = pMessage->GetString(); + NPError* pReason = (NPError*)pMessage->GetBytes(); + NPError aRet = NPERR_FILE_NOT_FOUND; + if( nFileID < static_cast<UINT32>(m_aNPWrapStreams.size()) ) + { + if( ! strcmp( m_aNPWrapStreams[ nFileID ]->url, pUrl ) ) + { + aRet = + NPN_DestroyStream( instance, m_aNPWrapStreams[ nFileID ], + *pReason ); + m_aNPWrapStreams.erase( m_aNPWrapStreams.begin() + nFileID ); + } + else + medDebug( 1, "StreamID %d has incoherent urls %s and %s\n", + nFileID, pUrl, m_aNPWrapStreams[ nFileID ]->url ); + } + else + medDebug( 1, "Nonexistent StreamID %d\n", nFileID ); + + Respond( pMessage->m_nID, + (char*)(&aRet), sizeof( NPError ), NULL ); + + delete [] pUrl; + delete [] pReason; + } + break; + case eNPN_NewStream: + { + UINT32 nInstance = pMessage->GetUINT32(); + NPP instance = m_aInstances[ nInstance ]->instance; + NPMIMEType pType = pMessage->GetString(); + char* pTarget = pMessage->GetString(); + + NPStream* pStream = NULL; + + NPError aRet = NPN_NewStream( instance, pType, pTarget, &pStream ); + + if( aRet != NPERR_NO_ERROR ) + { + UINT32 nDummy = 0; + Respond( pMessage->m_nID, + (char*)&aRet, sizeof( aRet ), + "", 0, + &nDummy, sizeof(UINT32), + &nDummy, sizeof(UINT32), + NULL ); + } + else + { + m_aNPWrapStreams.push_back( pStream ); + + ULONG nLen = strlen( pStream->url ); + Respond( pMessage->m_nID, + (char*)&aRet, sizeof( aRet ), + pStream->url, nLen, + &pStream->end, sizeof(UINT32), + &pStream->lastmodified, sizeof(UINT32), + NULL ); + } + + delete [] pTarget; + delete [] pType; + } + break; + case eNPN_PostURLNotify: + { + UINT32 nInstance = pMessage->GetUINT32(); + NPP instance = m_aInstances[ nInstance ]->instance; + char* pUrl = pMessage->GetString(); + char* pTarget = pMessage->GetString(); + UINT32 nLen = pMessage->GetUINT32(); + char* pBuf = (char*)pMessage->GetBytes(); + NPBool* pFile = (NPBool*)pMessage->GetBytes(); + void** pNData = (void**)pMessage->GetBytes(); + NPError aRet = + NPN_PostURLNotify( instance, pUrl, pTarget, nLen, pBuf, *pFile, *pNData ); + Respond( pMessage->m_nID, (char*)&aRet, sizeof( aRet ), NULL ); + delete [] pUrl; + delete [] pTarget; + delete [] pBuf; + delete [] pFile; + delete [] pNData; + } + break; + case eNPN_PostURL: + { + UINT32 nInstance = pMessage->GetUINT32(); + NPP instance = m_aInstances[ nInstance ]->instance; + char* pUrl = pMessage->GetString(); + char* pWindow = pMessage->GetString(); + UINT32 nLen = pMessage->GetUINT32(); + char* pBuf = (char*)pMessage->GetBytes(); + NPBool* pFile = (NPBool*)pMessage->GetBytes(); + NPError aRet = + NPN_PostURL( instance, pUrl, pWindow, nLen, pBuf, *pFile ); + Respond( pMessage->m_nID, (char*)&aRet, sizeof( aRet ), NULL ); + delete [] pUrl; + delete [] pWindow; + delete [] pBuf; + delete [] pFile; + } + break; + case eNPN_RequestRead: + { + UINT32 nFileID = pMessage->GetUINT32(); + NPStream* pStream = m_aNPWrapStreams[ nFileID ]; + UINT32 nRanges = pMessage->GetUINT32(); + UINT32* pArray = (UINT32*)pMessage->GetBytes(); + // build ranges table + NPByteRange* pFirst = new NPByteRange; + NPByteRange* pRun = pFirst; + for( UINT32 n = 0; n < nRanges; n++ ) + { + pRun->offset = pArray[ 2*n ]; + pRun->length = pArray[ 2*n+1 ]; + pRun->next = n < nRanges-1 ? new NPByteRange : NULL; + pRun = pRun->next; + } + NPError aRet = NPN_RequestRead( pStream, pFirst ); + Respond( pMessage->m_nID, (char*)&aRet, sizeof( aRet ), NULL ); + while( pFirst ) + { + pRun = pFirst->next; + delete pFirst; + pFirst = pRun; + } + delete [] pArray; + } + break; + case eNPN_Status: + { + UINT32 nInstance = pMessage->GetUINT32(); + NPP instance = m_aInstances[ nInstance ]->instance; + char* pString = pMessage->GetString(); + NPN_Status( instance, pString ); + delete [] pString; + } + break; + case eNPN_Version: + { + int major, minor, net_major, net_minor; + NPN_Version( &major, &minor, &net_major, &net_minor ); + Respond( pMessage->m_nID, + (char*)&major, sizeof( int ), + &minor, sizeof( int ), + &net_major, sizeof( int ), + &net_minor, sizeof( int ), + NULL ); + } + break; + case eNPN_Write: + { + UINT32 nInstance = pMessage->GetUINT32(); + NPP instance = m_aInstances[ nInstance ]->instance; + UINT32 nFileID = pMessage->GetUINT32(); + NPStream* pStream = m_aNPWrapStreams[ nFileID ]; + INT32 nLen = pMessage->GetUINT32(); + void* pBuffer = pMessage->GetBytes(); + INT32 nRet = NPN_Write( instance, pStream, nLen, pBuffer ); + Respond( pMessage->m_nID, + (char*)&nRet, sizeof( nRet ), + NULL ); + delete [] (char*)pBuffer; + delete instance; + } + break; + case eNPN_UserAgent: + { + UINT32 nInstance = pMessage->GetUINT32(); + NPP instance = m_aInstances[ nInstance ]->instance; + const char* pAnswer = NPN_UserAgent( instance ); + Respond( pMessage->m_nID, + (char*)pAnswer, strlen( pAnswer ), + NULL ); + } + break; + default: + medDebug( 1, "caught unknown NPN request %d\n", nCommand ); + } + + delete pMessage; + } + return 0; +} + +#define GET_INSTANCE() \ + UINT32 nInstance; \ + nInstance = GetNPPID( instance ); + +#define GET_INSTANCE_RET( err ) \ + GET_INSTANCE() \ + if( nInstance == PluginConnector::UnknownNPPID ) \ + return err + + +#define POST_INSTANCE() (char*)&nInstance, sizeof( nInstance ) + +NPError UnxPluginComm::NPP_Destroy( NPP instance, NPSavedData** save ) +{ + NPError aRet = NPERR_GENERIC_ERROR; + GET_INSTANCE_RET( aRet ); + MediatorMessage* pMes = + Transact( eNPP_Destroy, + POST_INSTANCE(), + NULL ); + if( ! pMes ) + return NPERR_GENERIC_ERROR; + delete pMes; + + pMes = Transact( eNPP_DestroyPhase2, + POST_INSTANCE(), + NULL ); + if( ! pMes ) + return NPERR_GENERIC_ERROR; + + aRet = GetNPError( pMes ); + ULONG nSaveBytes; + void* pSaveData = pMes->GetBytes( nSaveBytes ); + if( nSaveBytes == 4 && *(UINT32*)pSaveData == 0 ) + *save = NULL; + else + { + *save = new NPSavedData; + (*save)->len = nSaveBytes; + (*save)->buf = pSaveData; + } + delete pMes; + + return aRet; +} + +NPError UnxPluginComm::NPP_DestroyStream( NPP instance, NPStream* stream, NPError reason ) +{ + NPError aRet = NPERR_GENERIC_ERROR; + GET_INSTANCE_RET( aRet ); + UINT32 nFileID = GetStreamID( stream ); + if( nFileID == PluginConnector::UnknownStreamID ) + return NPERR_GENERIC_ERROR; + + MediatorMessage* pMes = + Transact( eNPP_DestroyStream, + POST_INSTANCE(), + &nFileID, sizeof( nFileID ), + &reason, sizeof( reason ), + NULL ); + m_aNPWrapStreams.erase( m_aNPWrapStreams.begin() + nFileID ); + if( ! pMes ) + return NPERR_GENERIC_ERROR; + + aRet = GetNPError( pMes ); + delete pMes; + return aRet; +} + +void* UnxPluginComm::NPP_GetJavaClass() +{ + return NULL; +} + +NPError UnxPluginComm::NPP_Initialize() +{ + MediatorMessage* pMes = + Transact( eNPP_Initialize, + NULL ); + if( ! pMes ) + return NPERR_GENERIC_ERROR; + + NPError aRet = GetNPError( pMes ); + delete pMes; + return aRet; +} + +NPError UnxPluginComm::NPP_New( NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, + char* argn[], char* argv[], NPSavedData *saved ) +{ + m_aInstances.push_back( + new ConnectorInstance( instance, pluginType, 0, + NULL, 0, NULL, 0, + saved ? (char*)saved->buf : NULL, + saved ? saved->len : 0 ) ); + + char *pArgnBuf, *pArgvBuf; + size_t nArgnLen = 0, nArgvLen = 0; + int i; + for( i = 0; i < argc; i++ ) + { + nArgnLen += strlen( argn[i] ) +1; + nArgvLen += strlen( argv[i] ) +1; + } + pArgnBuf = new char[ nArgnLen ]; + pArgvBuf = new char[ nArgvLen ]; + char* pRunArgn = pArgnBuf; + char* pRunArgv = pArgvBuf; + for( i = 0; i < argc; i++ ) + { + strcpy( pRunArgn, argn[i] ); + strcpy( pRunArgv, argv[i] ); + pRunArgn += strlen( argn[i] ) +1; + pRunArgv += strlen( argv[i] ) +1; + } + + MediatorMessage* pMes; + if( saved ) + pMes = + Transact( eNPP_New, + pluginType, strlen( pluginType ), + &mode, sizeof( mode ), + &argc, sizeof( argc ), + pArgnBuf, nArgnLen, + pArgvBuf, nArgvLen, + saved->buf, static_cast<size_t>(saved->len), + NULL ); + else + pMes = + Transact( eNPP_New, + pluginType, strlen( pluginType ), + &mode, sizeof( mode ), + &argc, sizeof( argc ), + pArgnBuf, nArgnLen, + pArgvBuf, nArgvLen, + "0000", size_t(4), + NULL ); + delete [] pArgnBuf; + delete [] pArgvBuf; + if( ! pMes ) + return NPERR_GENERIC_ERROR; + + NPError aRet = GetNPError( pMes ); + delete pMes; + + return aRet; +} + +NPError UnxPluginComm::NPP_NewStream( NPP instance, NPMIMEType type, NPStream* stream, + NPBool seekable, uint16* stype ) +{ + NPError aRet = NPERR_GENERIC_ERROR; + GET_INSTANCE_RET( aRet ); + + m_aNPWrapStreams.push_back( stream ); + MediatorMessage* pMes = + Transact( eNPP_NewStream, + POST_INSTANCE(), + type, strlen( type ), + stream->url, strlen( stream->url ), + &stream->end, sizeof( stream->end ), + &stream->lastmodified, sizeof( stream->lastmodified ), + &seekable, sizeof( seekable ), + NULL ); + + if( ! pMes ) + return NPERR_GENERIC_ERROR; + + aRet = GetNPError( pMes ); + uint16* pSType = (uint16*)pMes->GetBytes(); + *stype = *pSType; + + delete [] pSType; + delete pMes; + return aRet; +} + +void UnxPluginComm::NPP_Print( NPP /*instance*/, NPPrint* /*platformPrint*/ ) +{ +} + +NPError UnxPluginComm::NPP_SetWindow( NPP instance, NPWindow* window ) +{ + NPError aRet = NPERR_GENERIC_ERROR; + GET_INSTANCE_RET( aRet ); + + MediatorMessage* pMes = + Transact( eNPP_SetWindow, + POST_INSTANCE(), + window, sizeof( NPWindow ), + NULL ); + if( ! pMes ) + return NPERR_GENERIC_ERROR; + + aRet = GetNPError( pMes ); + delete pMes; + return aRet; +} + +void UnxPluginComm::NPP_Shutdown() +{ + Send( eNPP_Shutdown, NULL ); +} + +void UnxPluginComm::NPP_StreamAsFile( NPP instance, NPStream* stream, const char* fname ) +{ + GET_INSTANCE(); + UINT32 nFileID = GetStreamID( stream ); + if( nFileID == PluginConnector::UnknownStreamID ) + return; + + Send( eNPP_StreamAsFile, + POST_INSTANCE(), + &nFileID, sizeof( nFileID ), + fname, strlen( fname ), + NULL ); +} + +void UnxPluginComm::NPP_URLNotify( NPP instance, const char* url, NPReason reason, void* notifyData ) +{ + GET_INSTANCE(); + + Send( eNPP_URLNotify, + POST_INSTANCE(), + url, strlen( url ), + &reason, sizeof( reason ), + ¬ifyData, sizeof( void* ), + NULL ); +} + +int32 UnxPluginComm::NPP_Write( NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer ) +{ + GET_INSTANCE_RET( -1 ); + UINT32 nFileID = GetStreamID( stream ); + if( nFileID == PluginConnector::UnknownStreamID ) + return -1; + + MediatorMessage* pMes = + Transact( eNPP_Write, + POST_INSTANCE(), + &nFileID, sizeof( nFileID ), + &offset, sizeof( offset ), + buffer, static_cast<size_t>(len), + NULL ); + if( ! pMes ) + return 0; + + int32 aRet = pMes->GetUINT32(); + delete pMes; + + return aRet; +} + +int32 UnxPluginComm::NPP_WriteReady( NPP instance, NPStream* stream ) +{ + GET_INSTANCE_RET( -1 ); + UINT32 nFileID = GetStreamID( stream ); + if( nFileID == PluginConnector::UnknownStreamID ) + return -1; + + MediatorMessage* pMes = + Transact( eNPP_WriteReady, + POST_INSTANCE(), + &nFileID, sizeof( nFileID ), + NULL ); + + if( ! pMes ) + return 0; + + int32 aRet = pMes->GetUINT32(); + delete pMes; + + return aRet; +} + +char* UnxPluginComm::NPP_GetMIMEDescription() +{ + static char* pDesc = NULL; + MediatorMessage* pMes = + Transact( eNPP_GetMIMEDescription, + NULL ); + if( ! pMes ) + return (char*)""; + + if( pDesc ) + delete [] pDesc; + pDesc = pMes->GetString(); + delete pMes; + return pDesc; +} + +NPError UnxPluginComm::NPP_GetValue( NPP /*instance*/, NPPVariable /*variable*/, void* /*value*/ ) +{ + return 0; +} + +NPError UnxPluginComm::NPP_SetValue( NPP /*instance*/, NPNVariable /*variable*/, void* /*value*/ ) +{ + return 0; +} diff --git a/extensions/source/plugin/unx/npwrap.cxx b/extensions/source/plugin/unx/npwrap.cxx new file mode 100644 index 000000000000..1be5f9f42a03 --- /dev/null +++ b/extensions/source/plugin/unx/npwrap.cxx @@ -0,0 +1,513 @@ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_extensions.hxx" +#include <errno.h> +#include <dlfcn.h> +#include <unistd.h> +#include <sys/poll.h> +#include <fcntl.h> +#include <signal.h> + +#include <plugin/unx/plugcon.hxx> + +#include <osl/file.h> +#include <osl/module.h> + +PluginConnector* pConnector = NULL; + +int nAppArguments = 0; +char** pAppArguments = NULL; +Display* pAppDisplay = NULL; +Display* pXtAppDisplay = NULL; + +extern oslModule pPluginLib; +extern NPError (*pNP_Shutdown)(); + +void LoadAdditionalLibs(const char*); + +XtAppContext app_context; +Widget topLevel = NULL, topBox = NULL; +int wakeup_fd[2] = { 0, 0 }; +static bool bPluginAppQuit = false; + +static long GlobalConnectionLostHdl( void* /*pInst*/, void* /*pArg*/ ) +{ + medDebug( 1, "pluginapp exiting due to connection lost\n" ); + + write( wakeup_fd[1], "xxxx", 4 ); + return 0; +} + +extern "C" +{ + static int plugin_x_error_handler( Display*, XErrorEvent* ) + { + return 0; + } + + #ifndef ENABLE_GTK + static void ThreadEventHandler( XtPointer /*client_data*/, int* /*source*/, XtInputId* id ) + { + char buf[256]; + // clear pipe + int len, nLast = -1; + + while( (len = read( wakeup_fd[0], buf, sizeof( buf ) ) ) > 0 ) + nLast = len-1; + if( ! bPluginAppQuit ) + { + if( ( nLast == -1 || buf[nLast] != 'x' ) && pConnector ) + pConnector->CallWorkHandler(); + else + { + // it seems you can use XtRemoveInput only + // safely from within the callback + // why is that ? + medDebug( 1, "removing wakeup pipe\n" ); + XtRemoveInput( *id ); + XtAppSetExitFlag( app_context ); + bPluginAppQuit = true; + + delete pConnector; + pConnector = NULL; + } + } + } + #endif +} + + +IMPL_LINK( PluginConnector, NewMessageHdl, Mediator*, /*pMediator*/ ) +{ + medDebug( 1, "new message handler\n" ); + write( wakeup_fd[1], "cccc", 4 ); + return 0; + +} + +Widget createSubWidget( char* /*pPluginText*/, Widget shell, XLIB_Window aParentWindow ) +{ + Widget newWidget = XtVaCreateManagedWidget( +#if defined USE_MOTIF + "drawingArea", + xmDrawingAreaWidgetClass, +#else + "", +# if defined DISABLE_XAW + compositeWidgetClass, +# else + labelWidgetClass, +# endif +#endif + shell, + XtNwidth, 200, + XtNheight, 200, + (char *)NULL ); + XtRealizeWidget( shell ); + XtRealizeWidget( newWidget ); + + medDebug( 1, "Reparenting new widget %x to %x\n", XtWindow( newWidget ), aParentWindow ); + XReparentWindow( pXtAppDisplay, + XtWindow( shell ), + aParentWindow, + 0, 0 ); + XtMapWidget( shell ); + XtMapWidget( newWidget ); + XRaiseWindow( pXtAppDisplay, XtWindow( shell ) ); + XSync( pXtAppDisplay, False ); + + return newWidget; +} + +void* CreateNewShell( void** pShellReturn, XLIB_Window aParentWindow ) +{ + XLIB_String n, c; + XtGetApplicationNameAndClass(pXtAppDisplay, &n, &c); + + Widget newShell = + XtVaAppCreateShell( "pane", c, + topLevelShellWidgetClass, + pXtAppDisplay, + XtNwidth, 200, + XtNheight, 200, + XtNoverrideRedirect, True, + (char *)NULL ); + *pShellReturn = newShell; + + char pText[1024]; + sprintf( pText, "starting plugin %s ...", pAppArguments[2] ); + + Widget newWidget = createSubWidget( pText, newShell, aParentWindow ); + + return newWidget; +} + +static oslModule LoadModule( const char* pPath ) +{ + ::rtl::OUString sSystemPath( ::rtl::OUString::createFromAscii( pPath ) ); + ::rtl::OUString sFileURL; + osl_getFileURLFromSystemPath( sSystemPath.pData, &sFileURL.pData ); + + oslModule pLib = osl_loadModule( sFileURL.pData, SAL_LOADMODULE_LAZY ); + if( ! pLib ) + { + medDebug( 1, "could not open %s: %s\n", pPath, dlerror() ); + } + return pLib; +} + +// Unix specific implementation +static void CheckPlugin( const char* pPath ) +{ + oslModule pLib = LoadModule( pPath ); + + char*(*pNP_GetMIMEDescription)() = (char*(*)()) + osl_getAsciiFunctionSymbol( pLib, "NP_GetMIMEDescription" ); + if( pNP_GetMIMEDescription ) + printf( "%s\n", pNP_GetMIMEDescription() ); + else + medDebug( 1, "could not get symbol NP_GetMIMEDescription %s\n", dlerror() ); + + osl_unloadModule( pLib ); +} + +#if OSL_DEBUG_LEVEL > 1 && defined LINUX +#include <execinfo.h> +#endif + +extern "C" { + +static void signal_handler( int nSig ) +{ +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "caught signal %d, exiting\n", nSig ); +#ifdef LINUX + void* pStack[64]; + int nStackLevels = backtrace( pStack, sizeof(pStack)/sizeof(pStack[0]) ); + backtrace_symbols_fd( pStack, nStackLevels, STDERR_FILENO ); +#endif +#endif + if( pConnector ) + { + // ensure that a read on the other side will wakeup + delete pConnector; + pConnector = NULL; + } + + _exit(nSig); +} + +#ifdef ENABLE_GTK + +static gboolean noClosure( gpointer ) +{ + return TRUE; +} + +// Xt events +static gboolean prepareXtEvent( GSource*, gint* ) +{ + int nMask = XtAppPending( app_context ); + return (nMask & XtIMAll) != 0; +} + +static gboolean checkXtEvent( GSource* ) +{ + int nMask = XtAppPending( app_context ); + return (nMask & XtIMAll) != 0; +} + +static gboolean dispatchXtEvent( GSource*, GSourceFunc, gpointer ) +{ + XtAppProcessEvent( app_context, XtIMAll ); + return TRUE; +} + +static GSourceFuncs aXtEventFuncs = +{ + prepareXtEvent, + checkXtEvent, + dispatchXtEvent, + NULL, + noClosure, + NULL +}; + +static gboolean pollXtTimerCallback(gpointer) +{ + for(int i = 0; i < 5; i++) + { + if( (XtAppPending(app_context) & (XtIMAll & ~XtIMXEvent)) == 0 ) + break; + XtAppProcessEvent(app_context, XtIMAll & ~XtIMXEvent); + } + return TRUE; +} + +static gboolean prepareWakeupEvent( GSource*, gint* ) +{ + struct pollfd aPoll = { wakeup_fd[0], POLLIN, 0 }; + poll( &aPoll, 1, 0 ); + return (aPoll.revents & POLLIN ) != 0; +} + +static gboolean checkWakeupEvent( GSource* pSource ) +{ + gint nDum = 0; + return prepareWakeupEvent( pSource, &nDum ); +} + +static gboolean dispatchWakeupEvent( GSource*, GSourceFunc, gpointer ) +{ + char buf[256]; + // clear pipe + int len, nLast = -1; + + while( (len = read( wakeup_fd[0], buf, sizeof( buf ) ) ) > 0 ) + nLast = len-1; + if( ( nLast == -1 || buf[nLast] != 'x' ) && pConnector ) + pConnector->CallWorkHandler(); + else + { + XtAppSetExitFlag( app_context ); + bPluginAppQuit = true; + + delete pConnector; + pConnector = NULL; + } + + return TRUE; +} + +static GSourceFuncs aWakeupEventFuncs = { + prepareWakeupEvent, + checkWakeupEvent, + dispatchWakeupEvent, + NULL, + noClosure, + NULL +}; + +#endif // GTK + +} + +int main( int argc, char **argv) +{ + struct sigaction aSigAction; + aSigAction.sa_handler = signal_handler; + sigemptyset( &aSigAction.sa_mask ); + aSigAction.sa_flags = SA_NOCLDSTOP; + sigaction( SIGSEGV, &aSigAction, NULL ); + sigaction( SIGBUS, &aSigAction, NULL ); + sigaction( SIGABRT, &aSigAction, NULL ); + sigaction( SIGTERM, &aSigAction, NULL ); + sigaction( SIGILL, &aSigAction, NULL ); + + int nArg = (argc < 3) ? 1 : 2; + char* pBaseName = argv[nArg] + strlen(argv[nArg]); + while( pBaseName > argv[nArg] && pBaseName[-1] != '/' ) + pBaseName--; + LoadAdditionalLibs( pBaseName ); + + if( argc == 2 ) + { + CheckPlugin(argv[1]); + exit(0); + } + nAppArguments = argc; + pAppArguments = argv; + + XSetErrorHandler( plugin_x_error_handler ); + + if( pipe( wakeup_fd ) ) + { + medDebug( 1, "could not pipe()\n" ); + return 1; + } + // initialize 'wakeup' pipe. + int flags; + + // set close-on-exec descriptor flag. + if ((flags = fcntl (wakeup_fd[0], F_GETFD)) != -1) + { + flags |= FD_CLOEXEC; + fcntl (wakeup_fd[0], F_SETFD, flags); + } + if ((flags = fcntl (wakeup_fd[1], F_GETFD)) != -1) + { + flags |= FD_CLOEXEC; + fcntl (wakeup_fd[1], F_SETFD, flags); + } + + // set non-blocking I/O flag. + if ((flags = fcntl (wakeup_fd[0], F_GETFL)) != -1) + { + flags |= O_NONBLOCK; + fcntl (wakeup_fd[0], F_SETFL, flags); + } + if ((flags = fcntl (wakeup_fd[1], F_GETFL)) != -1) + { + flags |= O_NONBLOCK; + fcntl (wakeup_fd[1], F_SETFL, flags); + } + + pPluginLib = LoadModule( argv[2] ); + if( ! pPluginLib ) + { + exit(255); + } + int nSocket = atol( argv[1] ); + + #ifdef ENABLE_GTK + g_thread_init(NULL); + gtk_init(&argc, &argv); + #endif + + pConnector = new PluginConnector( nSocket ); + pConnector->SetConnectionLostHdl( Link( NULL, GlobalConnectionLostHdl ) ); + + XtSetLanguageProc( NULL, NULL, NULL ); + + XtToolkitInitialize(); + app_context = XtCreateApplicationContext(); + pXtAppDisplay = XtOpenDisplay( app_context, NULL, "SOPlugin", "SOPlugin", NULL, 0, &argc, argv ); + + + #ifdef ENABLE_GTK + // integrate Xt events into GTK event loop + GPollFD aXtPollDesc, aWakeupPollDesc; + + GSource* pXTSource = g_source_new( &aXtEventFuncs, sizeof(GSource) ); + if( !pXTSource ) + { + medDebug( 1, "could not get Xt GSource" ); + return 1; + } + + g_source_set_priority( pXTSource, GDK_PRIORITY_EVENTS ); + g_source_set_can_recurse( pXTSource, TRUE ); + g_source_attach( pXTSource, NULL ); + aXtPollDesc.fd = ConnectionNumber( pXtAppDisplay ); + aXtPollDesc.events = G_IO_IN; + aXtPollDesc.revents = 0; + g_source_add_poll( pXTSource, &aXtPollDesc ); + + gint xt_polling_timer_id = g_timeout_add( 25, pollXtTimerCallback, NULL); + // Initialize wakeup events listener + GSource *pWakeupSource = g_source_new( &aWakeupEventFuncs, sizeof(GSource) ); + if ( pWakeupSource == NULL ) + { + medDebug( 1, "could not get wakeup source" ); + return 1; + } + g_source_set_priority( pWakeupSource, GDK_PRIORITY_EVENTS); + g_source_attach( pWakeupSource, NULL ); + aWakeupPollDesc.fd = wakeup_fd[0]; + aWakeupPollDesc.events = G_IO_IN; + aWakeupPollDesc.revents = 0; + g_source_add_poll( pWakeupSource, &aWakeupPollDesc ); + + pAppDisplay = gdk_x11_display_get_xdisplay( gdk_display_get_default() ); + #else + pAppDisplay = pXtAppDisplay; + XtAppAddInput( app_context, + wakeup_fd[0], + (XtPointer)XtInputReadMask, + ThreadEventHandler, NULL ); + #endif + + // send that we are ready to go + MediatorMessage* pMessage = + pConnector->Transact( "init req", 8, + NULL ); + delete pMessage; + +#if OSL_DEBUG_LEVEL > 3 + int nPID = getpid(); + int nChild = fork(); + if( nChild == 0 ) + { + char pidbuf[16]; + char* pArgs[] = { "xterm", "-sl", "2000", "-sb", "-e", "gdb", "pluginapp.bin", pidbuf, NULL }; + sprintf( pidbuf, "%d", nPID ); + execvp( pArgs[0], pArgs ); + _exit(255); + } + else + sleep( 10 ); +#endif + + /* + * Loop for events. + */ + // for some reason XtAppSetExitFlag won't quit the application + // in ThreadEventHandler most of times; Xt will hang in select + // (hat is in XtAppNextEvent). Have our own mainloop instead + // of XtAppMainLoop + do + { + #ifdef ENABLE_GTK + g_main_context_iteration( NULL, TRUE ); + #else + XtAppProcessEvent( app_context, XtIMAll ); + #endif + } while( ! XtAppGetExitFlag( app_context ) && ! bPluginAppQuit ); + + medDebug( 1, "left plugin app main loop\n" ); + + g_source_remove(xt_polling_timer_id); + + pNP_Shutdown(); + medDebug( 1, "NP_Shutdown done\n" ); + osl_unloadModule( pPluginLib ); + medDebug( 1, "plugin close\n" ); + + close( wakeup_fd[0] ); + close( wakeup_fd[1] ); + + return 0; +} + +#ifdef GCC +extern "C" { + void __pure_virtual() + {} + + void* __builtin_new( int nBytes ) + { return malloc(nBytes); } + void* __builtin_vec_new( int nBytes ) + { return malloc(nBytes); } + void __builtin_delete( char* pMem ) + { free(pMem); } + void __builtin_vec_delete( char* pMem ) + { free(pMem); } +} +#endif + diff --git a/extensions/source/plugin/unx/plugcon.cxx b/extensions/source/plugin/unx/plugcon.cxx new file mode 100644 index 000000000000..74d8fa8bd2da --- /dev/null +++ b/extensions/source/plugin/unx/plugcon.cxx @@ -0,0 +1,282 @@ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_extensions.hxx" +#include <plugin/unx/plugcon.hxx> + +#include <cstdarg> +#include <vector> + +UINT32 PluginConnector::GetStreamID( NPStream* pStream ) +{ + size_t nLen = m_aNPWrapStreams.size(); + for( size_t i = 0; i < nLen; i++ ) + if( m_aNPWrapStreams[ i ] == pStream ) + return static_cast<UINT32>(i); + medDebug( 1, "Error: NPStream has no ID\n" ); + return UnknownStreamID; +} + +UINT32 PluginConnector::GetNPPID( NPP instance ) +{ + size_t nLen = m_aInstances.size(); + for( size_t i=0; i <nLen; i++ ) + if( m_aInstances[ i ]->instance == instance ) + return static_cast<UINT32>(i); + medDebug( 1, "Error: NPP has no ID\n" ); + + return UnknownNPPID; +} + +ConnectorInstance* PluginConnector::getInstance( NPP instance ) +{ + size_t nLen = m_aInstances.size(); + for( size_t i=0; i <nLen; i++ ) + { + ConnectorInstance* pInst = m_aInstances[i]; + if( pInst->instance == instance ) + return pInst; + } + return NULL; +} + +ConnectorInstance* PluginConnector::getInstanceById( UINT32 nInstanceID ) +{ + return nInstanceID < static_cast<UINT32>(m_aInstances.size()) ? m_aInstances[ nInstanceID ] : NULL; +} + +struct PtrStruct +{ + char* pData; + ULONG nBytes; + + PtrStruct( char* i_pData, ULONG i_nBytes ) + : pData( i_pData ), nBytes( i_nBytes ) {} +}; + +ULONG PluginConnector::FillBuffer( char*& rpBuffer, + const char* pFunction, + ULONG nFunctionLen, + va_list ap ) +{ + std::vector< PtrStruct > aList; + aList.reserve( 5 ); + + ULONG nDataSize = nFunctionLen + sizeof( ULONG ); + char* pNext; + + do { + pNext = va_arg( ap, char* ); + if( pNext ) + { + aList.push_back( PtrStruct( pNext, va_arg( ap, ULONG ) ) ); + nDataSize += aList.back().nBytes + sizeof(ULONG); + } + } while( pNext ); + + rpBuffer = new char[ nDataSize ]; + char* pRun = rpBuffer; + memcpy( pRun, &nFunctionLen, sizeof( nFunctionLen ) ); + pRun += sizeof( nFunctionLen ); + memcpy( pRun, pFunction, nFunctionLen ); + pRun += nFunctionLen; + + for( std::vector<PtrStruct>::const_iterator it = aList.begin(); it != aList.end(); ++it ) + { + memcpy( pRun, &it->nBytes, sizeof( ULONG ) ); + pRun += sizeof( ULONG ); + memcpy( pRun, it->pData, it->nBytes ); + pRun += it->nBytes; + } + return nDataSize; +} + +MediatorMessage* PluginConnector::Transact( const char* pFunction, + ULONG nFunctionLen, ... ) +{ + va_list ap; + char* pBuffer; + + va_start( ap, nFunctionLen ); + ULONG nSize = FillBuffer( pBuffer, pFunction, nFunctionLen, ap ); + va_end( ap ); + return TransactMessage( nSize, pBuffer ); +} + +MediatorMessage* PluginConnector::Transact( UINT32 nFunction, ... ) +{ + va_list ap; + char* pBuffer; + + va_start( ap, nFunction ); + ULONG nSize = FillBuffer( pBuffer, (char*)&nFunction, sizeof( nFunction ), ap ); + va_end( ap ); + return TransactMessage( nSize, pBuffer ); +} + +ULONG PluginConnector::Send( UINT32 nFunction, ... ) +{ + va_list ap; + char* pBuffer; + + va_start( ap, nFunction ); + ULONG nSize = FillBuffer( pBuffer, (char*)&nFunction, sizeof( nFunction ), ap ); + va_end( ap ); + return SendMessage( nSize, pBuffer ); +} + +void PluginConnector::Respond( ULONG nID, + char* pFunction, + ULONG nFunctionLen, ... ) +{ + va_list ap; + char* pBuffer; + + va_start( ap, nFunctionLen ); + ULONG nSize = FillBuffer( pBuffer, pFunction, nFunctionLen, ap ); + va_end( ap ); + SendMessage( nSize, pBuffer, nID | ( 1 << 24 ) ); +} + +MediatorMessage* PluginConnector::WaitForAnswer( ULONG nMessageID ) +{ + if( ! m_bValid ) + return NULL; + + nMessageID &= 0x00ffffff; + while( m_pListener ) + { + { + NAMESPACE_VOS(OGuard) aGuard( m_aQueueMutex ); + for( size_t i = 0; i < m_aMessageQueue.size(); i++ ) + { + MediatorMessage* pMessage = m_aMessageQueue[ i ]; + ULONG nID = pMessage->m_nID; + if( ( nID & 0xff000000 ) && + ( ( nID & 0x00ffffff ) == nMessageID ) ) + { + m_aMessageQueue.erase( m_aMessageQueue.begin() + i ); + return pMessage; + } + } + } + if( ! m_aMessageQueue.empty() ) + CallWorkHandler(); + WaitForMessage( 2000 ); + } + return NULL; +} + +ConnectorInstance::ConnectorInstance( NPP inst, char* type, + int args, char* pargnbuf, ULONG nargnbytes, + char* pargvbuf, ULONG nargvbytes, + char* savedata, ULONG savebytes ) : + instance( inst ), + pShell( NULL ), + pWidget( NULL ), + pForm( NULL ), + pGtkWindow( NULL ), + pGtkWidget( NULL ), + bShouldUseXEmbed( false ), + nArg( args ), + pArgnBuf( pargnbuf ), + pArgvBuf( pargvbuf ) +{ + memset( &window, 0, sizeof(window) ); + pMimeType = new char[ strlen( type ) + 1 ]; + strcpy( pMimeType, type ); + aData.len = savebytes; + aData.buf = savedata; + argn = new char*[ nArg ]; + argv = new char*[ nArg ]; + int i; + char* pRun = pArgnBuf; + for( i = 0; i < nArg; i++ ) + { + argn[i] = pRun; + while( *pRun != 0 && (ULONG)(pRun - pArgnBuf) < nargnbytes ) + pRun++; + if( (ULONG)(pRun - pArgnBuf) < nargnbytes ) + pRun++; + } + pRun = pArgvBuf; + for( i = 0; i < nArg; i++ ) + { + argv[i] = pRun; + while( *pRun != 0 && (ULONG)(pRun - pArgvBuf) < nargvbytes ) + pRun++; + if( (ULONG)(pRun - pArgvBuf) < nargvbytes ) + pRun++; + } +} + +ConnectorInstance::~ConnectorInstance() +{ + delete [] pMimeType; + delete [] argn; + delete [] argv; + delete [] pArgnBuf; + delete [] pArgvBuf; + delete [] (char*)aData.buf; +} + +const char* GetCommandName( CommandAtoms eCommand ) +{ + switch( eCommand ) + { + case eNPN_GetURL: return "NPN_GetURL"; + case eNPN_GetURLNotify: return "NPN_GetURLNotify"; + case eNPN_DestroyStream: return "NPN_DestroyStream"; + case eNPN_NewStream: return "NPN_NewStream"; + case eNPN_PostURLNotify: return "NPN_PostURLNotify"; + case eNPN_PostURL: return "NPN_PostURL"; + case eNPN_RequestRead: return "NPN_RequestRead"; + case eNPN_Status: return "NPN_Status"; + case eNPN_Version: return "NPN_Version"; + case eNPN_Write: return "NPN_Write"; + case eNPN_UserAgent: return "NPN_UserAgent"; + + case eNPP_DestroyStream: return "NPP_DestroyStream"; + case eNPP_Destroy: return "NPP_Destroy"; + case eNPP_DestroyPhase2: return "NPP_DestroyPhase2"; + case eNPP_NewStream: return "NPP_NewStream"; + case eNPP_New: return "NPP_New"; + case eNPP_SetWindow: return "NPP_SetWindow"; + case eNPP_StreamAsFile: return "NPP_StreamAsFile"; + case eNPP_URLNotify: return "NPP_URLNotify"; + case eNPP_WriteReady: return "NPP_WriteReady"; + case eNPP_Write: return "NPP_Write"; + case eNPP_GetMIMEDescription: return "NPP_GetMIMEDescription"; + case eNPP_Initialize: return "NPP_Initialize"; + case eNPP_Shutdown: return "NPP_Shutdown"; + + case eMaxCommand: return "eMaxCommand"; + default: return "unknown command"; + } + return NULL; +} diff --git a/extensions/source/plugin/unx/sysplug.cxx b/extensions/source/plugin/unx/sysplug.cxx new file mode 100644 index 000000000000..fbcf9b7497eb --- /dev/null +++ b/extensions/source/plugin/unx/sysplug.cxx @@ -0,0 +1,131 @@ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_extensions.hxx" +#include <cstdarg> + +#include <sys/types.h> +#include <signal.h> +#include <sys/wait.h> +#include <osl/file.hxx> +#include <osl/thread.h> +#include <rtl/bootstrap.hxx> + +#include <plugin/impl.hxx> + +int UnxPluginComm::nConnCounter = 0; + +UnxPluginComm::UnxPluginComm( + const String& /*mimetype*/, + const String& library, + XLIB_Window aParent, + int nDescriptor1, + int nDescriptor2 + ) : + PluginComm( ::rtl::OUStringToOString( library, osl_getThreadTextEncoding() ), false ), + PluginConnector( nDescriptor2 ) +{ + char pDesc[32]; + char pWindow[32]; + sprintf( pWindow, "%d", (int)aParent ); + sprintf( pDesc, "%d", nDescriptor1 ); + ByteString aLib( library, osl_getThreadTextEncoding() ); + rtl::OString path; + if (!getPluginappPath(&path)) { + fprintf( stderr, "cannot construct path to pluginapp.bin\n" ); + m_nCommPID = -1; + return; + } + + char const* pArgs[5]; + pArgs[0] = path.getStr(); + pArgs[1] = pDesc; + pArgs[2] = aLib.GetBuffer(); + pArgs[3] = pWindow; + pArgs[4] = NULL; + +#if OSL_DEBUG_LEVEL > 1 + m_nCommPID = 10; + fprintf( stderr, "Try to launch: %s %s %s %s, descriptors are %d, %d\n", pArgs[0], pArgs[1], pArgs[2], pArgs[3], nDescriptor1, nDescriptor2 ); +#endif + + if( ! ( m_nCommPID = fork() ) ) + { + execvp( pArgs[0], const_cast< char ** >(pArgs) ); + fprintf( stderr, "Error: could not exec %s\n", pArgs[0] ); + _exit(255); + } + + if( m_nCommPID != -1 ) + { + // wait for pluginapp.bin to start up + if( ! WaitForMessage( 5000 ) ) + { + fprintf( stderr, "Timeout on command: %s %s %s %s\n", pArgs[0], pArgs[1], pArgs[2], pArgs[3] ); + invalidate(); + } + else + { + MediatorMessage* pMessage = GetNextMessage( TRUE ); + Respond( pMessage->m_nID, + const_cast<char*>("init ack"),8, + NULL ); + delete pMessage; + NPP_Initialize(); + } + } +} + +UnxPluginComm::~UnxPluginComm() +{ + NPP_Shutdown(); + if( m_nCommPID != -1 && m_nCommPID != 0 ) + { + int status = 16777216; + pid_t nExit = waitpid( m_nCommPID, &status, WUNTRACED ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "child %d (plugin app child %d) exited with status %d\n", nExit, m_nCommPID, WEXITSTATUS(status) ); +#else + (void)nExit; +#endif + } +} + +bool UnxPluginComm::getPluginappPath(rtl::OString * path) { + OSL_ASSERT(path != NULL); + rtl::OUString p( + RTL_CONSTASCII_USTRINGPARAM("$OOO_BASE_DIR/program/pluginapp.bin")); + rtl::Bootstrap::expandMacros(p); + return + (osl::FileBase::getSystemPathFromFileURL(p, p) == + osl::FileBase::E_None) && + p.convertToString( + path, osl_getThreadTextEncoding(), + (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR | + RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)); +} diff --git a/extensions/source/plugin/unx/unxmgr.cxx b/extensions/source/plugin/unx/unxmgr.cxx new file mode 100644 index 000000000000..1a3736e87e0b --- /dev/null +++ b/extensions/source/plugin/unx/unxmgr.cxx @@ -0,0 +1,315 @@ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_extensions.hxx" +#include <cstdarg> +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> +#include <osl/thread.h> +#include <rtl/strbuf.hxx> +#include <tools/appendunixshellword.hxx> + +#include <vcl/svapp.hxx> +#include <plugin/impl.hxx> + +using namespace rtl; +using namespace std; +using namespace com::sun::star::uno; +using namespace com::sun::star::plugin; + +// Unix specific implementation +static bool CheckPlugin( const ByteString& rPath, list< PluginDescription* >& rDescriptions ) +{ +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "Trying plugin %s ... ", rPath.GetBuffer() ); +#endif + + xub_StrLen nPos = rPath.SearchBackward( '/' ); + if( nPos == STRING_NOTFOUND ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "no absolute path to plugin\n" ); +#endif + return false; + } + + ByteString aBaseName = rPath.Copy( nPos+1 ); + if( aBaseName.Equals( "libnullplugin.so" ) ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "don't like %s\n", aBaseName.GetBuffer() ); +#endif + return false; + } + + struct stat aStat; + if( stat( rPath.GetBuffer(), &aStat ) || ! S_ISREG( aStat.st_mode ) ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "%s is not a regular file\n", rPath.GetBuffer() ); +#endif + return false; + } + + + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + + rtl::OString path; + if (!UnxPluginComm::getPluginappPath(&path)) { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "cannot construct path to pluginapp.bin\n" ); +#endif + return false; + } + rtl::OStringBuffer cmd; + tools::appendUnixShellWord(&cmd, path); + cmd.append(' '); + tools::appendUnixShellWord(&cmd, rPath); + rtl::OString aCommand(cmd.makeStringAndClear()); + + FILE* pResult = popen( aCommand.getStr(), "r" ); + int nDescriptions = 0; + if( pResult ) + { + OStringBuffer aMIME; + char buf[256]; + while( fgets( buf, sizeof( buf ), pResult ) ) + { + for( size_t i = 0; i < sizeof(buf) && buf[i]; ++i ) + { + if( buf[i] == '\n' ) + buf[i] = ';'; + } + aMIME.append( buf ); + } + pclose( pResult ); + + if( aMIME.getLength() > 0 ) + { + OString aLine = aMIME.makeStringAndClear(); + + 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 = OStringToOUString( rPath, aEncoding ); + // set mimetype + pNew->Mimetype = OStringToOUString( aMimetype, aEncoding ); + // set extension line + pNew->Extension = OStringToOUString( aExtension.makeStringAndClear(), aEncoding ); + // set description + pNew->Description= OStringToOUString( aDesc, aEncoding ); + rDescriptions.push_back( pNew ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "Mimetype: %s\nExtension: %s\n" + "Description: %s\n", + OUStringToOString( pNew->Mimetype, aEncoding ).getStr(), + OUStringToOString( pNew->Extension, aEncoding ).getStr(), + OUStringToOString( pNew->Description, aEncoding ).getStr() + ); +#endif + } + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "result of \"%s\" contains no mimtype\n", + aCommand.getStr() ); +#endif + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "command \"%s\" failed\n", aCommand.getStr() ); +#endif + return nDescriptions > 0; +} + +union maxDirent +{ + char aBuffer[ sizeof( struct dirent ) + _PC_NAME_MAX +1 ]; + struct dirent asDirent; +}; + +static void CheckPluginRegistryFiles( const rtl::OString& rPath, list< PluginDescription* >& rDescriptions ) +{ + rtl::OStringBuffer aPath( 1024 ); + aPath.append( rPath ); + aPath.append( "/pluginreg.dat" ); + FILE* fp = fopen( aPath.getStr(), "r" ); + if( fp ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "parsing %s\n", aPath.getStr() ); +#endif + char aLine[1024]; + while( fgets( aLine, sizeof( aLine ), fp ) ) + { + int nLineLen = strlen( aLine ); + int nDotPos; + for( nDotPos = nLineLen-1; nDotPos > 0 && aLine[nDotPos] != ':'; nDotPos-- ) + ; + if( aLine[0] == '/' && aLine[nDotPos] == ':' && aLine[nDotPos+1] == '$' ) + CheckPlugin( ByteString( aLine, nDotPos ), rDescriptions ); + } + fclose( fp ); + } + + // check subdirectories + DIR* pDIR = opendir( rPath.getStr() ); + struct dirent* pDirEnt = NULL; + struct stat aStat; + maxDirent u; + while( pDIR && ! readdir_r( pDIR, &u.asDirent, &pDirEnt ) && pDirEnt ) + { + char* pBaseName = u.asDirent.d_name; + if( rtl_str_compare( ".", pBaseName ) && rtl_str_compare( "..", pBaseName ) ) + { + rtl::OStringBuffer aBuf( 1024 ); + aBuf.append( rPath ); + aBuf.append( '/' ); + aBuf.append( pBaseName ); + + if( ! stat( aBuf.getStr(), &aStat ) ) + { + if( S_ISDIR( aStat.st_mode ) ) + CheckPluginRegistryFiles( aBuf.makeStringAndClear(), rDescriptions ); + } + } + } + if( pDIR ) + closedir( pDIR ); +} + +Sequence<PluginDescription> XPluginManager_Impl::impl_getPluginDescriptions() throw() +{ + static Sequence<PluginDescription> aDescriptions; + static BOOL bHavePlugins = FALSE; + if( ! bHavePlugins ) + { + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + list<PluginDescription*> aPlugins; + int i; + + // unix: search for plugins in /usr/lib/netscape/plugins, + // ~/.netscape/plugins und NPX_PLUGIN_PATH + // additionally: search in PluginsPath + static const char* pHome = getenv( "HOME" ); + static const char* pNPXPluginPath = getenv( "NPX_PLUGIN_PATH" ); + + ByteString aSearchPath( "/usr/lib/netscape/plugins" ); + if( pHome ) + { + aSearchPath.Append( ':' ); + aSearchPath.Append( pHome ); + aSearchPath += "/.netscape/plugins"; + } + if( pNPXPluginPath ) + { + aSearchPath.Append( ':' ); + aSearchPath += pNPXPluginPath; + } + + const Sequence< ::rtl::OUString >& rPaths( PluginManager::getAdditionalSearchPaths() ); + for( i = 0; i < rPaths.getLength(); i++ ) + { + aSearchPath += ":"; + aSearchPath += ByteString( String( rPaths.getConstArray()[i] ), aEncoding ); + } + + int nPaths = aSearchPath.GetTokenCount( ':' ); + maxDirent u; + for( i = 0; i < nPaths; i++ ) + { + ByteString aPath( aSearchPath.GetToken( i, ':' ) ); + if( aPath.Len() ) + { + DIR* pDIR = opendir( aPath.GetBuffer() ); + struct dirent* pDirEnt = NULL; + while( pDIR && ! readdir_r( pDIR, &u.asDirent, &pDirEnt ) && pDirEnt ) + { + char* pBaseName = u.asDirent.d_name; + if( pBaseName[0] != '.' || + pBaseName[1] != '.' || + pBaseName[2] != 0 ) + { + ByteString aFileName( aPath ); + aFileName += "/"; + aFileName += pBaseName; + CheckPlugin( aFileName, aPlugins ); + } + } + if( pDIR ) + closedir( pDIR ); + } + } + + // try ~/.mozilla/pluginreg.dat + rtl::OStringBuffer aBuf(256); + aBuf.append( pHome ); + aBuf.append( "/.mozilla" ); + CheckPluginRegistryFiles( aBuf.makeStringAndClear(), aPlugins ); + + // create return value + aDescriptions = Sequence<PluginDescription>( aPlugins.size() ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "found %d plugins\n", aPlugins.size() ); +#endif + list<PluginDescription*>::iterator iter; + for( iter = aPlugins.begin(), i=0; iter != aPlugins.end(); ++iter ,i++ ) + { + aDescriptions.getArray()[ i ] = **iter; + delete *iter; + } + aPlugins.clear(); + bHavePlugins = TRUE; + } + return aDescriptions; +} |