xref: /core/sw/source/uibase/uno/unomailmerge.cxx (revision 309768c2)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <comphelper/servicehelper.hxx>
21 #include <utility>
22 #include <vcl/svapp.hxx>
23 #include <osl/mutex.hxx>
24 #include <svl/itemprop.hxx>
25 #include <svx/dataaccessdescriptor.hxx>
26 #include <unotools/tempfile.hxx>
27 #include <sfx2/app.hxx>
28 #include <sfx2/docfilt.hxx>
29 #include <tools/urlobj.hxx>
30 #include <comphelper/diagnose_ex.hxx>
31 #include <comphelper/processfactory.hxx>
32 #include <comphelper/propertyvalue.hxx>
33 #include <comphelper/string.hxx>
34 #include <cppuhelper/supportsservice.hxx>
35 #include <vcl/timer.hxx>
36 #include <com/sun/star/sdb/CommandType.hpp>
37 #include <com/sun/star/text/MailMergeType.hpp>
38 #include <com/sun/star/text/MailMergeEvent.hpp>
39 #include <com/sun/star/text/XMailMergeListener.hpp>
40 #include <com/sun/star/beans/PropertyAttribute.hpp>
41 #include <com/sun/star/sdbc/XResultSet.hpp>
42 #include <com/sun/star/sdbc/XConnection.hpp>
43 #include <com/sun/star/sdbc/XRowSet.hpp>
44 #include <com/sun/star/frame/Desktop.hpp>
45 #include <com/sun/star/util/XCloseable.hpp>
46 #include <com/sun/star/util/CloseVetoException.hpp>
47 #include <com/sun/star/sdbcx/XRowLocate.hpp>
48 #include <com/sun/star/frame/XStorable.hpp>
49 #include <com/sun/star/mail/XSmtpService.hpp>
50 #include <sfx2/viewfrm.hxx>
51 #include <sfx2/event.hxx>
52 #include <cppuhelper/implbase.hxx>
53 #include <printdata.hxx>
54 #include <swevent.hxx>
55 #include <unomailmerge.hxx>
56 #include <unoprnms.hxx>
57 #include <unomap.hxx>
58 #include <swunohelper.hxx>
59 #include <docsh.hxx>
60 #include <IDocumentDeviceAccess.hxx>
61 #include <view.hxx>
62 #include <dbmgr.hxx>
63 #include <unotxdoc.hxx>
64 #include <wrtsh.hxx>
65 #include <mmconfigitem.hxx>
66 #include <mailmergehelper.hxx>
67 
68 #include <iodetect.hxx>
69 
70 #include <memory>
71 
72 using namespace ::com::sun::star;
73 using namespace ::com::sun::star::frame;
74 using namespace ::com::sun::star::uno;
75 using namespace ::com::sun::star::lang;
76 using namespace ::com::sun::star::beans;
77 using namespace ::com::sun::star::text;
78 using namespace SWUnoHelper;
79 
80 typedef ::utl::SharedUNOComponent< XInterface > SharedComponent;
81 
GetMailMergeMutex()82 static osl::Mutex &    GetMailMergeMutex()
83 {
84     static osl::Mutex   aMutex;
85     return aMutex;
86 }
87 
88 namespace {
89 
90 enum CloseResult
91 {
92     eSuccess,       // successfully closed
93     eVetoed,        // vetoed, ownership transferred to the vetoing instance
94     eFailed         // failed for some unknown reason
95 };
96 
97 }
98 
CloseModelAndDocSh(Reference<frame::XModel> const & rxModel,SfxObjectShellRef & rxDocSh)99 static CloseResult CloseModelAndDocSh(
100        Reference< frame::XModel > const &rxModel,
101        SfxObjectShellRef &rxDocSh )
102 {
103     CloseResult eResult = eSuccess;
104 
105     rxDocSh = nullptr;
106 
107     //! models/documents should never be disposed (they may still be
108     //! used for printing which is called asynchronously for example)
109     //! instead call close
110     Reference< util::XCloseable > xClose( rxModel, UNO_QUERY );
111     if (xClose.is())
112     {
113         try
114         {
115             //! 'sal_True' -> transfer ownership to vetoing object if vetoed!
116             //! I.e. now that object is responsible for closing the model and doc shell.
117             xClose->close( true );
118         }
119         catch (const util::CloseVetoException&)
120         {
121             //! here we have the problem that the temporary file that is
122             //! currently being printed will never be deleted. :-(
123             eResult = eVetoed;
124         }
125         catch (const uno::RuntimeException&)
126         {
127             eResult = eFailed;
128         }
129     }
130     return eResult;
131 }
132 
133 /// @throws RuntimeException
LoadFromURL_impl(Reference<frame::XModel> & rxModel,SfxObjectShellRef & rxDocSh,const OUString & rURL,bool bClose)134 static bool LoadFromURL_impl(
135         Reference< frame::XModel > &rxModel,
136         SfxObjectShellRef &rxDocSh,
137         const OUString &rURL,
138         bool bClose )
139 {
140     // try to open the document readonly and hidden
141     Reference< frame::XModel > xTmpModel;
142     Sequence < PropertyValue > aArgs{ comphelper::makePropertyValue(u"Hidden"_ustr, true) };
143     try
144     {
145         Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
146         xTmpModel.set( xDesktop->loadComponentFromURL( rURL, u"_blank"_ustr, 0, aArgs ), UNO_QUERY );
147     }
148     catch (const Exception&)
149     {
150         return false;
151     }
152 
153     // try to get the DocShell
154     SwDocShell *pTmpDocShell = nullptr;
155     if (auto pTextDoc = comphelper::getFromUnoTunnel<SwXTextDocument>(xTmpModel); pTextDoc)
156         pTmpDocShell = pTextDoc->GetDocShell();
157 
158     bool bRes = false;
159     if (xTmpModel.is() && pTmpDocShell)    // everything available?
160     {
161         if (bClose)
162             CloseModelAndDocSh( rxModel, rxDocSh );
163         // set new stuff
164         rxModel = xTmpModel;
165         rxDocSh = pTmpDocShell;
166         bRes = true;
167     }
168     else
169     {
170         // SfxObjectShellRef is ok here, since the document will be explicitly closed
171         SfxObjectShellRef xTmpDocSh = pTmpDocShell;
172         CloseModelAndDocSh( xTmpModel, xTmpDocSh );
173     }
174 
175     return bRes;
176 }
177 
178 namespace
179 {
180     class DelayedFileDeletion : public ::cppu::WeakImplHelper<util::XCloseListener>
181     {
182     protected:
183         ::osl::Mutex                    m_aMutex;
184         Reference< util::XCloseable >   m_xDocument;
185         Timer                           m_aDeleteTimer;
186         OUString                        m_sTemporaryFile;
187         sal_Int32                       m_nPendingDeleteAttempts;
188 
189         DelayedFileDeletion(DelayedFileDeletion const&) = delete;
190         DelayedFileDeletion& operator=(DelayedFileDeletion const&) = delete;
191 
192     public:
193         DelayedFileDeletion( const Reference< XModel >& _rxModel,
194                              OUString _aTemporaryFile );
195 
196     protected:
197         virtual ~DelayedFileDeletion( ) override;
198 
199         // XCloseListener
200         virtual void SAL_CALL queryClosing( const EventObject& _rSource, sal_Bool _bGetsOwnership ) override;
201         virtual void SAL_CALL notifyClosing( const EventObject& _rSource ) override;
202 
203         // XEventListener
204         virtual void SAL_CALL disposing( const EventObject& Source ) override;
205 
206     private:
207         void implTakeOwnership( );
208         DECL_LINK( OnTryDeleteFile, Timer*, void );
209     };
210 
DelayedFileDeletion(const Reference<XModel> & _rxModel,OUString _aTemporaryFile)211     DelayedFileDeletion::DelayedFileDeletion( const Reference< XModel >& _rxModel, OUString  _aTemporaryFile )
212         :
213         m_xDocument( _rxModel, UNO_QUERY )
214         ,m_aDeleteTimer("sw DelayedFileDeletion m_aDeleteTimer")
215         ,m_sTemporaryFile(std::move( _aTemporaryFile ))
216         ,m_nPendingDeleteAttempts( 0 )
217     {
218         osl_atomic_increment( &m_refCount );
219         try
220         {
221             if ( m_xDocument.is() )
222             {
223                 m_xDocument->addCloseListener( this );
224                 // successfully added -> keep ourself alive
225                 acquire();
226             }
227             else {
228                 OSL_FAIL("DelayedFileDeletion::DelayedFileDeletion: model is no component!" );
229             }
230         }
231         catch (const Exception&)
232         {
233             OSL_FAIL("DelayedFileDeletion::DelayedFileDeletion: could not register as event listener at the model!" );
234         }
235         osl_atomic_decrement( &m_refCount );
236     }
237 
IMPL_LINK_NOARG(DelayedFileDeletion,OnTryDeleteFile,Timer *,void)238     IMPL_LINK_NOARG(DelayedFileDeletion, OnTryDeleteFile, Timer *, void)
239     {
240         ::osl::ClearableMutexGuard aGuard( m_aMutex );
241 
242         bool bSuccess = false;
243         try
244         {
245             bool bDeliverOwnership = ( 0 == m_nPendingDeleteAttempts );
246                 // if this is our last attempt, then anybody which vetoes this has to take the consequences
247                 // (means take the ownership)
248             m_xDocument->close( bDeliverOwnership );
249             bSuccess = true;
250         }
251         catch (const util::CloseVetoException&)
252         {
253             // somebody vetoed -> next try
254             if ( m_nPendingDeleteAttempts )
255             {
256                 // next attempt
257                 --m_nPendingDeleteAttempts;
258                 m_aDeleteTimer.Start();
259             }
260             else
261                 bSuccess = true;    // can't do anything here ...
262         }
263         catch (const Exception&)
264         {
265             TOOLS_WARN_EXCEPTION( "sw", "DelayedFileDeletion::OnTryDeleteFile: caught a strange exception!" );
266             bSuccess = true;
267                 // can't do anything here ...
268         }
269 
270         if ( bSuccess )
271         {
272             SWUnoHelper::UCB_DeleteFile( m_sTemporaryFile );
273             aGuard.clear();
274             release();  // this should be our last reference, we should be dead after this
275         }
276     }
277 
implTakeOwnership()278     void DelayedFileDeletion::implTakeOwnership( )
279     {
280         // revoke ourself as listener
281         try
282         {
283             m_xDocument->removeCloseListener( this );
284         }
285         catch (const Exception&)
286         {
287             OSL_FAIL("DelayedFileDeletion::implTakeOwnership: could not revoke the listener!" );
288         }
289 
290         m_aDeleteTimer.SetTimeout( 3000 );  // 3 seconds
291         m_aDeleteTimer.SetInvokeHandler( LINK( this, DelayedFileDeletion, OnTryDeleteFile ) );
292         m_nPendingDeleteAttempts = 3;   // try 3 times at most
293         m_aDeleteTimer.Start( );
294     }
295 
queryClosing(const EventObject &,sal_Bool _bGetsOwnership)296     void SAL_CALL DelayedFileDeletion::queryClosing( const EventObject& , sal_Bool _bGetsOwnership )
297     {
298         ::osl::MutexGuard aGuard( m_aMutex );
299         if ( _bGetsOwnership )
300             implTakeOwnership( );
301 
302         // always veto: We want to take the ownership ourself, as this is the only chance to delete
303         // the temporary file which the model is based on
304         throw util::CloseVetoException( );
305     }
306 
notifyClosing(const EventObject &)307     void SAL_CALL DelayedFileDeletion::notifyClosing( const EventObject&  )
308     {
309         OSL_FAIL("DelayedFileDeletion::notifyClosing: how this?" );
310         // this should not happen:
311         // Either, a foreign instance closes the document, then we should veto this, and take the ownership
312         // Or, we ourself close the document, then we should not be a listener anymore
313     }
314 
disposing(const EventObject &)315     void SAL_CALL DelayedFileDeletion::disposing( const EventObject&  )
316     {
317         OSL_FAIL("DelayedFileDeletion::disposing: how this?" );
318         // this should not happen:
319         // Either, a foreign instance closes the document, then we should veto this, and take the ownership
320         // Or, we ourself close the document, then we should not be a listener anymore
321     }
322 
~DelayedFileDeletion()323     DelayedFileDeletion::~DelayedFileDeletion( )
324     {
325     }
326 }
327 
DeleteTmpFile_Impl(Reference<frame::XModel> & rxModel,SfxObjectShellRef & rxDocSh,const OUString & rTmpFileURL)328 static bool DeleteTmpFile_Impl(
329         Reference< frame::XModel > &rxModel,
330         SfxObjectShellRef &rxDocSh,
331         const OUString &rTmpFileURL )
332 {
333     bool bRes = false;
334     if (!rTmpFileURL.isEmpty())
335     {
336         bool bDelete = true;
337         if ( eVetoed == CloseModelAndDocSh( rxModel, rxDocSh ) )
338         {
339             // somebody vetoed the closing, and took the ownership of the document
340             // -> ensure that the temporary file is deleted later on
341             new DelayedFileDeletion( rxModel, rTmpFileURL );
342                 // note: as soon as #106931# is fixed, the whole DelayedFileDeletion is to be superseded by
343                 // a better solution
344             bDelete = false;
345         }
346 
347         rxModel = nullptr;
348         rxDocSh = nullptr; // destroy doc shell
349 
350         if ( bDelete )
351         {
352             if ( !SWUnoHelper::UCB_DeleteFile( rTmpFileURL ) )
353             {
354                 new DelayedFileDeletion( rxModel, rTmpFileURL );
355                     // same not as above: as soon as #106931#, ...
356             }
357         }
358         else
359             bRes = true;    // file will be deleted delayed
360     }
361     return bRes;
362 }
363 
SwXMailMerge()364 SwXMailMerge::SwXMailMerge() :
365     m_aEvtListeners   ( GetMailMergeMutex() ),
366     m_aMergeListeners ( GetMailMergeMutex() ),
367     m_aPropListeners  ( GetMailMergeMutex() ),
368     m_pPropSet( aSwMapProvider.GetPropertySet( PROPERTY_MAP_MAILMERGE ) ),
369     m_nDataCommandType(sdb::CommandType::TABLE),
370     m_nOutputType(MailMergeType::PRINTER),
371     m_bEscapeProcessing(true),     //!! allow to process properties like "Filter", "Order", ...
372     m_bFileNameFromColumn(false),
373     m_bSendAsHTML(false),
374     m_bSendAsAttachment(false),
375     m_bSaveAsSingleFile(false),
376     m_bDisposing(false),
377     m_pMgr(nullptr)
378 {
379     // create empty document
380     // like in: SwModule::InsertEnv (appenv.cxx)
381     m_xDocSh = new SwDocShell( SfxObjectCreateMode::STANDARD );
382     m_xDocSh->DoInitNew();
383     SfxViewFrame *pFrame = SfxViewFrame::LoadHiddenDocument( *m_xDocSh, SFX_INTERFACE_NONE );
384     SwView *pView = static_cast<SwView*>( pFrame->GetViewShell() );
385     pView->AttrChangedNotify(nullptr); //So that SelectShell is called.
386     m_xModel = m_xDocSh->GetModel();
387 }
388 
~SwXMailMerge()389 SwXMailMerge::~SwXMailMerge()
390 {
391     if (!m_aTmpFileName.isEmpty())
392         DeleteTmpFile_Impl( m_xModel, m_xDocSh, m_aTmpFileName );
393     else    // there was no temporary file in use
394     {
395         //! we still need to close the model and doc shell manually
396         //! because there is no automatism that will do that later.
397         //! #120086#
398         if ( eVetoed == CloseModelAndDocSh( m_xModel, m_xDocSh ) )
399             OSL_FAIL("ownership transferred to vetoing object!" );
400 
401         m_xModel = nullptr;
402         m_xDocSh = nullptr; // destroy doc shell
403     }
404 }
405 
406 // Guarantee object consistence in case of an exception
407 class MailMergeExecuteFinalizer
408 {
409 public:
MailMergeExecuteFinalizer(SwXMailMerge * mailmerge)410     explicit MailMergeExecuteFinalizer(SwXMailMerge *mailmerge)
411         : m_pMailMerge(mailmerge)
412     {
413         assert(m_pMailMerge); //mailmerge object missing
414     }
~MailMergeExecuteFinalizer()415     ~MailMergeExecuteFinalizer()
416     {
417         osl::MutexGuard aMgrGuard( GetMailMergeMutex() );
418         m_pMailMerge->m_pMgr = nullptr;
419     }
420 
421 private:
422     MailMergeExecuteFinalizer(MailMergeExecuteFinalizer const&) = delete;
423     MailMergeExecuteFinalizer& operator=(MailMergeExecuteFinalizer const&) = delete;
424 
425     SwXMailMerge *m_pMailMerge;
426 };
427 
execute(const uno::Sequence<beans::NamedValue> & rArguments)428 uno::Any SAL_CALL SwXMailMerge::execute(
429         const uno::Sequence< beans::NamedValue >& rArguments )
430 {
431     SolarMutexGuard aGuard;
432     MailMergeExecuteFinalizer aFinalizer(this);
433 
434     // get property values to be used
435     // (use values from the service as default and override them with
436     // the values that are provided as arguments)
437 
438     uno::Sequence< uno::Any >           aCurSelection   = m_aSelection;
439     uno::Reference< sdbc::XResultSet >  xCurResultSet   = m_xResultSet;
440     uno::Reference< sdbc::XConnection > xCurConnection  = m_xConnection;
441     uno::Reference< frame::XModel >     xCurModel       = m_xModel;
442     OUString   aCurDataSourceName       = m_aDataSourceName;
443     OUString   aCurDataCommand          = m_aDataCommand;
444     OUString   aCurFilter               = m_aFilter;
445     OUString   aCurDocumentURL          = m_aDocumentURL;
446     OUString   aCurOutputURL            = m_aOutputURL;
447     OUString   aCurFileNamePrefix       = m_aFileNamePrefix;
448     sal_Int32  nCurDataCommandType      = m_nDataCommandType;
449     sal_Int16  nCurOutputType           = m_nOutputType;
450     bool   bCurEscapeProcessing     = m_bEscapeProcessing;
451     bool   bCurFileNameFromColumn   = m_bFileNameFromColumn;
452 
453     SfxObjectShellRef xCurDocSh = m_xDocSh;   // the document
454 
455     for (const beans::NamedValue& rArgument : rArguments)
456     {
457         const OUString &rName   = rArgument.Name;
458         const Any &rValue       = rArgument.Value;
459 
460         bool bOK = true;
461         if (rName == UNO_NAME_SELECTION)
462             bOK = rValue >>= aCurSelection;
463         else if (rName == UNO_NAME_RESULT_SET)
464             bOK = rValue >>= xCurResultSet;
465         else if (rName == UNO_NAME_CONNECTION)
466             bOK = rValue >>= xCurConnection;
467         else if (rName == UNO_NAME_MODEL)
468             throw PropertyVetoException("Property is read-only: " + rName, getXWeak() );
469         else if (rName == UNO_NAME_DATA_SOURCE_NAME)
470             bOK = rValue >>= aCurDataSourceName;
471         else if (rName == UNO_NAME_DAD_COMMAND)
472             bOK = rValue >>= aCurDataCommand;
473         else if (rName == UNO_NAME_FILTER)
474             bOK = rValue >>= aCurFilter;
475         else if (rName == UNO_NAME_DOCUMENT_URL)
476         {
477             bOK = rValue >>= aCurDocumentURL;
478             if (!aCurDocumentURL.isEmpty()
479                 && !LoadFromURL_impl( xCurModel, xCurDocSh, aCurDocumentURL, false ))
480                 throw RuntimeException("Failed to create document from URL: " + aCurDocumentURL, getXWeak() );
481         }
482         else if (rName == UNO_NAME_OUTPUT_URL)
483         {
484             bOK = rValue >>= aCurOutputURL;
485             if (!aCurOutputURL.isEmpty())
486             {
487                 if (!UCB_IsDirectory(aCurOutputURL))
488                     throw IllegalArgumentException("URL does not point to a directory: " + aCurOutputURL, getXWeak(), 0 );
489                 if (UCB_IsReadOnlyFileName(aCurOutputURL))
490                     throw IllegalArgumentException("URL is read-only: " + aCurOutputURL, getXWeak(), 0 );
491             }
492         }
493         else if (rName == UNO_NAME_FILE_NAME_PREFIX)
494             bOK = rValue >>= aCurFileNamePrefix;
495         else if (rName == UNO_NAME_DAD_COMMAND_TYPE)
496             bOK = rValue >>= nCurDataCommandType;
497         else if (rName == UNO_NAME_OUTPUT_TYPE)
498             bOK = rValue >>= nCurOutputType;
499         else if (rName == UNO_NAME_ESCAPE_PROCESSING)
500             bOK = rValue >>= bCurEscapeProcessing;
501         else if (rName == UNO_NAME_FILE_NAME_FROM_COLUMN)
502             bOK = rValue >>= bCurFileNameFromColumn;
503         else if (rName == UNO_NAME_SUBJECT)
504             bOK = rValue >>= m_sSubject;
505         else if (rName == UNO_NAME_ADDRESS_FROM_COLUMN)
506             bOK = rValue >>= m_sAddressFromColumn;
507         else if (rName == UNO_NAME_SEND_AS_HTML)
508             bOK = rValue >>= m_bSendAsHTML;
509         else if (rName == UNO_NAME_MAIL_BODY)
510             bOK = rValue >>= m_sMailBody;
511         else if (rName == UNO_NAME_ATTACHMENT_NAME)
512             bOK = rValue >>= m_sAttachmentName;
513         else if (rName == UNO_NAME_ATTACHMENT_FILTER)
514             bOK = rValue >>= m_sAttachmentFilter;
515         else if (rName == UNO_NAME_COPIES_TO)
516             bOK = rValue >>= m_aCopiesTo;
517         else if (rName == UNO_NAME_BLIND_COPIES_TO)
518             bOK = rValue >>= m_aBlindCopiesTo;
519         else if (rName == UNO_NAME_SEND_AS_ATTACHMENT)
520             bOK = rValue >>= m_bSendAsAttachment;
521         else if (rName == UNO_NAME_PRINT_OPTIONS)
522             bOK = rValue >>= m_aPrintSettings;
523         else if (rName == UNO_NAME_SAVE_AS_SINGLE_FILE)
524             bOK = rValue >>= m_bSaveAsSingleFile;
525         else if (rName == UNO_NAME_SAVE_FILTER)
526             bOK = rValue >>= m_sSaveFilter;
527         else if (rName == UNO_NAME_SAVE_FILTER_OPTIONS)
528             bOK = rValue >>= m_sSaveFilterOptions;
529         else if (rName == UNO_NAME_SAVE_FILTER_DATA)
530             bOK = rValue >>= m_aSaveFilterData;
531         else if (rName == UNO_NAME_IN_SERVER_PASSWORD)
532             bOK = rValue >>= m_sInServerPassword;
533         else if (rName == UNO_NAME_OUT_SERVER_PASSWORD)
534             bOK = rValue >>= m_sOutServerPassword;
535         else
536             throw UnknownPropertyException( "Property is unknown: " + rName, getXWeak() );
537 
538         if (!bOK)
539             throw IllegalArgumentException("Property type mismatch or property not set: " + rName, getXWeak(), 0 );
540     }
541 
542     // need to translate the selection: the API here requires a sequence of bookmarks, but the Merge
543     // method we will call below requires a sequence of indices.
544     if ( aCurSelection.hasElements() )
545     {
546         Sequence< Any > aTranslated( aCurSelection.getLength() );
547 
548         bool bValid = false;
549         Reference< sdbcx::XRowLocate > xRowLocate( xCurResultSet, UNO_QUERY );
550         if ( xRowLocate.is() )
551         {
552             Any* pTranslated = aTranslated.getArray();
553 
554             try
555             {
556                 bool bEverythingsFine = true;
557                 for (const Any& rBookmark : aCurSelection)
558                 {
559                     bEverythingsFine = xRowLocate->moveToBookmark( rBookmark );
560                     if ( !bEverythingsFine )
561                         break;
562                     *pTranslated <<= xCurResultSet->getRow();
563                     ++pTranslated;
564                 }
565                 if ( bEverythingsFine )
566                     bValid = true;
567             }
568             catch (const Exception&)
569             {
570                 bValid = false;
571             }
572         }
573 
574         if ( !bValid )
575         {
576             throw IllegalArgumentException(
577                 u"The current 'Selection' does not describe a valid array of bookmarks, relative to the current 'ResultSet'."_ustr,
578                 getXWeak(),
579                 0
580             );
581         }
582 
583         aCurSelection = aTranslated;
584     }
585 
586     SfxViewFrame*   pFrame = SfxViewFrame::GetFirst( xCurDocSh.get(), false);
587     SwView *pView = pFrame ? dynamic_cast<SwView*>( pFrame->GetViewShell()  ) : nullptr;
588     if (!pView)
589         throw RuntimeException();
590 
591     // avoid assertion in 'Update' from Sfx by supplying a shell
592     // and thus avoiding the SelectShell call in Writers GetState function
593     // while still in Update of Sfx.
594     // (GetSelection in Update is not allowed)
595     if (!aCurDocumentURL.isEmpty())
596         pView->AttrChangedNotify(nullptr);//So that SelectShell is called.
597 
598     SharedComponent aRowSetDisposeHelper;
599     if (!xCurResultSet.is())
600     {
601         if (aCurDataSourceName.isEmpty() || aCurDataCommand.isEmpty() )
602         {
603             OSL_FAIL("PropertyValues missing or unset");
604             throw IllegalArgumentException(u"Either the ResultSet or DataSourceName and DataCommand must be set."_ustr, getXWeak(), 0 );
605         }
606 
607         // build ResultSet from DataSourceName, DataCommand and DataCommandType
608 
609         Reference< XMultiServiceFactory > xMgr( ::comphelper::getProcessServiceFactory() );
610         if (xMgr.is())
611         {
612             Reference< XInterface > xInstance = xMgr->createInstance( u"com.sun.star.sdb.RowSet"_ustr );
613             aRowSetDisposeHelper.reset( xInstance, SharedComponent::TakeOwnership );
614             Reference< XPropertySet > xRowSetPropSet( xInstance, UNO_QUERY );
615             OSL_ENSURE( xRowSetPropSet.is(), "failed to get XPropertySet interface from RowSet" );
616             if (xRowSetPropSet.is())
617             {
618                 if (xCurConnection.is())
619                     xRowSetPropSet->setPropertyValue( u"ActiveConnection"_ustr,  Any( xCurConnection ) );
620                 xRowSetPropSet->setPropertyValue( u"DataSourceName"_ustr,    Any( aCurDataSourceName ) );
621                 xRowSetPropSet->setPropertyValue( u"Command"_ustr,           Any( aCurDataCommand ) );
622                 xRowSetPropSet->setPropertyValue( u"CommandType"_ustr,       Any( nCurDataCommandType ) );
623                 xRowSetPropSet->setPropertyValue( u"EscapeProcessing"_ustr,  Any( bCurEscapeProcessing ) );
624                 xRowSetPropSet->setPropertyValue( u"ApplyFilter"_ustr,       Any( true ) );
625                 xRowSetPropSet->setPropertyValue( u"Filter"_ustr,            Any( aCurFilter ) );
626 
627                 Reference< sdbc::XRowSet > xRowSet( xInstance, UNO_QUERY );
628                 if (xRowSet.is())
629                     xRowSet->execute(); // build ResultSet from properties
630                 if( !xCurConnection.is() )
631                     xCurConnection.set( xRowSetPropSet->getPropertyValue( u"ActiveConnection"_ustr ), UNO_QUERY );
632                 xCurResultSet = xRowSet;
633                 OSL_ENSURE( xCurResultSet.is(), "failed to build ResultSet" );
634             }
635         }
636     }
637 
638     svx::ODataAccessDescriptor aDescriptor;
639     aDescriptor.setDataSource(aCurDataSourceName);
640     aDescriptor[ svx::DataAccessDescriptorProperty::Connection ]         <<= xCurConnection;
641     aDescriptor[ svx::DataAccessDescriptorProperty::Command ]            <<= aCurDataCommand;
642     aDescriptor[ svx::DataAccessDescriptorProperty::CommandType ]        <<= nCurDataCommandType;
643     aDescriptor[ svx::DataAccessDescriptorProperty::EscapeProcessing ]   <<= bCurEscapeProcessing;
644     aDescriptor[ svx::DataAccessDescriptorProperty::Cursor ]             <<= xCurResultSet;
645     // aDescriptor[ svx::DataAccessDescriptorProperty::ColumnName ]      not used
646     // aDescriptor[ svx::DataAccessDescriptorProperty::ColumnObject ]    not used
647     aDescriptor[ svx::DataAccessDescriptorProperty::Selection ]          <<= aCurSelection;
648 
649     DBManagerOptions nMergeType;
650     switch (nCurOutputType)
651     {
652         case MailMergeType::PRINTER : nMergeType = DBMGR_MERGE_PRINTER; break;
653         case MailMergeType::FILE    : nMergeType = DBMGR_MERGE_FILE; break;
654         case MailMergeType::MAIL    : nMergeType = DBMGR_MERGE_EMAIL; break;
655         case MailMergeType::SHELL   : nMergeType = DBMGR_MERGE_SHELL; break;
656         default:
657             throw IllegalArgumentException(u"Invalid value of property: OutputType"_ustr, getXWeak(), 0 );
658     }
659 
660     SwWrtShell &rSh = pView->GetWrtShell();
661     SwDBManager* pMgr = rSh.GetDBManager();
662     //force layout creation
663     rSh.CalcLayout();
664     OSL_ENSURE( pMgr, "database manager missing" );
665     m_pMgr = pMgr;
666 
667     SwMergeDescriptor aMergeDesc( nMergeType, rSh, aDescriptor );
668 
669     std::unique_ptr< SwMailMergeConfigItem > pMMConfigItem;
670     uno::Reference< mail::XMailService > xInService;
671     switch (nCurOutputType)
672     {
673     case MailMergeType::PRINTER:
674         {
675             // #i25686# printing should not be done asynchronously to prevent dangling offices
676             // when mail merge is called as command line macro
677             aMergeDesc.aPrintOptions = m_aPrintSettings;
678             aMergeDesc.bCreateSingleFile = true;
679         }
680         break;
681     case MailMergeType::SHELL:
682         aMergeDesc.bCreateSingleFile = true;
683         pMMConfigItem.reset(new SwMailMergeConfigItem);
684         aMergeDesc.pMailMergeConfigItem = pMMConfigItem.get();
685         break;
686     case MailMergeType::FILE:
687         {
688             INetURLObject aURLObj;
689             aURLObj.SetSmartProtocol( INetProtocol::File );
690 
691             if (!aCurDocumentURL.isEmpty())
692             {
693                 // if OutputURL or FileNamePrefix are missing get
694                 // them from DocumentURL
695                 aURLObj.SetSmartURL( aCurDocumentURL );
696                 if (aCurFileNamePrefix.isEmpty())
697                     aCurFileNamePrefix = aURLObj.GetBase(); // filename without extension
698                 if (aCurOutputURL.isEmpty())
699                 {
700                     aURLObj.removeSegment();
701                     aCurOutputURL = aURLObj.GetMainURL( INetURLObject::DecodeMechanism::ToIUri );
702                 }
703             }
704             else    // default empty document without URL
705             {
706                 if (aCurOutputURL.isEmpty())
707                     throw RuntimeException(u"OutputURL is not set and can not be obtained."_ustr, getXWeak() );
708             }
709 
710             aURLObj.SetSmartURL( aCurOutputURL );
711             OUString aPath = aURLObj.GetMainURL( INetURLObject::DecodeMechanism::ToIUri );
712 
713             static constexpr OUString aDelim( u"/"_ustr );
714             if (!aPath.isEmpty() && !aPath.endsWith(aDelim))
715                 aPath += aDelim;
716             if (bCurFileNameFromColumn)
717                 aMergeDesc.sDBcolumn = aCurFileNamePrefix;
718             else
719             {
720                 aPath += aCurFileNamePrefix;
721             }
722 
723             aMergeDesc.sPrefix = aPath;
724             aMergeDesc.sSaveToFilter = m_sSaveFilter;
725             aMergeDesc.sSaveToFilterOptions = m_sSaveFilterOptions;
726             aMergeDesc.aSaveToFilterData = m_aSaveFilterData;
727             aMergeDesc.bCreateSingleFile = m_bSaveAsSingleFile;
728         }
729         break;
730     case MailMergeType::MAIL:
731             {
732                 aMergeDesc.sDBcolumn = m_sAddressFromColumn;
733                 if(m_sAddressFromColumn.isEmpty())
734                     throw RuntimeException(u"Mail address column not set."_ustr, getXWeak() );
735                 aMergeDesc.sSaveToFilter     = m_sAttachmentFilter;
736                 aMergeDesc.sSubject          = m_sSubject;
737                 aMergeDesc.sMailBody         = m_sMailBody;
738                 aMergeDesc.sAttachmentName   = m_sAttachmentName;
739                 aMergeDesc.aCopiesTo         = m_aCopiesTo;
740                 aMergeDesc.aBlindCopiesTo    = m_aBlindCopiesTo;
741                 aMergeDesc.bSendAsHTML       = m_bSendAsHTML;
742                 aMergeDesc.bSendAsAttachment = m_bSendAsAttachment;
743 
744                 aMergeDesc.bCreateSingleFile = false;
745                 pMMConfigItem.reset(new SwMailMergeConfigItem);
746                 aMergeDesc.pMailMergeConfigItem = pMMConfigItem.get();
747                 aMergeDesc.xSmtpServer = SwMailMergeHelper::ConnectToSmtpServer(
748                         *pMMConfigItem,
749                         xInService,
750                         m_sInServerPassword, m_sOutServerPassword );
751                 if( !aMergeDesc.xSmtpServer.is() || !aMergeDesc.xSmtpServer->isConnected())
752                     throw RuntimeException(u"Failed to connect to mail server."_ustr, getXWeak() );
753             }
754         break;
755     }
756 
757     // save document with temporary filename
758     std::shared_ptr<const SfxFilter> pSfxFlt = SwIoSystem::GetFilterOfFormat(
759             FILTER_XML,
760             SwDocShell::Factory().GetFilterContainer() );
761     OUString aExtension(comphelper::string::stripStart(pSfxFlt->GetDefaultExtension(), '*'));
762     m_aTmpFileName = utl::CreateTempURL( u"SwMM", true, aExtension );
763 
764     Reference< XStorable > xStorable( xCurModel, UNO_QUERY );
765     bool bStoredAsTemporary = false;
766     if ( xStorable.is() )
767     {
768         try
769         {
770             xStorable->storeAsURL( m_aTmpFileName, Sequence< PropertyValue >() );
771             bStoredAsTemporary = true;
772         }
773         catch (const Exception&)
774         {
775         }
776     }
777     if ( !bStoredAsTemporary )
778         throw RuntimeException(u"Failed to save temporary file."_ustr, getXWeak() );
779 
780     pMgr->SetMergeSilent( true );       // suppress dialogs, message boxes, etc.
781     const SwXMailMerge *pOldSrc = pMgr->GetMailMergeEvtSrc();
782     OSL_ENSURE( !pOldSrc || pOldSrc == this, "Ooops... different event source already set." );
783     pMgr->SetMailMergeEvtSrc( this );   // launch events for listeners
784 
785     SfxGetpApp()->NotifyEvent(SfxEventHint(SfxEventHintId::SwMailMerge, SwDocShell::GetEventName(STR_SW_EVENT_MAIL_MERGE), xCurDocSh.get()));
786     bool bSucc = pMgr->Merge( aMergeDesc );
787     SfxGetpApp()->NotifyEvent(SfxEventHint(SfxEventHintId::SwMailMergeEnd, SwDocShell::GetEventName(STR_SW_EVENT_MAIL_MERGE_END), xCurDocSh.get()));
788 
789     pMgr->SetMailMergeEvtSrc( pOldSrc );
790 
791     if ( xCurModel.get() != m_xModel.get() )
792     {   // in case it was a temporary model -> close it, and delete the file
793         DeleteTmpFile_Impl( xCurModel, xCurDocSh, m_aTmpFileName );
794         m_aTmpFileName.clear();
795     }
796     // (in case it wasn't a temporary model, it will be closed in the dtor, at the latest)
797 
798     if (!bSucc)
799         throw Exception(u"Mail merge failed. Sorry, no further information available."_ustr, getXWeak() );
800 
801     //de-initialize services
802     if(xInService.is() && xInService->isConnected())
803         xInService->disconnect();
804     if(aMergeDesc.xSmtpServer.is() && aMergeDesc.xSmtpServer->isConnected())
805         aMergeDesc.xSmtpServer->disconnect();
806 
807     if (DBMGR_MERGE_SHELL == nMergeType)
808     {
809         return Any( aMergeDesc.pMailMergeConfigItem->GetTargetView()->GetDocShell()->GetBaseModel() );
810     }
811     else
812         return Any( true );
813 }
814 
cancel()815 void SAL_CALL SwXMailMerge::cancel()
816 {
817     // Cancel may be called from a second thread, so this protects from m_pMgr
818     /// cleanup in the execute function.
819     osl::MutexGuard aMgrGuard( GetMailMergeMutex() );
820     if (m_pMgr)
821         m_pMgr->MergeCancel();
822 }
823 
LaunchMailMergeEvent(const MailMergeEvent & rEvt) const824 void SwXMailMerge::LaunchMailMergeEvent( const MailMergeEvent &rEvt ) const
825 {
826     comphelper::OInterfaceIteratorHelper2 aIt( const_cast<SwXMailMerge *>(this)->m_aMergeListeners );
827     while (aIt.hasMoreElements())
828     {
829         static_cast< XMailMergeListener* >( aIt.next() )->notifyMailMergeEvent( rEvt );
830     }
831 }
832 
launchEvent(const PropertyChangeEvent & rEvt) const833 void SwXMailMerge::launchEvent( const PropertyChangeEvent &rEvt ) const
834 {
835     comphelper::OInterfaceContainerHelper3<XPropertyChangeListener> *pContainer =
836             m_aPropListeners.getContainer( rEvt.PropertyHandle );
837     if (pContainer)
838     {
839         pContainer->notifyEach( &XPropertyChangeListener::propertyChange, rEvt );
840     }
841 }
842 
getPropertySetInfo()843 uno::Reference< beans::XPropertySetInfo > SAL_CALL SwXMailMerge::getPropertySetInfo(  )
844 {
845     SolarMutexGuard aGuard;
846     static Reference< XPropertySetInfo > aRef = m_pPropSet->getPropertySetInfo();
847     return aRef;
848 }
849 
setPropertyValue(const OUString & rPropertyName,const uno::Any & rValue)850 void SAL_CALL SwXMailMerge::setPropertyValue(
851         const OUString& rPropertyName, const uno::Any& rValue )
852 {
853     SolarMutexGuard aGuard;
854 
855     const SfxItemPropertyMapEntry* pCur = m_pPropSet->getPropertyMap().getByName( rPropertyName );
856     if (!pCur)
857         throw UnknownPropertyException(rPropertyName);
858     else if (pCur->nFlags & PropertyAttribute::READONLY)
859         throw PropertyVetoException();
860     else
861     {
862         void *pData = nullptr;
863         switch (pCur->nWID)
864         {
865             case WID_SELECTION :                pData = &m_aSelection;  break;
866             case WID_RESULT_SET :               pData = &m_xResultSet;  break;
867             case WID_CONNECTION :               pData = &m_xConnection;  break;
868             case WID_MODEL :                    pData = &m_xModel;  break;
869             case WID_DATA_SOURCE_NAME :         pData = &m_aDataSourceName;  break;
870             case WID_DATA_COMMAND :             pData = &m_aDataCommand;  break;
871             case WID_FILTER :                   pData = &m_aFilter;  break;
872             case WID_DOCUMENT_URL :             pData = &m_aDocumentURL;  break;
873             case WID_OUTPUT_URL :               pData = &m_aOutputURL;  break;
874             case WID_DATA_COMMAND_TYPE :        pData = &m_nDataCommandType;  break;
875             case WID_OUTPUT_TYPE :              pData = &m_nOutputType;  break;
876             case WID_ESCAPE_PROCESSING :        pData = &m_bEscapeProcessing;  break;
877             case WID_FILE_NAME_FROM_COLUMN :    pData = &m_bFileNameFromColumn;  break;
878             case WID_FILE_NAME_PREFIX :         pData = &m_aFileNamePrefix;  break;
879             case WID_MAIL_SUBJECT:              pData = &m_sSubject; break;
880             case WID_ADDRESS_FROM_COLUMN:       pData = &m_sAddressFromColumn; break;
881             case WID_SEND_AS_HTML:              pData = &m_bSendAsHTML; break;
882             case WID_SEND_AS_ATTACHMENT:        pData = &m_bSendAsAttachment; break;
883             case WID_MAIL_BODY:                 pData = &m_sMailBody; break;
884             case WID_ATTACHMENT_NAME:           pData = &m_sAttachmentName; break;
885             case WID_ATTACHMENT_FILTER:         pData = &m_sAttachmentFilter;break;
886             case WID_PRINT_OPTIONS:             pData = &m_aPrintSettings; break;
887             case WID_SAVE_AS_SINGLE_FILE:       pData = &m_bSaveAsSingleFile; break;
888             case WID_SAVE_FILTER:               pData = &m_sSaveFilter; break;
889             case WID_SAVE_FILTER_OPTIONS:       pData = &m_sSaveFilterOptions; break;
890             case WID_SAVE_FILTER_DATA:          pData = &m_aSaveFilterData; break;
891             case WID_COPIES_TO:                 pData = &m_aCopiesTo; break;
892             case WID_BLIND_COPIES_TO:           pData = &m_aBlindCopiesTo;break;
893             case WID_IN_SERVER_PASSWORD:        pData = &m_sInServerPassword; break;
894             case WID_OUT_SERVER_PASSWORD:       pData = &m_sOutServerPassword; break;
895             default :
896                 OSL_FAIL("unknown WID");
897         }
898         Any aOld( pData, pCur->aType );
899 
900         bool bChanged = false;
901         bool bOK = true;
902         if (aOld != rValue)
903         {
904             if (pData == &m_aSelection)
905                 bOK = rValue >>= m_aSelection;
906             else if (pData == &m_xResultSet)
907                 bOK = rValue >>= m_xResultSet;
908             else if (pData == &m_xConnection)
909                 bOK = rValue >>= m_xConnection;
910             else if (pData == &m_xModel)
911                 bOK = rValue >>= m_xModel;
912             else if (pData == &m_aDataSourceName)
913                 bOK = rValue >>= m_aDataSourceName;
914             else if (pData == &m_aDataCommand)
915                 bOK = rValue >>= m_aDataCommand;
916             else if (pData == &m_aFilter)
917                 bOK = rValue >>= m_aFilter;
918             else if (pData == &m_aDocumentURL)
919             {
920                 OUString aText;
921                 bOK = rValue >>= aText;
922                 if (!aText.isEmpty()
923                     && !LoadFromURL_impl( m_xModel, m_xDocSh, aText, true ))
924                     throw RuntimeException("Failed to create document from URL: " + aText, getXWeak() );
925                 m_aDocumentURL = aText;
926             }
927             else if (pData == &m_aOutputURL)
928             {
929                 OUString aText;
930                 bOK = rValue >>= aText;
931                 if (!aText.isEmpty())
932                 {
933                     if (!UCB_IsDirectory(aText))
934                         throw IllegalArgumentException("URL does not point to a directory: " + aText, getXWeak(), 0 );
935                     if (UCB_IsReadOnlyFileName(aText))
936                         throw IllegalArgumentException("URL is read-only: " + aText, getXWeak(), 0 );
937                 }
938                 m_aOutputURL = aText;
939             }
940             else if (pData == &m_nDataCommandType)
941                 bOK = rValue >>= m_nDataCommandType;
942             else if (pData == &m_nOutputType)
943                 bOK = rValue >>= m_nOutputType;
944             else if (pData == &m_bEscapeProcessing)
945                 bOK = rValue >>= m_bEscapeProcessing;
946             else if (pData == &m_bFileNameFromColumn)
947                 bOK = rValue >>= m_bFileNameFromColumn;
948             else if (pData == &m_aFileNamePrefix)
949                 bOK = rValue >>= m_aFileNamePrefix;
950             else if (pData == &m_sSubject)
951                 bOK = rValue >>= m_sSubject;
952             else if (pData == &m_sAddressFromColumn)
953                 bOK = rValue >>= m_sAddressFromColumn;
954             else if (pData == &m_bSendAsHTML)
955                 bOK = rValue >>= m_bSendAsHTML;
956             else if (pData == &m_bSendAsAttachment)
957                 bOK = rValue >>= m_bSendAsAttachment;
958             else if (pData == &m_sMailBody)
959                 bOK = rValue >>= m_sMailBody;
960             else if (pData == &m_sAttachmentName)
961                 bOK = rValue >>= m_sAttachmentName;
962             else if (pData == &m_sAttachmentFilter)
963                 bOK = rValue >>= m_sAttachmentFilter;
964             else if (pData == &m_aPrintSettings)
965                 bOK = rValue >>= m_aPrintSettings;
966             else if (pData == &m_bSaveAsSingleFile)
967                 bOK = rValue >>= m_bSaveAsSingleFile;
968             else if (pData == &m_sSaveFilter)
969                 bOK = rValue >>= m_sSaveFilter;
970             else if (pData == &m_sSaveFilterOptions)
971                 bOK = rValue >>= m_sSaveFilterOptions;
972             else if (pData == &m_aSaveFilterData)
973                 bOK = rValue >>= m_aSaveFilterData;
974             else if (pData == &m_aCopiesTo)
975                 bOK = rValue >>= m_aCopiesTo;
976             else if (pData == &m_aBlindCopiesTo)
977                 bOK = rValue >>= m_aBlindCopiesTo;
978             else if(pData == &m_sInServerPassword)
979                 bOK = rValue >>= m_sInServerPassword;
980             else if(pData == &m_sOutServerPassword)
981                 bOK = rValue >>= m_sOutServerPassword;
982             else {
983                 OSL_FAIL("invalid pointer" );
984             }
985             OSL_ENSURE( bOK, "set value failed" );
986             bChanged = true;
987         }
988         if (!bOK)
989             throw IllegalArgumentException("Property type mismatch or property not set: " + rPropertyName, getXWeak(), 0 );
990 
991         if (bChanged)
992         {
993             PropertyChangeEvent aChgEvt( static_cast<XPropertySet *>(this), rPropertyName,
994                     false, pCur->nWID, aOld, rValue );
995             launchEvent( aChgEvt );
996         }
997     }
998 }
999 
getPropertyValue(const OUString & rPropertyName)1000 uno::Any SAL_CALL SwXMailMerge::getPropertyValue(
1001         const OUString& rPropertyName )
1002 {
1003     SolarMutexGuard aGuard;
1004 
1005     Any aRet;
1006 
1007     const SfxItemPropertyMapEntry* pCur = m_pPropSet->getPropertyMap().getByName( rPropertyName );
1008     if (!pCur)
1009         throw UnknownPropertyException(rPropertyName);
1010 
1011     switch (pCur->nWID)
1012     {
1013         case WID_SELECTION :                aRet <<= m_aSelection;  break;
1014         case WID_RESULT_SET :               aRet <<= m_xResultSet;  break;
1015         case WID_CONNECTION :               aRet <<= m_xConnection;  break;
1016         case WID_MODEL :                    aRet <<= m_xModel;  break;
1017         case WID_DATA_SOURCE_NAME :         aRet <<= m_aDataSourceName;  break;
1018         case WID_DATA_COMMAND :             aRet <<= m_aDataCommand;  break;
1019         case WID_FILTER :                   aRet <<= m_aFilter;  break;
1020         case WID_DOCUMENT_URL :             aRet <<= m_aDocumentURL;  break;
1021         case WID_OUTPUT_URL :               aRet <<= m_aOutputURL;  break;
1022         case WID_DATA_COMMAND_TYPE :        aRet <<= m_nDataCommandType;  break;
1023         case WID_OUTPUT_TYPE :              aRet <<= m_nOutputType;  break;
1024         case WID_ESCAPE_PROCESSING :        aRet <<= m_bEscapeProcessing;  break;
1025         case WID_FILE_NAME_FROM_COLUMN :    aRet <<= m_bFileNameFromColumn;  break;
1026         case WID_FILE_NAME_PREFIX :         aRet <<= m_aFileNamePrefix;  break;
1027         case WID_MAIL_SUBJECT:              aRet <<= m_sSubject; break;
1028         case WID_ADDRESS_FROM_COLUMN:       aRet <<= m_sAddressFromColumn; break;
1029         case WID_SEND_AS_HTML:              aRet <<= m_bSendAsHTML; break;
1030         case WID_SEND_AS_ATTACHMENT:        aRet <<= m_bSendAsAttachment; break;
1031         case WID_MAIL_BODY:                 aRet <<= m_sMailBody; break;
1032         case WID_ATTACHMENT_NAME:           aRet <<= m_sAttachmentName; break;
1033         case WID_ATTACHMENT_FILTER:         aRet <<= m_sAttachmentFilter;break;
1034         case WID_PRINT_OPTIONS:             aRet <<= m_aPrintSettings; break;
1035         case WID_SAVE_AS_SINGLE_FILE:       aRet <<= m_bSaveAsSingleFile; break;
1036         case WID_SAVE_FILTER:               aRet <<= m_sSaveFilter; break;
1037         case WID_SAVE_FILTER_OPTIONS:       aRet <<= m_sSaveFilterOptions; break;
1038         case WID_SAVE_FILTER_DATA:          aRet <<= m_aSaveFilterData; break;
1039         case WID_COPIES_TO:                 aRet <<= m_aCopiesTo; break;
1040         case WID_BLIND_COPIES_TO:           aRet <<= m_aBlindCopiesTo;break;
1041         case WID_IN_SERVER_PASSWORD:        aRet <<= m_sInServerPassword; break;
1042         case WID_OUT_SERVER_PASSWORD:       aRet <<= m_sOutServerPassword; break;
1043         default :
1044             OSL_FAIL("unknown WID");
1045     }
1046 
1047     return aRet;
1048 }
1049 
addPropertyChangeListener(const OUString & rPropertyName,const uno::Reference<beans::XPropertyChangeListener> & rListener)1050 void SAL_CALL SwXMailMerge::addPropertyChangeListener(
1051         const OUString& rPropertyName,
1052         const uno::Reference< beans::XPropertyChangeListener >& rListener )
1053 {
1054     SolarMutexGuard aGuard;
1055     if (!m_bDisposing && rListener.is())
1056     {
1057         const SfxItemPropertyMapEntry* pCur = m_pPropSet->getPropertyMap().getByName( rPropertyName );
1058         if (!pCur)
1059             throw UnknownPropertyException(rPropertyName);
1060         m_aPropListeners.addInterface( pCur->nWID, rListener );
1061     }
1062 }
1063 
removePropertyChangeListener(const OUString & rPropertyName,const uno::Reference<beans::XPropertyChangeListener> & rListener)1064 void SAL_CALL SwXMailMerge::removePropertyChangeListener(
1065         const OUString& rPropertyName,
1066         const uno::Reference< beans::XPropertyChangeListener >& rListener )
1067 {
1068     SolarMutexGuard aGuard;
1069     if (!m_bDisposing && rListener.is())
1070     {
1071         const SfxItemPropertyMapEntry* pCur = m_pPropSet->getPropertyMap().getByName( rPropertyName );
1072         if (!pCur)
1073             throw UnknownPropertyException(rPropertyName);
1074         m_aPropListeners.removeInterface( pCur->nWID, rListener );
1075     }
1076 }
1077 
addVetoableChangeListener(const OUString &,const uno::Reference<beans::XVetoableChangeListener> &)1078 void SAL_CALL SwXMailMerge::addVetoableChangeListener(
1079         const OUString& /*rPropertyName*/,
1080         const uno::Reference< beans::XVetoableChangeListener >& /*rListener*/ )
1081 {
1082     // no vetoable property, thus no support for vetoable change listeners
1083     OSL_FAIL("not implemented");
1084 }
1085 
removeVetoableChangeListener(const OUString &,const uno::Reference<beans::XVetoableChangeListener> &)1086 void SAL_CALL SwXMailMerge::removeVetoableChangeListener(
1087         const OUString& /*rPropertyName*/,
1088         const uno::Reference< beans::XVetoableChangeListener >& /*rListener*/ )
1089 {
1090     // no vetoable property, thus no support for vetoable change listeners
1091     OSL_FAIL("not implemented");
1092 }
1093 
dispose()1094 void SAL_CALL SwXMailMerge::dispose()
1095 {
1096     SolarMutexGuard aGuard;
1097 
1098     if (!m_bDisposing)
1099     {
1100         m_bDisposing = true;
1101 
1102         EventObject aEvtObj( static_cast<XPropertySet *>(this) );
1103         m_aEvtListeners.disposeAndClear( aEvtObj );
1104         m_aMergeListeners.disposeAndClear( aEvtObj );
1105         m_aPropListeners.disposeAndClear( aEvtObj );
1106     }
1107 }
1108 
addEventListener(const Reference<XEventListener> & rxListener)1109 void SAL_CALL SwXMailMerge::addEventListener(
1110         const Reference< XEventListener >& rxListener )
1111 {
1112     SolarMutexGuard aGuard;
1113     if (!m_bDisposing && rxListener.is())
1114         m_aEvtListeners.addInterface( rxListener );
1115 }
1116 
removeEventListener(const Reference<XEventListener> & rxListener)1117 void SAL_CALL SwXMailMerge::removeEventListener(
1118         const Reference< XEventListener >& rxListener )
1119 {
1120     SolarMutexGuard aGuard;
1121     if (!m_bDisposing && rxListener.is())
1122         m_aEvtListeners.removeInterface( rxListener );
1123 }
1124 
addMailMergeEventListener(const uno::Reference<XMailMergeListener> & rxListener)1125 void SAL_CALL SwXMailMerge::addMailMergeEventListener(
1126         const uno::Reference< XMailMergeListener >& rxListener )
1127 {
1128     SolarMutexGuard aGuard;
1129     if (!m_bDisposing && rxListener.is())
1130         m_aMergeListeners.addInterface( rxListener );
1131 }
1132 
removeMailMergeEventListener(const uno::Reference<XMailMergeListener> & rxListener)1133 void SAL_CALL SwXMailMerge::removeMailMergeEventListener(
1134         const uno::Reference< XMailMergeListener >& rxListener )
1135 {
1136     SolarMutexGuard aGuard;
1137     if (!m_bDisposing && rxListener.is())
1138         m_aMergeListeners.removeInterface( rxListener );
1139 }
1140 
getImplementationName()1141 OUString SAL_CALL SwXMailMerge::getImplementationName()
1142 {
1143     return u"SwXMailMerge"_ustr;
1144 }
1145 
supportsService(const OUString & rServiceName)1146 sal_Bool SAL_CALL SwXMailMerge::supportsService( const OUString& rServiceName )
1147 {
1148     return cppu::supportsService(this, rServiceName);
1149 }
1150 
getSupportedServiceNames()1151 uno::Sequence< OUString > SAL_CALL SwXMailMerge::getSupportedServiceNames()
1152 {
1153     return { u"com.sun.star.text.MailMerge"_ustr, u"com.sun.star.sdb.DataAccessDescriptor"_ustr };
1154 }
1155 
1156 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1157