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