/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_sw.hxx" #include #include #include #include #include #include #include // GetAppData #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef _COM_SUN_STAR_UTIL_CloseVetoException_HPP_ #include #endif #include #include #include "com/sun/star/mail/XSmtpService.hpp" #include #include #include #include #include #include #include #include #include #include #ifndef IDOCUMENTDEVICEACCESS_HXX_INCLUDED #include #endif #include #include #include #include #include #include #include #include #include #include #define SN_MAIL_MERGE "com.sun.star.text.MailMerge" #define SN_DATA_ACCESS_DESCRIPTOR "com.sun.star.sdb.DataAccessDescriptor" using namespace ::com::sun::star; using namespace ::com::sun::star::frame; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::beans; using namespace ::com::sun::star::text; using ::rtl::OUString; using namespace SWUnoHelper; //////////////////////////////////////////////////////////// typedef ::utl::SharedUNOComponent< XInterface > SharedComponent; //////////////////////////////////////////////////////////// osl::Mutex & GetMailMergeMutex() { static osl::Mutex aMutex; return aMutex; } //////////////////////////////////////////////////////////// enum CloseResult { eSuccess, // successfully closed eVetoed, // vetoed, ownership transferred to the vetoing instance eFailed // failed for some unknown reason }; static CloseResult CloseModelAndDocSh( Reference< frame::XModel > &rxModel, SfxObjectShellRef &rxDocSh ) { CloseResult eResult = eSuccess; rxDocSh = 0; //! models/documents should never be disposed (they may still be //! used for printing which is called asynchronously for example) //! instead call close Reference< util::XCloseable > xClose( rxModel, UNO_QUERY ); if (xClose.is()) { try { //! 'sal_True' -> transfer ownership to vetoing object if vetoed! //! I.e. now that object is responsible for closing the model and doc shell. xClose->close( sal_True ); } catch (util::CloseVetoException &) { //! here we have the problem that the temporary file that is //! currently being printed will never be deleted. :-( eResult = eVetoed; } catch ( const uno::RuntimeException& ) { eResult = eFailed; } } return eResult; } //////////////////////////////////////////////////////////// static sal_Bool LoadFromURL_impl( Reference< frame::XModel > &rxModel, SfxObjectShellRef &rxDocSh, const String &rURL, sal_Bool bClose ) throw (RuntimeException) { // try to open the document readonly and hidden Reference< frame::XModel > xTmpModel; Sequence < PropertyValue > aArgs( 1 ); aArgs[0].Name = C2U("Hidden"); sal_Bool bVal = sal_True; aArgs[0].Value <<= bVal; try { Reference < XComponentLoader > xDesktop( ::comphelper::getProcessServiceFactory()-> createInstance( C2U("com.sun.star.frame.Desktop") ), UNO_QUERY ); xTmpModel = Reference < XModel >( xDesktop->loadComponentFromURL( rURL, C2U("_blank"), 0, aArgs ), UNO_QUERY ); } catch( Exception & ) { return sal_False; } // try to get the DocShell SwDocShell *pTmpDocShell = 0; Reference < XUnoTunnel > xTunnel( xTmpModel, UNO_QUERY ); if (xTunnel.is()) { SwXTextDocument* pTextDoc = reinterpret_cast( xTunnel->getSomething( SwXTextDocument::getUnoTunnelId() )); pTmpDocShell = pTextDoc ? pTextDoc->GetDocShell() : 0; } sal_Bool bRes = sal_False; if (xTmpModel.is() && pTmpDocShell) // everything available? { if (bClose) CloseModelAndDocSh( rxModel, rxDocSh ); // set new stuff rxModel = xTmpModel; rxDocSh = pTmpDocShell; bRes = sal_True; } else { // SfxObjectShellRef is ok here, since the document will be explicitly closed SfxObjectShellRef xTmpDocSh = pTmpDocShell; CloseModelAndDocSh( xTmpModel, xTmpDocSh ); } return bRes; } //========================================================== namespace { class DelayedFileDeletion : public ::cppu::WeakImplHelper1< util::XCloseListener > { protected: ::osl::Mutex m_aMutex; Reference< util::XCloseable > m_xDocument; Timer m_aDeleteTimer; String m_sTemporaryFile; sal_Int32 m_nPendingDeleteAttempts; public: DelayedFileDeletion( const Reference< XModel >& _rxModel, const String& _rTemporaryFile ); protected: ~DelayedFileDeletion( ); // XCloseListener virtual void SAL_CALL queryClosing( const EventObject& _rSource, sal_Bool _bGetsOwnership ) throw (util::CloseVetoException, RuntimeException); virtual void SAL_CALL notifyClosing( const EventObject& _rSource ) throw (RuntimeException); // XEventListener virtual void SAL_CALL disposing( const EventObject& Source ) throw (RuntimeException); private: void implTakeOwnership( ); DECL_LINK( OnTryDeleteFile, void* ); private: DelayedFileDeletion( const DelayedFileDeletion& ); // never implemented DelayedFileDeletion& operator=( const DelayedFileDeletion& ); // never implemented }; DBG_NAME( DelayedFileDeletion ) //------------------------------------------------------ DelayedFileDeletion::DelayedFileDeletion( const Reference< XModel >& _rxModel, const String& _rTemporaryFile ) : m_xDocument( _rxModel, UNO_QUERY ) ,m_sTemporaryFile( _rTemporaryFile ) ,m_nPendingDeleteAttempts( 0 ) { DBG_CTOR( DelayedFileDeletion, NULL ); osl_incrementInterlockedCount( &m_refCount ); try { if ( m_xDocument.is() ) { m_xDocument->addCloseListener( this ); // successfully added -> keep ourself alive acquire(); } else { DBG_ERROR( "DelayedFileDeletion::DelayedFileDeletion: model is no component!" ); } } catch( const Exception& ) { DBG_ERROR( "DelayedFileDeletion::DelayedFileDeletion: could not register as event listener at the model!" ); } osl_decrementInterlockedCount( &m_refCount ); } //-------------------------------------------------------------------- IMPL_LINK( DelayedFileDeletion, OnTryDeleteFile, void*, EMPTYARG ) { ::osl::ClearableMutexGuard aGuard( m_aMutex ); sal_Bool bSuccess = sal_False; try { sal_Bool bDeliverOwnership = ( 0 == m_nPendingDeleteAttempts ); // if this is our last attempt, then anybody which vetoes this has to take the consequences // (means take the ownership) m_xDocument->close( bDeliverOwnership ); bSuccess = sal_True; } catch( const util::CloseVetoException& ) { // somebody vetoed -> next try if ( m_nPendingDeleteAttempts ) { // next attempt --m_nPendingDeleteAttempts; m_aDeleteTimer.Start(); } else bSuccess = sal_True; // can't do anything here ... } catch( const Exception& ) { DBG_ERROR( "DelayedFileDeletion::OnTryDeleteFile: caught a strange exception!" ); bSuccess = sal_True; // can't do anything here ... } if ( bSuccess ) { SWUnoHelper::UCB_DeleteFile( m_sTemporaryFile ); aGuard.clear(); release(); // this should be our last reference, we should be dead after this } return 0L; } //-------------------------------------------------------------------- void DelayedFileDeletion::implTakeOwnership( ) { // revoke ourself as listener try { m_xDocument->removeCloseListener( this ); } catch( const Exception & ) { DBG_ERROR( "DelayedFileDeletion::implTakeOwnership: could not revoke the listener!" ); } m_aDeleteTimer.SetTimeout( 3000 ); // 3 seconds m_aDeleteTimer.SetTimeoutHdl( LINK( this, DelayedFileDeletion, OnTryDeleteFile ) ); m_nPendingDeleteAttempts = 3; // try 3 times at most m_aDeleteTimer.Start( ); } //-------------------------------------------------------------------- void SAL_CALL DelayedFileDeletion::queryClosing( const EventObject& , sal_Bool _bGetsOwnership ) throw (util::CloseVetoException, RuntimeException) { ::osl::MutexGuard aGuard( m_aMutex ); if ( _bGetsOwnership ) implTakeOwnership( ); // always veto: We want to take the ownership ourself, as this is the only chance to delete // the temporary file which the model is based on throw util::CloseVetoException( ); } //-------------------------------------------------------------------- void SAL_CALL DelayedFileDeletion::notifyClosing( const EventObject& ) throw (RuntimeException) { DBG_ERROR( "DelayedFileDeletion::notifyClosing: how this?" ); // this should not happen: // Either, a foreign instance closes the document, then we should veto this, and take the ownership // Or, we ourself close the document, then we should not be a listener anymore } //------------------------------------------------------ void SAL_CALL DelayedFileDeletion::disposing( const EventObject& ) throw (RuntimeException) { DBG_ERROR( "DelayedFileDeletion::disposing: how this?" ); // this should not happen: // Either, a foreign instance closes the document, then we should veto this, and take the ownership // Or, we ourself close the document, then we should not be a listener anymore } //------------------------------------------------------ DelayedFileDeletion::~DelayedFileDeletion( ) { DBG_DTOR( DelayedFileDeletion, NULL ); } } //////////////////////////////////////////////////////////// static sal_Bool DeleteTmpFile_Impl( Reference< frame::XModel > &rxModel, SfxObjectShellRef &rxDocSh, const String &rTmpFileURL ) { sal_Bool bRes = sal_False; if (rTmpFileURL.Len()) { sal_Bool bDelete = sal_True; if ( eVetoed == CloseModelAndDocSh( rxModel, rxDocSh ) ) { // somebody vetoed the closing, and took the ownership of the document // -> ensure that the temporary file is deleted later on Reference< XEventListener > xEnsureDelete( new DelayedFileDeletion( rxModel, rTmpFileURL ) ); // note: as soon as #106931# is fixed, the whole DelayedFileDeletion is to be superseeded by // a better solution bDelete = sal_False; } rxModel = 0; rxDocSh = 0; // destroy doc shell if ( bDelete ) { if ( !SWUnoHelper::UCB_DeleteFile( rTmpFileURL ) ) { Reference< XEventListener > xEnsureDelete( new DelayedFileDeletion( rxModel, rTmpFileURL ) ); // same not as above: as soon as #106931#, ... } } else bRes = sal_True; // file will be deleted delayed } return bRes; } //////////////////////////////////////////////////////////// SwXMailMerge::SwXMailMerge() : aEvtListeners ( GetMailMergeMutex() ), aMergeListeners ( GetMailMergeMutex() ), aPropListeners ( GetMailMergeMutex() ), pPropSet( aSwMapProvider.GetPropertySet( PROPERTY_MAP_MAILMERGE ) ), bSendAsHTML(sal_False), bSendAsAttachment(sal_False), bSaveAsSingleFile(sal_False) { // create empty document // like in: SwModule::InsertEnv (appenv.cxx) SwDocShell *pDocShell = new SwDocShell( SFX_CREATE_MODE_STANDARD ); xDocSh = pDocShell; xDocSh->DoInitNew( 0 ); SfxViewFrame *pFrame = SfxViewFrame::LoadHiddenDocument( *xDocSh, 0 ); SwView *pView = (SwView*) pFrame->GetViewShell(); pView->AttrChangedNotify( &pView->GetWrtShell() );//Damit SelectShell gerufen wird. xModel = pDocShell->GetModel(); nDataCommandType = sdb::CommandType::TABLE; nOutputType = MailMergeType::PRINTER; bEscapeProcessing = sal_True; //!! allow to process properties like "Filter", "Order", ... bSinglePrintJobs = sal_False; bFileNameFromColumn = sal_False; bDisposing = sal_False; } SwXMailMerge::~SwXMailMerge() { if (aTmpFileName.Len()) DeleteTmpFile_Impl( xModel, xDocSh, aTmpFileName ); else // there was no temporary file in use { //! we still need to close the model and doc shell manually //! because there is no automatism that will do that later. //! #120086# if ( eVetoed == CloseModelAndDocSh( xModel, xDocSh ) ) DBG_WARNING( "owner ship transferred to vetoing object!" ); xModel = 0; xDocSh = 0; // destroy doc shell } } uno::Any SAL_CALL SwXMailMerge::execute( const uno::Sequence< beans::NamedValue >& rArguments ) throw (IllegalArgumentException, Exception, RuntimeException) { vos::OGuard aGuard( Application::GetSolarMutex() ); // // get property values to be used // (use values from the service as default and override them with // the values that are provided as arguments) // uno::Sequence< uno::Any > aCurSelection = aSelection; uno::Reference< sdbc::XResultSet > xCurResultSet = xResultSet; uno::Reference< sdbc::XConnection > xCurConnection = xConnection; uno::Reference< frame::XModel > xCurModel = xModel; OUString aCurDataSourceName = aDataSourceName; OUString aCurDataCommand = aDataCommand; OUString aCurFilter = aFilter; OUString aCurDocumentURL = aDocumentURL; OUString aCurOutputURL = aOutputURL; OUString aCurFileNamePrefix = aFileNamePrefix; sal_Int32 nCurDataCommandType = nDataCommandType; sal_Int16 nCurOutputType = nOutputType; sal_Bool bCurEscapeProcessing = bEscapeProcessing; sal_Bool bCurSinglePrintJobs = bSinglePrintJobs; sal_Bool bCurFileNameFromColumn = bFileNameFromColumn; // SfxObjectShellRef xCurDocSh = xDocSh; // the document // const beans::NamedValue *pArguments = rArguments.getConstArray(); sal_Int32 nArgs = rArguments.getLength(); for (sal_Int32 i = 0; i < nArgs; ++i) { const OUString &rName = pArguments[i].Name; const Any &rValue = pArguments[i].Value; sal_Bool bOK = sal_True; if (rName.equalsAscii( GetPropName( UNO_NAME_SELECTION ) )) bOK = rValue >>= aCurSelection; else if (rName.equalsAscii( GetPropName( UNO_NAME_RESULT_SET ) )) bOK = rValue >>= xCurResultSet; else if (rName.equalsAscii( GetPropName( UNO_NAME_CONNECTION ) )) bOK = rValue >>= xCurConnection; else if (rName.equalsAscii( GetPropName( UNO_NAME_MODEL ) )) throw PropertyVetoException( OUString ( RTL_CONSTASCII_USTRINGPARAM ( "Property is read-only: " ) ) + rName, static_cast < cppu::OWeakObject * > ( this ) ); else if (rName.equalsAscii( GetPropName( UNO_NAME_DATA_SOURCE_NAME ) )) bOK = rValue >>= aCurDataSourceName; else if (rName.equalsAscii( GetPropName( UNO_NAME_DAD_COMMAND ) )) bOK = rValue >>= aCurDataCommand; else if (rName.equalsAscii( GetPropName( UNO_NAME_FILTER ) )) bOK = rValue >>= aCurFilter; else if (rName.equalsAscii( GetPropName( UNO_NAME_DOCUMENT_URL ) )) { bOK = rValue >>= aCurDocumentURL; if (aCurDocumentURL.getLength() && !LoadFromURL_impl( xCurModel, xCurDocSh, aCurDocumentURL, sal_False )) throw RuntimeException( OUString ( RTL_CONSTASCII_USTRINGPARAM ( "Failed to create document from URL: " ) ) + aCurDocumentURL, static_cast < cppu::OWeakObject * > ( this ) ); } else if (rName.equalsAscii( GetPropName( UNO_NAME_OUTPUT_URL ) )) { bOK = rValue >>= aCurOutputURL; if (aCurOutputURL.getLength()) { if (!UCB_IsDirectory(aCurOutputURL)) throw IllegalArgumentException( OUString ( RTL_CONSTASCII_USTRINGPARAM ( "URL does not point to a directory: " ) ) + aCurOutputURL, static_cast < cppu::OWeakObject * > ( this ), 0 ); if (UCB_IsReadOnlyFileName(aCurOutputURL)) throw IllegalArgumentException( OUString ( RTL_CONSTASCII_USTRINGPARAM ( "URL is read-only: " ) ) + aCurOutputURL, static_cast < cppu::OWeakObject * > ( this ), 0 ); } } else if (rName.equalsAscii( GetPropName( UNO_NAME_FILE_NAME_PREFIX ) )) bOK = rValue >>= aCurFileNamePrefix; else if (rName.equalsAscii( GetPropName( UNO_NAME_DAD_COMMAND_TYPE ) )) bOK = rValue >>= nCurDataCommandType; else if (rName.equalsAscii( GetPropName( UNO_NAME_OUTPUT_TYPE ) )) bOK = rValue >>= nCurOutputType; else if (rName.equalsAscii( GetPropName( UNO_NAME_ESCAPE_PROCESSING ) )) bOK = rValue >>= bCurEscapeProcessing; else if (rName.equalsAscii( GetPropName( UNO_NAME_SINGLE_PRINT_JOBS ) )) bOK = rValue >>= bCurSinglePrintJobs; else if (rName.equalsAscii( GetPropName( UNO_NAME_FILE_NAME_FROM_COLUMN ) )) bOK = rValue >>= bCurFileNameFromColumn; else if (rName.equalsAscii( GetPropName( UNO_NAME_SUBJECT ) )) bOK = rValue >>= sSubject; else if (rName.equalsAscii( GetPropName( UNO_NAME_ADDRESS_FROM_COLUMN ) )) bOK = rValue >>= sAddressFromColumn; else if (rName.equalsAscii( GetPropName( UNO_NAME_SEND_AS_HTML ) )) bOK = rValue >>= bSendAsHTML; else if (rName.equalsAscii( GetPropName( UNO_NAME_MAIL_BODY ) )) bOK = rValue >>= sMailBody; else if (rName.equalsAscii( GetPropName( UNO_NAME_ATTACHMENT_NAME ) )) bOK = rValue >>= sAttachmentName; else if (rName.equalsAscii( GetPropName( UNO_NAME_ATTACHMENT_FILTER ) )) bOK = rValue >>= sAttachmentFilter; else if (rName.equalsAscii( GetPropName( UNO_NAME_COPIES_TO ) )) bOK = rValue >>= aCopiesTo; else if (rName.equalsAscii( GetPropName( UNO_NAME_BLIND_COPIES_TO ) )) bOK = rValue >>= aBlindCopiesTo; else if (rName.equalsAscii( GetPropName( UNO_NAME_SEND_AS_ATTACHMENT ) )) bOK = rValue >>= bSendAsAttachment; else if (rName.equalsAscii( GetPropName( UNO_NAME_PRINT_OPTIONS ) )) bOK = rValue >>= aPrintSettings; else if (rName.equalsAscii( GetPropName( UNO_NAME_SAVE_AS_SINGLE_FILE ) )) bOK = rValue >>= bSaveAsSingleFile; else if (rName.equalsAscii( GetPropName( UNO_NAME_SAVE_FILTER ) )) bOK = rValue >>= sSaveFilter; else if (rName.equalsAscii( GetPropName( UNO_NAME_SAVE_FILTER_OPTIONS ) )) bOK = rValue >>= sSaveFilterOptions; else if (rName.equalsAscii( GetPropName( UNO_NAME_SAVE_FILTER_DATA ) )) bOK = rValue >>= aSaveFilterData; else if (rName.equalsAscii( GetPropName( UNO_NAME_IN_SERVER_PASSWORD ) )) bOK = rValue >>= sInServerPassword; else if (rName.equalsAscii( GetPropName( UNO_NAME_OUT_SERVER_PASSWORD ) )) bOK = rValue >>= sOutServerPassword; else throw UnknownPropertyException( OUString ( RTL_CONSTASCII_USTRINGPARAM ( "Property is unknown: " ) ) + rName, static_cast < cppu::OWeakObject * > ( this ) ); if (!bOK) throw IllegalArgumentException( OUString ( RTL_CONSTASCII_USTRINGPARAM ( "Property type mismatch or property not set: " ) ) + rName, static_cast < cppu::OWeakObject * > ( this ), 0 ); } // need to translate the selection: the API here requires a sequence of bookmarks, but the MergeNew // method we will call below requires a sequence of indicies. if ( aCurSelection.getLength() ) { Sequence< Any > aTranslated( aCurSelection.getLength() ); sal_Bool bValid = sal_False; Reference< sdbcx::XRowLocate > xRowLocate( xCurResultSet, UNO_QUERY ); if ( xRowLocate.is() ) { const Any* pBookmarks = aCurSelection.getConstArray(); const Any* pBookmarksEnd = pBookmarks + aCurSelection.getLength(); Any* pTranslated = aTranslated.getArray(); try { sal_Bool bEverythingsFine = sal_True; for ( ; ( pBookmarks != pBookmarksEnd ) && bEverythingsFine; ++pBookmarks ) { if ( xRowLocate->moveToBookmark( *pBookmarks ) ) *pTranslated <<= xCurResultSet->getRow(); else bEverythingsFine = sal_False; } if ( bEverythingsFine ) bValid = sal_True; } catch( const Exception& ) { bValid = sal_False; } } if ( !bValid ) { throw IllegalArgumentException( OUString ( RTL_CONSTASCII_USTRINGPARAM ( "The current 'Selection' does not describe a valid array of bookmarks, relative to the current 'ResultSet'." ) ), static_cast < cppu::OWeakObject * > ( this ), 0 ); } aCurSelection = aTranslated; } SfxViewFrame* pFrame = SfxViewFrame::GetFirst( xCurDocSh, sal_False); SwView *pView = PTR_CAST( SwView, pFrame->GetViewShell() ); if (!pView) throw RuntimeException(); SwWrtShell &rSh = *pView->GetWrtShellPtr(); // avoid assertion in 'Update' from Sfx by supplying a shell // and thus avoiding the SelectShell call in Writers GetState function // while still in Update of Sfx. // (GetSelection in Update is not allowed) if (pView && aCurDocumentURL.getLength()) pView->AttrChangedNotify( &pView->GetWrtShell() );//Damit SelectShell gerufen wird. SharedComponent aRowSetDisposeHelper; if (!xCurResultSet.is()) { if (!aCurDataSourceName.getLength() || !aCurDataCommand.getLength() ) { DBG_ERROR("PropertyValues missing or unset"); throw IllegalArgumentException( OUString ( RTL_CONSTASCII_USTRINGPARAM ( "Either the ResultSet or DataSourceName and DataCommand must be set." ) ), static_cast < cppu::OWeakObject * > ( this ), 0 ); } // // build ResultSet from DataSourceName, DataCommand and DataCommandType // Reference< XMultiServiceFactory > xMgr( ::comphelper::getProcessServiceFactory() ); if (xMgr.is()) { Reference< XInterface > xInstance = xMgr->createInstance( C2U( "com.sun.star.sdb.RowSet" )); aRowSetDisposeHelper.reset( xInstance, SharedComponent::TakeOwnership ); Reference< XPropertySet > xRowSetPropSet( xInstance, UNO_QUERY ); DBG_ASSERT( xRowSetPropSet.is(), "failed to get XPropertySet interface from RowSet" ); if (xRowSetPropSet.is()) { if (xCurConnection.is()) xRowSetPropSet->setPropertyValue( C2U("ActiveConnection"), makeAny( xCurConnection ) ); xRowSetPropSet->setPropertyValue( C2U("DataSourceName"), makeAny( aCurDataSourceName ) ); xRowSetPropSet->setPropertyValue( C2U("Command"), makeAny( aCurDataCommand ) ); xRowSetPropSet->setPropertyValue( C2U("CommandType"), makeAny( nCurDataCommandType ) ); xRowSetPropSet->setPropertyValue( C2U("EscapeProcessing"), makeAny( bCurEscapeProcessing ) ); xRowSetPropSet->setPropertyValue( C2U("ApplyFilter"), makeAny( sal_True ) ); xRowSetPropSet->setPropertyValue( C2U("Filter"), makeAny( aCurFilter ) ); Reference< sdbc::XRowSet > xRowSet( xInstance, UNO_QUERY ); if (xRowSet.is()) xRowSet->execute(); // build ResultSet from properties if( !xCurConnection.is() ) xCurConnection.set( xRowSetPropSet->getPropertyValue( C2U( "ActiveConnection" )), UNO_QUERY ); xCurResultSet = Reference< sdbc::XResultSet >( xRowSet, UNO_QUERY ); DBG_ASSERT( xCurResultSet.is(), "failed to build ResultSet" ); } } } svx::ODataAccessDescriptor aDescriptor; aDescriptor.setDataSource(aCurDataSourceName); aDescriptor[ svx::daConnection ] <<= xCurConnection; aDescriptor[ svx::daCommand ] <<= aCurDataCommand; aDescriptor[ svx::daCommandType ] <<= nCurDataCommandType; aDescriptor[ svx::daEscapeProcessing ] <<= bCurEscapeProcessing; aDescriptor[ svx::daCursor ] <<= xCurResultSet; // aDescriptor[ svx::daColumnName ] not used // aDescriptor[ svx::daColumnObject ] not used aDescriptor[ svx::daSelection ] <<= aCurSelection; sal_uInt16 nMergeType; switch (nCurOutputType) { case MailMergeType::PRINTER : nMergeType = DBMGR_MERGE_MAILMERGE; break; case MailMergeType::FILE : nMergeType = DBMGR_MERGE_MAILFILES; break; case MailMergeType::MAIL : nMergeType = DBMGR_MERGE_MAILING; break; default: throw IllegalArgumentException( OUString ( RTL_CONSTASCII_USTRINGPARAM ( "Invalid value of property:" ) ) + C2U("OutputType"), static_cast < cppu::OWeakObject * > ( this ), 0 ); } SwNewDBMgr* pMgr = rSh.GetNewDBMgr(); //force layout creation rSh.CalcLayout(); DBG_ASSERT( pMgr, "database manager missing" ); SwMergeDescriptor aMergeDesc( nMergeType, rSh, aDescriptor ); std::auto_ptr< SwMailMergeConfigItem > pMMConfigItem; uno::Reference< mail::XMailService > xInService; if (MailMergeType::PRINTER == nCurOutputType) { IDocumentDeviceAccess* pIDDA = rSh.getIDocumentDeviceAccess(); SwPrintData aPrtData( pIDDA->getPrintData() ); aPrtData.SetPrintSingleJobs( bCurSinglePrintJobs ); pIDDA->setPrintData( aPrtData ); // #i25686# printing should not be done asynchronously to prevent dangling offices // when mail merge is called as command line macro aMergeDesc.bPrintAsync = sal_False; aMergeDesc.aPrintOptions = aPrintSettings; aMergeDesc.bCreateSingleFile = true; } else /* FILE and MAIL*/ { INetURLObject aURLObj; aURLObj.SetSmartProtocol( INET_PROT_FILE ); if (aCurDocumentURL.getLength()) { // if OutputURL or FileNamePrefix are missing get // them from DocumentURL aURLObj.SetSmartURL( aCurDocumentURL ); if (!aCurFileNamePrefix.getLength()) aCurFileNamePrefix = aURLObj.GetBase(); // filename without extension if (!aCurOutputURL.getLength()) { //aCurOutputURL = aURLObj.GetURLPath(); aURLObj.removeSegment(); aCurOutputURL = aURLObj.GetMainURL( INetURLObject::DECODE_TO_IURI ); } } else // default empty document without URL { if (!aCurOutputURL.getLength()) throw RuntimeException( OUString ( RTL_CONSTASCII_USTRINGPARAM ( "OutputURL is not set and can not be obtained." ) ), static_cast < cppu::OWeakObject * > ( this ) ); } aURLObj.SetSmartURL( aCurOutputURL ); String aPath = aURLObj.GetMainURL( INetURLObject::DECODE_TO_IURI ); String aDelim( INET_PATH_TOKEN ); if (aPath.Len() >= aDelim.Len() && aPath.Copy( aPath.Len()-aDelim.Len() ).CompareTo( aDelim ) != COMPARE_EQUAL) aPath += aDelim; if (bCurFileNameFromColumn) pMgr->SetEMailColumn( aCurFileNamePrefix ); else { aPath += String( aCurFileNamePrefix ); pMgr->SetEMailColumn( String() ); } pMgr->SetSubject( aPath ); if(MailMergeType::FILE == nCurOutputType) { aMergeDesc.sSaveToFilter = sSaveFilter; aMergeDesc.sSaveToFilterOptions = sSaveFilterOptions; aMergeDesc.aSaveToFilterData = aSaveFilterData; aMergeDesc.bCreateSingleFile = bSaveAsSingleFile; } else /*if(MailMergeType::MAIL == nCurOutputType)*/ { pMgr->SetEMailColumn( sAddressFromColumn ); if(!sAddressFromColumn.getLength()) throw RuntimeException( OUString ( RTL_CONSTASCII_USTRINGPARAM ( "Mail address column not set." ) ), static_cast < cppu::OWeakObject * > ( this ) ); aMergeDesc.sSaveToFilter = sAttachmentFilter; aMergeDesc.sSubject = sSubject; aMergeDesc.sMailBody = sMailBody; aMergeDesc.sAttachmentName = sAttachmentName; aMergeDesc.aCopiesTo = aCopiesTo; aMergeDesc.aBlindCopiesTo = aBlindCopiesTo; aMergeDesc.bSendAsHTML = bSendAsHTML; aMergeDesc.bSendAsAttachment = bSendAsAttachment; aMergeDesc.bCreateSingleFile = sal_False; pMMConfigItem = std::auto_ptr< SwMailMergeConfigItem >(new SwMailMergeConfigItem); aMergeDesc.pMailMergeConfigItem = pMMConfigItem.get(); aMergeDesc.xSmtpServer = SwMailMergeHelper::ConnectToSmtpServer( *pMMConfigItem, xInService, sInServerPassword, sOutServerPassword ); if( !aMergeDesc.xSmtpServer.is() || !aMergeDesc.xSmtpServer->isConnected()) throw RuntimeException( OUString ( RTL_CONSTASCII_USTRINGPARAM ( "Failed to connect to mail server." ) ), static_cast < cppu::OWeakObject * > ( this ) ); } } // save document with temporary filename const SfxFilter *pSfxFlt = SwIoSystem::GetFilterOfFormat( String::CreateFromAscii( FILTER_XML ), SwDocShell::Factory().GetFilterContainer() ); String aExtension( pSfxFlt->GetDefaultExtension() ); aExtension.EraseLeadingChars( '*' ); TempFile aTempFile( C2U("SwMM"), &aExtension ); aTmpFileName = aTempFile.GetName(); Reference< XStorable > xStorable( xCurModel, UNO_QUERY ); sal_Bool bStoredAsTemporary = sal_False; if ( xStorable.is() ) { try { xStorable->storeAsURL( aTmpFileName, Sequence< PropertyValue >() ); bStoredAsTemporary = sal_True; } catch( const Exception& ) { } } if ( !bStoredAsTemporary ) throw RuntimeException( OUString ( RTL_CONSTASCII_USTRINGPARAM ( "Failed to save temporary file." ) ), static_cast < cppu::OWeakObject * > ( this ) ); pMgr->SetMergeSilent( sal_True ); // suppress dialogs, message boxes, etc. const SwXMailMerge *pOldSrc = pMgr->GetMailMergeEvtSrc(); DBG_ASSERT( !pOldSrc || pOldSrc == this, "Ooops... different event source already set." ); pMgr->SetMailMergeEvtSrc( this ); // launch events for listeners SFX_APP()->NotifyEvent(SfxEventHint(SW_EVENT_MAIL_MERGE, SwDocShell::GetEventName(STR_SW_EVENT_MAIL_MERGE), xCurDocSh)); sal_Bool bSucc = pMgr->MergeNew( aMergeDesc ); SFX_APP()->NotifyEvent(SfxEventHint(SW_EVENT_MAIL_MERGE_END, SwDocShell::GetEventName(STR_SW_EVENT_MAIL_MERGE_END), xCurDocSh)); pMgr->SetMailMergeEvtSrc( pOldSrc ); if ( xCurModel.get() != xModel.get() ) { // in case it was a temporary model -> close it, and delete the file DeleteTmpFile_Impl( xCurModel, xCurDocSh, aTmpFileName ); aTmpFileName.Erase(); } // (in case it wasn't a temporary model, it will be closed in the dtor, at the latest) if (!bSucc) throw Exception( OUString ( RTL_CONSTASCII_USTRINGPARAM ( "Mail merge failed. Sorry, no further information available." ) ), static_cast < cppu::OWeakObject * > ( this ) ); //de-initialize services if(xInService.is() && xInService->isConnected()) xInService->disconnect(); if(aMergeDesc.xSmtpServer.is() && aMergeDesc.xSmtpServer->isConnected()) aMergeDesc.xSmtpServer->disconnect(); return makeAny( sal_True ); } void SwXMailMerge::LaunchMailMergeEvent( const MailMergeEvent &rEvt ) const { cppu::OInterfaceIteratorHelper aIt( ((SwXMailMerge *) this)->aMergeListeners ); while (aIt.hasMoreElements()) { Reference< XMailMergeListener > xRef( aIt.next(), UNO_QUERY ); if (xRef.is()) xRef->notifyMailMergeEvent( rEvt ); } } void SwXMailMerge::launchEvent( const PropertyChangeEvent &rEvt ) const { cppu::OInterfaceContainerHelper *pContainer = aPropListeners.getContainer( rEvt.PropertyHandle ); if (pContainer) { cppu::OInterfaceIteratorHelper aIt( *pContainer ); while (aIt.hasMoreElements()) { Reference< XPropertyChangeListener > xRef( aIt.next(), UNO_QUERY ); if (xRef.is()) xRef->propertyChange( rEvt ); } } } uno::Reference< beans::XPropertySetInfo > SAL_CALL SwXMailMerge::getPropertySetInfo( ) throw (RuntimeException) { vos::OGuard aGuard( Application::GetSolarMutex() ); static Reference< XPropertySetInfo > aRef = pPropSet->getPropertySetInfo(); return aRef; } void SAL_CALL SwXMailMerge::setPropertyValue( const OUString& rPropertyName, const uno::Any& rValue ) throw (UnknownPropertyException, PropertyVetoException, IllegalArgumentException, WrappedTargetException, RuntimeException) { vos::OGuard aGuard( Application::GetSolarMutex() ); const SfxItemPropertySimpleEntry* pCur = pPropSet->getPropertyMap()->getByName( rPropertyName ); if (!pCur) throw UnknownPropertyException(); else if (pCur->nFlags & PropertyAttribute::READONLY) throw PropertyVetoException(); else { void *pData = NULL; const uno::Type* pType = pCur->pType; switch (pCur->nWID) { case WID_SELECTION : pData = &aSelection; break; case WID_RESULT_SET : pData = &xResultSet; break; case WID_CONNECTION : pData = &xConnection; break; case WID_MODEL : pData = &xModel; break; case WID_DATA_SOURCE_NAME : pData = &aDataSourceName; break; case WID_DATA_COMMAND : pData = &aDataCommand; break; case WID_FILTER : pData = &aFilter; break; case WID_DOCUMENT_URL : pData = &aDocumentURL; break; case WID_OUTPUT_URL : pData = &aOutputURL; break; case WID_DATA_COMMAND_TYPE : pData = &nDataCommandType; break; case WID_OUTPUT_TYPE : pData = &nOutputType; break; case WID_ESCAPE_PROCESSING : pData = &bEscapeProcessing; break; case WID_SINGLE_PRINT_JOBS : pData = &bSinglePrintJobs; break; case WID_FILE_NAME_FROM_COLUMN : pData = &bFileNameFromColumn; break; case WID_FILE_NAME_PREFIX : pData = &aFileNamePrefix; break; case WID_MAIL_SUBJECT: pData = &sSubject; break; case WID_ADDRESS_FROM_COLUMN: pData = &sAddressFromColumn; break; case WID_SEND_AS_HTML: pData = &bSendAsHTML; break; case WID_SEND_AS_ATTACHMENT: pData = &bSendAsAttachment; break; case WID_MAIL_BODY: pData = &sMailBody; break; case WID_ATTACHMENT_NAME: pData = &sAttachmentName; break; case WID_ATTACHMENT_FILTER: pData = &sAttachmentFilter;break; case WID_PRINT_OPTIONS: pData = &aPrintSettings; break; case WID_SAVE_AS_SINGLE_FILE: pData = &bSaveAsSingleFile; break; case WID_SAVE_FILTER: pData = &sSaveFilter; break; case WID_SAVE_FILTER_OPTIONS: pData = &sSaveFilterOptions; break; case WID_SAVE_FILTER_DATA: pData = &aSaveFilterData; break; case WID_COPIES_TO: pData = &aCopiesTo; break; case WID_BLIND_COPIES_TO: pData = &aBlindCopiesTo;break; case WID_IN_SERVER_PASSWORD: pData = &sInServerPassword; break; case WID_OUT_SERVER_PASSWORD: pData = &sOutServerPassword; break; default : DBG_ERROR("unknown WID"); } Any aOld( pData, *pType ); sal_Bool bChanged = sal_False; sal_Bool bOK = sal_True; if (aOld != rValue) { if (pData == &aSelection) bOK = rValue >>= aSelection; else if (pData == &xResultSet) bOK = rValue >>= xResultSet; else if (pData == &xConnection) bOK = rValue >>= xConnection; else if (pData == &xModel) bOK = rValue >>= xModel; else if (pData == &aDataSourceName) bOK = rValue >>= aDataSourceName; else if (pData == &aDataCommand) bOK = rValue >>= aDataCommand; else if (pData == &aFilter) bOK = rValue >>= aFilter; else if (pData == &aDocumentURL) { OUString aText; bOK = rValue >>= aText; if (aText.getLength() && !LoadFromURL_impl( xModel, xDocSh, aText, sal_True )) throw RuntimeException( OUString ( RTL_CONSTASCII_USTRINGPARAM ( "Failed to create document from URL: " ) ) + aText, static_cast < cppu::OWeakObject * > ( this ) ); aDocumentURL = aText; } else if (pData == &aOutputURL) { OUString aText; bOK = rValue >>= aText; if (aText.getLength()) { if (!UCB_IsDirectory(aText)) throw IllegalArgumentException( OUString ( RTL_CONSTASCII_USTRINGPARAM ( "URL does not point to a directory: " ) ) + aText, static_cast < cppu::OWeakObject * > ( this ), 0 ); if (UCB_IsReadOnlyFileName(aText)) throw IllegalArgumentException( OUString ( RTL_CONSTASCII_USTRINGPARAM ( "URL is read-only: " ) ) + aText, static_cast < cppu::OWeakObject * > ( this ), 0 ); } aOutputURL = aText; } else if (pData == &nDataCommandType) bOK = rValue >>= nDataCommandType; else if (pData == &nOutputType) bOK = rValue >>= nOutputType; else if (pData == &bEscapeProcessing) bOK = rValue >>= bEscapeProcessing; else if (pData == &bSinglePrintJobs) bOK = rValue >>= bSinglePrintJobs; else if (pData == &bFileNameFromColumn) bOK = rValue >>= bFileNameFromColumn; else if (pData == &aFileNamePrefix) bOK = rValue >>= aFileNamePrefix; else if (pData == &sSubject) bOK = rValue >>= sSubject; else if (pData == &sAddressFromColumn) bOK = rValue >>= sAddressFromColumn; else if (pData == &bSendAsHTML) bOK = rValue >>= bSendAsHTML; else if (pData == &bSendAsAttachment) bOK = rValue >>= bSendAsAttachment; else if (pData == &sMailBody) bOK = rValue >>= sMailBody; else if (pData == &sAttachmentName) bOK = rValue >>= sAttachmentName; else if (pData == &sAttachmentFilter) bOK = rValue >>= sAttachmentFilter; else if (pData == &aPrintSettings) bOK = rValue >>= aPrintSettings; else if (pData == &bSaveAsSingleFile) bOK = rValue >>= bSaveAsSingleFile; else if (pData == &sSaveFilter) bOK = rValue >>= sSaveFilter; else if (pData == &sSaveFilterOptions) bOK = rValue >>= sSaveFilterOptions; else if (pData == &aSaveFilterData) bOK = rValue >>= aSaveFilterData; else if (pData == &aCopiesTo) bOK = rValue >>= aCopiesTo; else if (pData == &aBlindCopiesTo) bOK = rValue >>= aBlindCopiesTo; else if(pData == &sInServerPassword) bOK = rValue >>= sInServerPassword; else if(pData == &sOutServerPassword) bOK = rValue >>= sInServerPassword; else { DBG_ERROR( "invalid pointer" ); } DBG_ASSERT( bOK, "set value failed" ); bChanged = sal_True; } if (!bOK) throw IllegalArgumentException( OUString ( RTL_CONSTASCII_USTRINGPARAM ( "Property type mismatch or property not set: " ) ) + rPropertyName, static_cast < cppu::OWeakObject * > ( this ), 0 ); if (bChanged) { PropertyChangeEvent aChgEvt( (XPropertySet *) this, rPropertyName, sal_False, pCur->nWID, aOld, rValue ); launchEvent( aChgEvt ); } } } uno::Any SAL_CALL SwXMailMerge::getPropertyValue( const OUString& rPropertyName ) throw (UnknownPropertyException, WrappedTargetException, RuntimeException) { vos::OGuard aGuard( Application::GetSolarMutex() ); Any aRet; const SfxItemPropertySimpleEntry* pCur = pPropSet->getPropertyMap()->getByName( rPropertyName ); if (!pCur) throw UnknownPropertyException(); else { switch (pCur->nWID) { case WID_SELECTION : aRet <<= aSelection; break; case WID_RESULT_SET : aRet <<= xResultSet; break; case WID_CONNECTION : aRet <<= xConnection; break; case WID_MODEL : aRet <<= xModel; break; case WID_DATA_SOURCE_NAME : aRet <<= aDataSourceName; break; case WID_DATA_COMMAND : aRet <<= aDataCommand; break; case WID_FILTER : aRet <<= aFilter; break; case WID_DOCUMENT_URL : aRet <<= aDocumentURL; break; case WID_OUTPUT_URL : aRet <<= aOutputURL; break; case WID_DATA_COMMAND_TYPE : aRet <<= nDataCommandType; break; case WID_OUTPUT_TYPE : aRet <<= nOutputType; break; case WID_ESCAPE_PROCESSING : aRet <<= bEscapeProcessing; break; case WID_SINGLE_PRINT_JOBS : aRet <<= bSinglePrintJobs; break; case WID_FILE_NAME_FROM_COLUMN : aRet <<= bFileNameFromColumn; break; case WID_FILE_NAME_PREFIX : aRet <<= aFileNamePrefix; break; case WID_MAIL_SUBJECT: aRet <<= sSubject; break; case WID_ADDRESS_FROM_COLUMN: aRet <<= sAddressFromColumn; break; case WID_SEND_AS_HTML: aRet <<= bSendAsHTML; break; case WID_SEND_AS_ATTACHMENT: aRet <<= bSendAsAttachment; break; case WID_MAIL_BODY: aRet <<= sMailBody; break; case WID_ATTACHMENT_NAME: aRet <<= sAttachmentName; break; case WID_ATTACHMENT_FILTER: aRet <<= sAttachmentFilter;break; case WID_PRINT_OPTIONS: aRet <<= aPrintSettings; break; case WID_SAVE_AS_SINGLE_FILE: aRet <<= bSaveAsSingleFile; break; case WID_SAVE_FILTER: aRet <<= sSaveFilter; break; case WID_SAVE_FILTER_OPTIONS: aRet <<= sSaveFilterOptions; break; case WID_SAVE_FILTER_DATA: aRet <<= aSaveFilterData; break; case WID_COPIES_TO: aRet <<= aCopiesTo; break; case WID_BLIND_COPIES_TO: aRet <<= aBlindCopiesTo;break; case WID_IN_SERVER_PASSWORD: aRet <<= sInServerPassword; break; case WID_OUT_SERVER_PASSWORD: aRet <<= sOutServerPassword; break; default : DBG_ERROR("unknown WID"); } } return aRet; } void SAL_CALL SwXMailMerge::addPropertyChangeListener( const OUString& rPropertyName, const uno::Reference< beans::XPropertyChangeListener >& rListener ) throw (UnknownPropertyException, WrappedTargetException, RuntimeException) { vos::OGuard aGuard( Application::GetSolarMutex() ); if (!bDisposing && rListener.is()) { const SfxItemPropertySimpleEntry* pCur = pPropSet->getPropertyMap()->getByName( rPropertyName ); if (pCur) aPropListeners.addInterface( pCur->nWID, rListener ); else throw UnknownPropertyException(); } } void SAL_CALL SwXMailMerge::removePropertyChangeListener( const OUString& rPropertyName, const uno::Reference< beans::XPropertyChangeListener >& rListener ) throw (UnknownPropertyException, WrappedTargetException, RuntimeException) { vos::OGuard aGuard( Application::GetSolarMutex() ); if (!bDisposing && rListener.is()) { const SfxItemPropertySimpleEntry* pCur = pPropSet->getPropertyMap()->getByName( rPropertyName ); if (pCur) aPropListeners.removeInterface( pCur->nWID, rListener ); else throw UnknownPropertyException(); } } void SAL_CALL SwXMailMerge::addVetoableChangeListener( const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener >& /*rListener*/ ) throw (UnknownPropertyException, WrappedTargetException, RuntimeException) { // no vetoable property, thus no support for vetoable change listeners DBG_WARNING( "not implemented"); } void SAL_CALL SwXMailMerge::removeVetoableChangeListener( const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener >& /*rListener*/ ) throw (UnknownPropertyException, WrappedTargetException, RuntimeException) { // no vetoable property, thus no support for vetoable change listeners DBG_WARNING( "not implemented"); } void SAL_CALL SwXMailMerge::dispose() throw(RuntimeException) { vos::OGuard aGuard( Application::GetSolarMutex() ); if (!bDisposing) { bDisposing = sal_True; EventObject aEvtObj( (XPropertySet *) this ); aEvtListeners.disposeAndClear( aEvtObj ); aMergeListeners.disposeAndClear( aEvtObj ); aPropListeners.disposeAndClear( aEvtObj ); } } void SAL_CALL SwXMailMerge::addEventListener( const Reference< XEventListener >& rxListener ) throw(RuntimeException) { vos::OGuard aGuard( Application::GetSolarMutex() ); if (!bDisposing && rxListener.is()) aEvtListeners.addInterface( rxListener ); } void SAL_CALL SwXMailMerge::removeEventListener( const Reference< XEventListener >& rxListener ) throw(RuntimeException) { vos::OGuard aGuard( Application::GetSolarMutex() ); if (!bDisposing && rxListener.is()) aEvtListeners.removeInterface( rxListener ); } void SAL_CALL SwXMailMerge::addMailMergeEventListener( const uno::Reference< XMailMergeListener >& rxListener ) throw (RuntimeException) { vos::OGuard aGuard( Application::GetSolarMutex() ); if (!bDisposing && rxListener.is()) aMergeListeners.addInterface( rxListener ); } void SAL_CALL SwXMailMerge::removeMailMergeEventListener( const uno::Reference< XMailMergeListener >& rxListener ) throw (RuntimeException) { vos::OGuard aGuard( Application::GetSolarMutex() ); if (!bDisposing && rxListener.is()) aMergeListeners.removeInterface( rxListener ); } OUString SAL_CALL SwXMailMerge::getImplementationName() throw(RuntimeException) { vos::OGuard aGuard( Application::GetSolarMutex() ); return SwXMailMerge_getImplementationName(); } sal_Bool SAL_CALL SwXMailMerge::supportsService( const OUString& rServiceName ) throw(RuntimeException) { vos::OGuard aGuard( Application::GetSolarMutex() ); return C2U( SN_MAIL_MERGE ) == rServiceName || C2U( SN_DATA_ACCESS_DESCRIPTOR ) == rServiceName; } uno::Sequence< OUString > SAL_CALL SwXMailMerge::getSupportedServiceNames() throw(RuntimeException) { vos::OGuard aGuard( Application::GetSolarMutex() ); return SwXMailMerge_getSupportedServiceNames(); } //////////////////////////////////////////////////////////// uno::Sequence< OUString > SAL_CALL SwXMailMerge_getSupportedServiceNames() throw() { uno::Sequence< OUString > aNames(2); OUString *pName = aNames.getArray(); pName[0] = C2U( SN_MAIL_MERGE ); pName[1] = C2U( SN_DATA_ACCESS_DESCRIPTOR ); return aNames; } OUString SAL_CALL SwXMailMerge_getImplementationName() throw() { return OUString( C2U( "SwXMailMerge" ) ); } uno::Reference< uno::XInterface > SAL_CALL SwXMailMerge_createInstance( const uno::Reference< XMultiServiceFactory > & /*rSMgr*/) throw( uno::Exception ) { vos::OGuard aGuard( Application::GetSolarMutex() ); //the module may not be loaded SwDLL::Init(); uno::Reference< uno::XInterface > xRef = (cppu::OWeakObject *) new SwXMailMerge(); return xRef; }