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 <sal/config.h>
21 
22 #include <cassert>
23 
24 #include <unotxdoc.hxx>
25 #include <sfx2/app.hxx>
26 #include <com/sun/star/sdb/CommandType.hpp>
27 #include <com/sun/star/sdb/XDocumentDataSource.hpp>
28 #include <com/sun/star/lang/DisposedException.hpp>
29 #include <com/sun/star/lang/XEventListener.hpp>
30 #include <com/sun/star/uri/UriReferenceFactory.hpp>
31 #include <com/sun/star/uri/VndSunStarPkgUrlReferenceFactory.hpp>
32 #include <com/sun/star/util/NumberFormatter.hpp>
33 #include <com/sun/star/sdb/DatabaseContext.hpp>
34 #include <com/sun/star/sdb/TextConnectionSettings.hpp>
35 #include <com/sun/star/sdb/XCompletedConnection.hpp>
36 #include <com/sun/star/sdb/XCompletedExecution.hpp>
37 #include <com/sun/star/container/XChild.hpp>
38 #include <com/sun/star/text/MailMergeEvent.hpp>
39 #include <com/sun/star/frame/XStorable.hpp>
40 #include <com/sun/star/task/InteractionHandler.hpp>
41 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
42 #include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
43 #include <com/sun/star/beans/XPropertySet.hpp>
44 #include <vcl/errinf.hxx>
45 #include <vcl/print.hxx>
46 #include <vcl/scheduler.hxx>
47 #include <sfx2/fcontnr.hxx>
48 #include <sfx2/filedlghelper.hxx>
49 #include <sfx2/viewfrm.hxx>
50 #include <dbconfig.hxx>
51 #include <unotools/tempfile.hxx>
52 #include <unotools/pathoptions.hxx>
53 #include <svl/zforlist.hxx>
54 #include <svl/stritem.hxx>
55 #include <sfx2/docfile.hxx>
56 #include <sfx2/docfilt.hxx>
57 #include <sfx2/progress.hxx>
58 #include <sfx2/dispatch.hxx>
59 #include <cmdid.h>
60 #include <swmodule.hxx>
61 #include <view.hxx>
62 #include <docsh.hxx>
63 #include <edtwin.hxx>
64 #include <wrtsh.hxx>
65 #include <fldbas.hxx>
66 #include <dbui.hxx>
67 #include <dbmgr.hxx>
68 #include <doc.hxx>
69 #include <IDocumentLinksAdministration.hxx>
70 #include <IDocumentFieldsAccess.hxx>
71 #include <IDocumentUndoRedo.hxx>
72 #include <swwait.hxx>
73 #include <swunohelper.hxx>
74 #include <strings.hrc>
75 #include <mmconfigitem.hxx>
76 #include <com/sun/star/sdbc/XRowSet.hpp>
77 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
78 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
79 #include <com/sun/star/sdb/XQueriesSupplier.hpp>
80 #include <com/sun/star/sdb/XColumn.hpp>
81 #include <com/sun/star/sdbc/DataType.hpp>
82 #include <com/sun/star/sdbc/ResultSetType.hpp>
83 #include <com/sun/star/sdbc/SQLException.hpp>
84 #include <com/sun/star/mail/MailAttachment.hpp>
85 #include <comphelper/processfactory.hxx>
86 #include <comphelper/property.hxx>
87 #include <comphelper/storagehelper.hxx>
88 #include <comphelper/string.hxx>
89 #include <comphelper/types.hxx>
90 #include <mailmergehelper.hxx>
91 #include <maildispatcher.hxx>
92 #include <svtools/htmlcfg.hxx>
93 #include <i18nlangtag/languagetag.hxx>
94 #include <com/sun/star/util/XNumberFormatTypes.hpp>
95 #include <svl/numuno.hxx>
96 #include <connectivity/dbtools.hxx>
97 #include <connectivity/dbconversion.hxx>
98 #include <unotools/charclass.hxx>
99 #include <tools/diagnose_ex.h>
100 
101 #include <unomailmerge.hxx>
102 #include <sfx2/event.hxx>
103 #include <svx/dataaccessdescriptor.hxx>
104 #include <osl/mutex.hxx>
105 #include <rtl/textenc.h>
106 #include <rtl/tencinfo.h>
107 #include <cppuhelper/implbase.hxx>
108 #include <ndindex.hxx>
109 #include <swevent.hxx>
110 #include <sal/log.hxx>
111 #include <swabstdlg.hxx>
112 #include <vector>
113 #include <section.hxx>
114 #include <rootfrm.hxx>
115 #include <calc.hxx>
116 #include <dbfld.hxx>
117 #include <IDocumentState.hxx>
118 #include <imaildsplistener.hxx>
119 #include <iodetect.hxx>
120 #include <IDocumentDeviceAccess.hxx>
121 
122 #include <memory>
123 #include <comphelper/propertysequence.hxx>
124 
125 using namespace ::com::sun::star;
126 using namespace sw;
127 
128 namespace {
129 
lcl_emitEvent(SfxEventHintId nEventId,sal_Int32 nStrId,SfxObjectShell * pDocShell)130 void lcl_emitEvent(SfxEventHintId nEventId, sal_Int32 nStrId, SfxObjectShell* pDocShell)
131 {
132     SfxGetpApp()->NotifyEvent(SfxEventHint(nEventId,
133                                            SwDocShell::GetEventName(nStrId),
134                                            pDocShell));
135 }
136 
137 }
138 
139 std::vector<std::pair<SwDocShell*, OUString>> SwDBManager::m_aUncommittedRegistrations;
140 
141 namespace {
142 
143 enum class SwDBNextRecord { NEXT, FIRST };
144 
145 }
146 
147 static bool lcl_ToNextRecord( SwDSParam* pParam, const SwDBNextRecord action = SwDBNextRecord::NEXT );
148 
149 namespace {
150 
151 enum class WorkingDocType { SOURCE, TARGET, COPY };
152 
153 }
154 
155 static SfxObjectShell* lcl_CreateWorkingDocument(
156     const WorkingDocType aType, const SwWrtShell &rSourceWrtShell,
157     const vcl::Window *pSourceWindow,
158     SwDBManager** const ppDBManager,
159     SwView** const pView, SwWrtShell** const pWrtShell, SwDoc** const pDoc );
160 
lcl_getCountFromResultSet(sal_Int32 & rCount,const SwDSParam * pParam)161 static bool lcl_getCountFromResultSet( sal_Int32& rCount, const SwDSParam* pParam )
162 {
163     rCount = pParam->aSelection.getLength();
164     if ( rCount > 0 )
165         return true;
166 
167     uno::Reference<beans::XPropertySet> xPrSet(pParam->xResultSet, uno::UNO_QUERY);
168     if ( xPrSet.is() )
169     {
170         try
171         {
172             bool bFinal = false;
173             uno::Any aFinal = xPrSet->getPropertyValue("IsRowCountFinal");
174             aFinal >>= bFinal;
175             if(!bFinal)
176             {
177                 pParam->xResultSet->last();
178                 pParam->xResultSet->first();
179             }
180             uno::Any aCount = xPrSet->getPropertyValue("RowCount");
181             if( aCount >>= rCount )
182                 return true;
183         }
184         catch(const uno::Exception&)
185         {
186         }
187     }
188     return false;
189 }
190 
191 class SwDBManager::ConnectionDisposedListener_Impl
192     : public cppu::WeakImplHelper< lang::XEventListener >
193 {
194 private:
195     SwDBManager * m_pDBManager;
196 
197     virtual void SAL_CALL disposing( const lang::EventObject& Source ) override;
198 
199 public:
200     explicit ConnectionDisposedListener_Impl(SwDBManager& rMgr);
201 
Dispose()202     void Dispose() { m_pDBManager = nullptr; }
203 
204 };
205 
206 namespace {
207 
208 /// Listens to removed data sources, and if it's one that's embedded into this document, triggers embedding removal.
209 class SwDataSourceRemovedListener : public cppu::WeakImplHelper<sdb::XDatabaseRegistrationsListener>
210 {
211     uno::Reference<sdb::XDatabaseContext> m_xDatabaseContext;
212     SwDBManager* m_pDBManager;
213 
214 public:
215     explicit SwDataSourceRemovedListener(SwDBManager& rDBManager);
216     virtual ~SwDataSourceRemovedListener() override;
217     virtual void SAL_CALL registeredDatabaseLocation(const sdb::DatabaseRegistrationEvent& rEvent) override;
218     virtual void SAL_CALL revokedDatabaseLocation(const sdb::DatabaseRegistrationEvent& rEvent) override;
219     virtual void SAL_CALL changedDatabaseLocation(const sdb::DatabaseRegistrationEvent& rEvent) override;
220     virtual void SAL_CALL disposing(const lang::EventObject& rObject) override;
221     void Dispose();
222 };
223 
224 }
225 
SwDataSourceRemovedListener(SwDBManager & rDBManager)226 SwDataSourceRemovedListener::SwDataSourceRemovedListener(SwDBManager& rDBManager)
227     : m_pDBManager(&rDBManager)
228 {
229     uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
230     m_xDatabaseContext = sdb::DatabaseContext::create(xComponentContext);
231     m_xDatabaseContext->addDatabaseRegistrationsListener(this);
232 }
233 
~SwDataSourceRemovedListener()234 SwDataSourceRemovedListener::~SwDataSourceRemovedListener()
235 {
236     if (m_xDatabaseContext.is())
237         m_xDatabaseContext->removeDatabaseRegistrationsListener(this);
238 }
239 
registeredDatabaseLocation(const sdb::DatabaseRegistrationEvent &)240 void SAL_CALL SwDataSourceRemovedListener::registeredDatabaseLocation(const sdb::DatabaseRegistrationEvent& /*rEvent*/)
241 {
242 }
243 
revokedDatabaseLocation(const sdb::DatabaseRegistrationEvent & rEvent)244 void SAL_CALL SwDataSourceRemovedListener::revokedDatabaseLocation(const sdb::DatabaseRegistrationEvent& rEvent)
245 {
246     if (!m_pDBManager || m_pDBManager->getEmbeddedName().isEmpty())
247         return;
248 
249     SwDoc* pDoc = m_pDBManager->getDoc();
250     if (!pDoc)
251         return;
252 
253     SwDocShell* pDocShell = pDoc->GetDocShell();
254     if (!pDocShell)
255         return;
256 
257     OUString aOwnURL = pDocShell->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::WithCharset);
258     OUString sTmpName = "vnd.sun.star.pkg://" +
259         INetURLObject::encode(aOwnURL, INetURLObject::PART_AUTHORITY, INetURLObject::EncodeMechanism::All);
260     sTmpName += "/" + m_pDBManager->getEmbeddedName();
261 
262     if (sTmpName != rEvent.OldLocation)
263         return;
264 
265     // The revoked database location is inside this document, then remove the
266     // embedding, as otherwise it would be back on the next reload of the
267     // document.
268     pDocShell->GetStorage()->removeElement(m_pDBManager->getEmbeddedName());
269     m_pDBManager->setEmbeddedName(OUString(), *pDocShell);
270 }
271 
changedDatabaseLocation(const sdb::DatabaseRegistrationEvent & rEvent)272 void SAL_CALL SwDataSourceRemovedListener::changedDatabaseLocation(const sdb::DatabaseRegistrationEvent& rEvent)
273 {
274     if (rEvent.OldLocation != rEvent.NewLocation)
275         revokedDatabaseLocation(rEvent);
276 }
277 
disposing(const lang::EventObject &)278 void SwDataSourceRemovedListener::disposing(const lang::EventObject& /*rObject*/)
279 {
280     m_xDatabaseContext.clear();
281 }
282 
Dispose()283 void SwDataSourceRemovedListener::Dispose()
284 {
285     m_pDBManager = nullptr;
286 }
287 
288 struct SwDBManager::SwDBManager_Impl
289 {
290     std::unique_ptr<SwDSParam> pMergeData;
291     VclPtr<AbstractMailMergeDlg>  pMergeDialog;
292     rtl::Reference<SwDBManager::ConnectionDisposedListener_Impl> m_xDisposeListener;
293     rtl::Reference<SwDataSourceRemovedListener> m_xDataSourceRemovedListener;
294     osl::Mutex                    m_aAllEmailSendMutex;
295     uno::Reference< mail::XMailMessage> m_xLastMessage;
296 
SwDBManager_ImplSwDBManager::SwDBManager_Impl297     explicit SwDBManager_Impl(SwDBManager& rDBManager)
298         : m_xDisposeListener(new ConnectionDisposedListener_Impl(rDBManager))
299         {}
300 
~SwDBManager_ImplSwDBManager::SwDBManager_Impl301     ~SwDBManager_Impl()
302     {
303         m_xDisposeListener->Dispose();
304         if (m_xDataSourceRemovedListener.is())
305             m_xDataSourceRemovedListener->Dispose();
306     }
307 };
308 
lcl_InitNumberFormatter(SwDSParam & rParam,uno::Reference<sdbc::XDataSource> const & xSource)309 static void lcl_InitNumberFormatter(SwDSParam& rParam, uno::Reference<sdbc::XDataSource> const & xSource)
310 {
311     uno::Reference<uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext();
312     rParam.xFormatter = util::NumberFormatter::create(xContext);
313     uno::Reference<beans::XPropertySet> xSourceProps(
314         (xSource.is()
315          ? xSource
316          : SwDBManager::getDataSourceAsParent(
317              rParam.xConnection, rParam.sDataSource)),
318         uno::UNO_QUERY);
319     if(!xSourceProps.is())
320         return;
321 
322     uno::Any aFormats = xSourceProps->getPropertyValue("NumberFormatsSupplier");
323     if(!aFormats.hasValue())
324         return;
325 
326     uno::Reference<util::XNumberFormatsSupplier> xSuppl;
327     aFormats >>= xSuppl;
328     if(xSuppl.is())
329     {
330         uno::Reference< beans::XPropertySet > xSettings = xSuppl->getNumberFormatSettings();
331         uno::Any aNull = xSettings->getPropertyValue("NullDate");
332         aNull >>= rParam.aNullDate;
333         if(rParam.xFormatter.is())
334             rParam.xFormatter->attachNumberFormatsSupplier(xSuppl);
335     }
336 }
337 
lcl_MoveAbsolute(SwDSParam * pParam,tools::Long nAbsPos)338 static bool lcl_MoveAbsolute(SwDSParam* pParam, tools::Long nAbsPos)
339 {
340     bool bRet = false;
341     try
342     {
343         if(pParam->aSelection.hasElements())
344         {
345             if(pParam->aSelection.getLength() <= nAbsPos)
346             {
347                 pParam->bEndOfDB = true;
348                 bRet = false;
349             }
350             else
351             {
352                 pParam->nSelectionIndex = nAbsPos;
353                 sal_Int32 nPos = 0;
354                 pParam->aSelection.getConstArray()[ pParam->nSelectionIndex ] >>= nPos;
355                 pParam->bEndOfDB = !pParam->xResultSet->absolute( nPos );
356                 bRet = !pParam->bEndOfDB;
357             }
358         }
359         else if(pParam->bScrollable)
360         {
361             bRet = pParam->xResultSet->absolute( nAbsPos );
362         }
363         else
364         {
365             OSL_FAIL("no absolute positioning available");
366         }
367     }
368     catch(const uno::Exception&)
369     {
370     }
371     return bRet;
372 }
373 
lcl_GetColumnCnt(SwDSParam * pParam,const uno::Reference<beans::XPropertySet> & rColumnProps,LanguageType nLanguage,OUString & rResult,double * pNumber)374 static void lcl_GetColumnCnt(SwDSParam *pParam,
375                              const uno::Reference< beans::XPropertySet > &rColumnProps,
376                              LanguageType nLanguage, OUString &rResult, double* pNumber)
377 {
378     SwDBFormatData aFormatData;
379     if(!pParam->xFormatter.is())
380     {
381         uno::Reference<sdbc::XDataSource> xSource = SwDBManager::getDataSourceAsParent(
382                                     pParam->xConnection,pParam->sDataSource);
383         lcl_InitNumberFormatter(*pParam, xSource );
384     }
385     aFormatData.aNullDate = pParam->aNullDate;
386     aFormatData.xFormatter = pParam->xFormatter;
387 
388     aFormatData.aLocale = LanguageTag( nLanguage ).getLocale();
389 
390     rResult = SwDBManager::GetDBField( rColumnProps, aFormatData, pNumber);
391 }
392 
lcl_GetColumnCnt(SwDSParam * pParam,const OUString & rColumnName,LanguageType nLanguage,OUString & rResult,double * pNumber)393 static bool lcl_GetColumnCnt(SwDSParam* pParam, const OUString& rColumnName,
394                              LanguageType nLanguage, OUString& rResult, double* pNumber)
395 {
396     uno::Reference< sdbcx::XColumnsSupplier > xColsSupp( pParam->xResultSet, uno::UNO_QUERY );
397     uno::Reference<container::XNameAccess> xCols;
398     try
399     {
400         xCols = xColsSupp->getColumns();
401     }
402     catch(const lang::DisposedException&)
403     {
404     }
405     if(!xCols.is() || !xCols->hasByName(rColumnName))
406         return false;
407     uno::Any aCol = xCols->getByName(rColumnName);
408     uno::Reference< beans::XPropertySet > xColumnProps;
409     aCol >>= xColumnProps;
410     lcl_GetColumnCnt( pParam, xColumnProps, nLanguage, rResult, pNumber );
411     return true;
412 };
413 
414 // import data
Merge(const SwMergeDescriptor & rMergeDesc)415 bool SwDBManager::Merge( const SwMergeDescriptor& rMergeDesc )
416 {
417     assert( !m_bInMerge && !m_pImpl->pMergeData && "merge already activated!" );
418 
419     SfxObjectShellLock  xWorkObjSh;
420     SwWrtShell         *pWorkShell            = nullptr;
421     SwDoc              *pWorkDoc              = nullptr;
422     SwDBManager        *pWorkDocOrigDBManager = nullptr;
423 
424     switch( rMergeDesc.nMergeType )
425     {
426         case DBMGR_MERGE_PRINTER:
427         case DBMGR_MERGE_EMAIL:
428         case DBMGR_MERGE_FILE:
429         case DBMGR_MERGE_SHELL:
430         {
431             SwDocShell *pSourceDocSh = rMergeDesc.rSh.GetView().GetDocShell();
432             if( pSourceDocSh->IsModified() )
433             {
434                 pWorkDocOrigDBManager = this;
435                 xWorkObjSh = lcl_CreateWorkingDocument(
436                     WorkingDocType::SOURCE, rMergeDesc.rSh, nullptr,
437                     &pWorkDocOrigDBManager, nullptr, &pWorkShell, &pWorkDoc );
438             }
439             [[fallthrough]];
440         }
441 
442         default:
443             if( !xWorkObjSh.Is() )
444                 pWorkShell = &rMergeDesc.rSh;
445             break;
446     }
447 
448     SwDBData aData;
449     aData.nCommandType = sdb::CommandType::TABLE;
450     uno::Reference<sdbc::XResultSet>  xResSet;
451     uno::Sequence<uno::Any> aSelection;
452     uno::Reference< sdbc::XConnection> xConnection;
453 
454     aData.sDataSource = rMergeDesc.rDescriptor.getDataSource();
455     rMergeDesc.rDescriptor[svx::DataAccessDescriptorProperty::Command]      >>= aData.sCommand;
456     rMergeDesc.rDescriptor[svx::DataAccessDescriptorProperty::CommandType]  >>= aData.nCommandType;
457 
458     if ( rMergeDesc.rDescriptor.has(svx::DataAccessDescriptorProperty::Cursor) )
459         rMergeDesc.rDescriptor[svx::DataAccessDescriptorProperty::Cursor] >>= xResSet;
460     if ( rMergeDesc.rDescriptor.has(svx::DataAccessDescriptorProperty::Selection) )
461         rMergeDesc.rDescriptor[svx::DataAccessDescriptorProperty::Selection] >>= aSelection;
462     if ( rMergeDesc.rDescriptor.has(svx::DataAccessDescriptorProperty::Connection) )
463         rMergeDesc.rDescriptor[svx::DataAccessDescriptorProperty::Connection] >>= xConnection;
464 
465     if((aData.sDataSource.isEmpty() || aData.sCommand.isEmpty()) && !xResSet.is())
466     {
467         return false;
468     }
469 
470     m_pImpl->pMergeData.reset(new SwDSParam(aData, xResSet, aSelection));
471     SwDSParam*  pTemp = FindDSData(aData, false);
472     if(pTemp)
473         *pTemp = *m_pImpl->pMergeData;
474     else
475     {
476         // calls from the calculator may have added a connection with an invalid commandtype
477         //"real" data base connections added here have to re-use the already available
478         //DSData and set the correct CommandType
479         aData.nCommandType = -1;
480         pTemp = FindDSData(aData, false);
481         if(pTemp)
482             *pTemp = *m_pImpl->pMergeData;
483         else
484         {
485             m_DataSourceParams.push_back(std::make_unique<SwDSParam>(*m_pImpl->pMergeData));
486             try
487             {
488                 uno::Reference<lang::XComponent> xComponent(m_DataSourceParams.back()->xConnection, uno::UNO_QUERY);
489                 if(xComponent.is())
490                     xComponent->addEventListener(m_pImpl->m_xDisposeListener);
491             }
492             catch(const uno::Exception&)
493             {
494             }
495         }
496     }
497     if(!m_pImpl->pMergeData->xConnection.is())
498         m_pImpl->pMergeData->xConnection = xConnection;
499     // add an XEventListener
500 
501     lcl_ToNextRecord(m_pImpl->pMergeData.get(), SwDBNextRecord::FIRST);
502 
503     uno::Reference<sdbc::XDataSource> xSource = SwDBManager::getDataSourceAsParent(xConnection,aData.sDataSource);
504 
505     lcl_InitNumberFormatter(*m_pImpl->pMergeData, xSource);
506 
507     pWorkShell->ChgDBData(aData);
508     m_bInMerge = true;
509 
510     if (IsInitDBFields())
511     {
512         // with database fields without DB-Name, use DB-Name from Doc
513         std::vector<OUString> aDBNames;
514         aDBNames.emplace_back();
515         SwDBData aInsertData = pWorkShell->GetDBData();
516         OUString sDBName = aInsertData.sDataSource
517             + OUStringChar(DB_DELIM) + aInsertData.sCommand
518             + OUStringChar(DB_DELIM)
519             + OUString::number(aInsertData.nCommandType);
520         pWorkShell->ChangeDBFields( aDBNames, sDBName);
521         SetInitDBFields(false);
522     }
523 
524     bool bRet = true;
525     switch(rMergeDesc.nMergeType)
526     {
527         case DBMGR_MERGE:
528             pWorkShell->StartAllAction();
529             pWorkShell->SwViewShell::UpdateFields( true );
530             pWorkShell->SetModified();
531             pWorkShell->EndAllAction();
532             break;
533 
534         case DBMGR_MERGE_PRINTER:
535         case DBMGR_MERGE_EMAIL:
536         case DBMGR_MERGE_FILE:
537         case DBMGR_MERGE_SHELL:
538             // save files and send them as e-Mail if required
539             bRet = MergeMailFiles(pWorkShell, rMergeDesc);
540             break;
541 
542         default:
543             // insert selected entries
544             // (was: InsertRecord)
545             ImportFromConnection(pWorkShell);
546             break;
547     }
548 
549     m_pImpl->pMergeData.reset();
550 
551     if( xWorkObjSh.Is() )
552     {
553         pWorkDoc->SetDBManager( pWorkDocOrigDBManager );
554         xWorkObjSh->DoClose();
555     }
556 
557     m_bInMerge = false;
558 
559     return bRet;
560 }
561 
ImportFromConnection(SwWrtShell * pSh)562 void SwDBManager::ImportFromConnection(  SwWrtShell* pSh )
563 {
564     if(!m_pImpl->pMergeData || m_pImpl->pMergeData->bEndOfDB)
565         return;
566 
567     pSh->StartAllAction();
568     pSh->StartUndo();
569     bool bGroupUndo(pSh->DoesGroupUndo());
570     pSh->DoGroupUndo(false);
571 
572     if( pSh->HasSelection() )
573         pSh->DelRight();
574 
575     std::unique_ptr<SwWait> pWait;
576 
577     {
578         sal_uLong i = 0;
579         do {
580 
581             ImportDBEntry(pSh);
582             if( 10 == ++i )
583                 pWait.reset(new SwWait( *pSh->GetView().GetDocShell(), true));
584 
585         } while(ToNextMergeRecord());
586     }
587 
588     pSh->DoGroupUndo(bGroupUndo);
589     pSh->EndUndo();
590     pSh->EndAllAction();
591 }
592 
ImportDBEntry(SwWrtShell * pSh)593 void SwDBManager::ImportDBEntry(SwWrtShell* pSh)
594 {
595     if(!m_pImpl->pMergeData || m_pImpl->pMergeData->bEndOfDB)
596         return;
597 
598     uno::Reference< sdbcx::XColumnsSupplier > xColsSupp( m_pImpl->pMergeData->xResultSet, uno::UNO_QUERY );
599     uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns();
600     OUStringBuffer sStr;
601     uno::Sequence<OUString> aColNames = xCols->getElementNames();
602     const OUString* pColNames = aColNames.getConstArray();
603     tools::Long nLength = aColNames.getLength();
604     for(tools::Long i = 0; i < nLength; i++)
605     {
606         uno::Any aCol = xCols->getByName(pColNames[i]);
607         uno::Reference< beans::XPropertySet > xColumnProp;
608         aCol >>= xColumnProp;
609         SwDBFormatData aDBFormat;
610         sStr.append(GetDBField( xColumnProp, aDBFormat));
611         if (i < nLength - 1)
612             sStr.append("\t");
613     }
614     pSh->SwEditShell::Insert2(sStr.makeStringAndClear());
615     pSh->SwFEShell::SplitNode();    // line feed
616 }
617 
GetTableNames(weld::ComboBox & rBox,const OUString & rDBName)618 bool SwDBManager::GetTableNames(weld::ComboBox& rBox, const OUString& rDBName)
619 {
620     bool bRet = false;
621     OUString sOldTableName(rBox.get_active_text());
622     rBox.clear();
623     SwDSParam* pParam = FindDSConnection(rDBName, false);
624     uno::Reference< sdbc::XConnection> xConnection;
625     if (pParam && pParam->xConnection.is())
626         xConnection = pParam->xConnection;
627     else
628     {
629         if ( !rDBName.isEmpty() )
630             xConnection = RegisterConnection( rDBName );
631     }
632     if (xConnection.is())
633     {
634         uno::Reference<sdbcx::XTablesSupplier> xTSupplier(xConnection, uno::UNO_QUERY);
635         if(xTSupplier.is())
636         {
637             uno::Reference<container::XNameAccess> xTables = xTSupplier->getTables();
638             const uno::Sequence<OUString> aTables = xTables->getElementNames();
639             for (const OUString& rTable : aTables)
640                 rBox.append("0", rTable);
641         }
642         uno::Reference<sdb::XQueriesSupplier> xQSupplier(xConnection, uno::UNO_QUERY);
643         if(xQSupplier.is())
644         {
645             uno::Reference<container::XNameAccess> xQueries = xQSupplier->getQueries();
646             const uno::Sequence<OUString> aQueries = xQueries->getElementNames();
647             for (const OUString& rQuery : aQueries)
648                 rBox.append("1", rQuery);
649         }
650         if (!sOldTableName.isEmpty())
651             rBox.set_active_text(sOldTableName);
652         bRet = true;
653     }
654     return bRet;
655 }
656 
657 // fill Listbox with column names of a database
GetColumnNames(weld::ComboBox & rBox,const OUString & rDBName,const OUString & rTableName)658 void SwDBManager::GetColumnNames(weld::ComboBox& rBox,
659                              const OUString& rDBName, const OUString& rTableName)
660 {
661     SwDBData aData;
662     aData.sDataSource = rDBName;
663     aData.sCommand = rTableName;
664     aData.nCommandType = -1;
665     SwDSParam* pParam = FindDSData(aData, false);
666     uno::Reference< sdbc::XConnection> xConnection;
667     if(pParam && pParam->xConnection.is())
668         xConnection = pParam->xConnection;
669     else
670     {
671         xConnection = RegisterConnection( rDBName );
672     }
673     GetColumnNames(rBox, xConnection, rTableName);
674 }
675 
GetColumnNames(weld::ComboBox & rBox,uno::Reference<sdbc::XConnection> const & xConnection,const OUString & rTableName)676 void SwDBManager::GetColumnNames(weld::ComboBox& rBox,
677         uno::Reference< sdbc::XConnection> const & xConnection,
678         const OUString& rTableName)
679 {
680     rBox.clear();
681     uno::Reference< sdbcx::XColumnsSupplier> xColsSupp = SwDBManager::GetColumnSupplier(xConnection, rTableName);
682     if(xColsSupp.is())
683     {
684         uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns();
685         const uno::Sequence<OUString> aColNames = xCols->getElementNames();
686         for (const OUString& rColName : aColNames)
687         {
688             rBox.append_text(rColName);
689         }
690         ::comphelper::disposeComponent( xColsSupp );
691     }
692 }
693 
SwDBManager(SwDoc * pDoc)694 SwDBManager::SwDBManager(SwDoc* pDoc)
695     : m_aMergeStatus( MergeStatus::Ok )
696     , m_bInitDBFields(false)
697     , m_bInMerge(false)
698     , m_bMergeSilent(false)
699     , m_pImpl(new SwDBManager_Impl(*this))
700     , m_pMergeEvtSrc(nullptr)
701     , m_pDoc(pDoc)
702 {
703 }
704 
~SwDBManager()705 SwDBManager::~SwDBManager() COVERITY_NOEXCEPT_FALSE
706 {
707     RevokeLastRegistrations();
708 
709     // copy required, m_DataSourceParams can be modified while disposing components
710     std::vector<uno::Reference<sdbc::XConnection>> aCopiedConnections;
711     for (const auto & pParam : m_DataSourceParams)
712     {
713         if(pParam->xConnection.is())
714         {
715             aCopiedConnections.push_back(pParam->xConnection);
716         }
717     }
718     for (const auto & xConnection : aCopiedConnections)
719     {
720         try
721         {
722             uno::Reference<lang::XComponent> xComp(xConnection, uno::UNO_QUERY);
723             if(xComp.is())
724                 xComp->dispose();
725         }
726         catch(const uno::RuntimeException&)
727         {
728             //may be disposed already since multiple entries may have used the same connection
729         }
730     }
731 }
732 
lcl_RemoveSectionLinks(SwWrtShell & rWorkShell)733 static void lcl_RemoveSectionLinks( SwWrtShell& rWorkShell )
734 {
735     //reset all links of the sections of synchronized labels
736     size_t nSections = rWorkShell.GetSectionFormatCount();
737     for (size_t nSection = 0; nSection < nSections; ++nSection)
738     {
739         SwSectionData aSectionData( *rWorkShell.GetSectionFormat( nSection ).GetSection() );
740         if( aSectionData.GetType() == SectionType::FileLink )
741         {
742             aSectionData.SetType( SectionType::Content );
743             aSectionData.SetLinkFileName( OUString() );
744             rWorkShell.UpdateSection( nSection, aSectionData );
745         }
746     }
747     rWorkShell.SetLabelDoc( false );
748 }
749 
lcl_SaveDebugDoc(SfxObjectShell * xTargetDocShell,const char * name,int no=0)750 static void lcl_SaveDebugDoc( SfxObjectShell *xTargetDocShell,
751                               const char *name, int no = 0 )
752 {
753     static OUString sTempDirURL;
754     if( sTempDirURL.isEmpty() )
755     {
756         SvtPathOptions aPathOpt;
757         utl::TempFile aTempDir( &aPathOpt.GetTempPath(), true );
758         if( aTempDir.IsValid() )
759         {
760             INetURLObject aTempDirURL( aTempDir.GetURL() );
761             sTempDirURL = aTempDirURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
762             SAL_INFO( "sw.mailmerge", "Dump directory: " << sTempDirURL );
763         }
764     }
765     if( sTempDirURL.isEmpty() )
766         return;
767 
768     const OUString sExt( ".odt" );
769     OUString basename = OUString::createFromAscii( name );
770     if (no > 0)
771         basename += OUString::number(no) + "-";
772     // aTempFile is not deleted, but that seems to be intentional
773     utl::TempFile aTempFile( basename, true, &sExt, &sTempDirURL );
774     INetURLObject aTempFileURL( aTempFile.GetURL() );
775     auto pDstMed = std::make_unique<SfxMedium>(
776         aTempFileURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
777         StreamMode::STD_READWRITE );
778     bool bAnyError = !xTargetDocShell->DoSaveAs( *pDstMed );
779     // xObjectShell->DoSaveCompleted crashes the mail merge unit tests, so skip it
780     bAnyError |= (ERRCODE_NONE != xTargetDocShell->GetError());
781     if( bAnyError )
782         SAL_WARN( "sw.mailmerge", "Error saving: " << aTempFile.GetURL() );
783     else
784         SAL_INFO( "sw.mailmerge", "Saved doc as: " << aTempFile.GetURL() );
785 }
786 
lcl_SaveDoc(const INetURLObject * pFileURL,const std::shared_ptr<const SfxFilter> & pStoreToFilter,const OUString * pStoreToFilterOptions,const uno::Sequence<beans::PropertyValue> * pSaveToFilterData,const bool bIsPDFexport,SfxObjectShell * xObjectShell,SwWrtShell & rWorkShell,OUString * const decodedURL=nullptr)787 static bool lcl_SaveDoc(
788     const INetURLObject* pFileURL,
789     const std::shared_ptr<const SfxFilter>& pStoreToFilter,
790     const OUString* pStoreToFilterOptions,
791     const uno::Sequence< beans::PropertyValue >* pSaveToFilterData,
792     const bool bIsPDFexport,
793     SfxObjectShell* xObjectShell,
794     SwWrtShell& rWorkShell,
795     OUString * const decodedURL = nullptr )
796 {
797     OUString url = pFileURL->GetMainURL( INetURLObject::DecodeMechanism::NONE );
798     if( decodedURL )
799         (*decodedURL) = url;
800 
801     SfxMedium* pDstMed = new SfxMedium( url, StreamMode::STD_READWRITE );
802     pDstMed->SetFilter( pStoreToFilter );
803     if( pDstMed->GetItemSet() )
804     {
805         if( pStoreToFilterOptions )
806             pDstMed->GetItemSet()->Put( SfxStringItem(SID_FILE_FILTEROPTIONS,
807                                         *pStoreToFilterOptions));
808         if( pSaveToFilterData->hasElements() )
809             pDstMed->GetItemSet()->Put( SfxUnoAnyItem(SID_FILTER_DATA,
810                                         uno::makeAny(*pSaveToFilterData)));
811     }
812 
813     // convert fields to text if we are exporting to PDF.
814     // this prevents a second merge while updating the fields
815     // in SwXTextDocument::getRendererCount()
816     if( bIsPDFexport )
817         rWorkShell.ConvertFieldsToText();
818 
819     bool bAnyError = !xObjectShell->DoSaveAs(*pDstMed);
820     // Actually this should be a bool... so in case of email and individual
821     // files, where this is set, we skip the recently used handling
822     bAnyError |= !xObjectShell->DoSaveCompleted( pDstMed, !decodedURL );
823     bAnyError |= (ERRCODE_NONE != xObjectShell->GetError());
824     if( bAnyError )
825     {
826         // error message ??
827         ErrorHandler::HandleError( xObjectShell->GetError() );
828     }
829     return !bAnyError;
830 }
831 
lcl_PreparePrinterOptions(const uno::Sequence<beans::PropertyValue> & rInPrintOptions,uno::Sequence<beans::PropertyValue> & rOutPrintOptions)832 static void lcl_PreparePrinterOptions(
833     const uno::Sequence< beans::PropertyValue >& rInPrintOptions,
834     uno::Sequence< beans::PropertyValue >& rOutPrintOptions)
835 {
836     // printing should be done synchronously otherwise the document
837     // might already become invalid during the process
838 
839     const sal_Int32 nOffset = 1;
840     rOutPrintOptions.realloc( nOffset );
841     rOutPrintOptions[ 0 ].Name = "Wait";
842     rOutPrintOptions[ 0 ].Value <<= true;
843 
844     // copy print options
845     sal_Int32 nIndex = nOffset;
846     for( const beans::PropertyValue& rOption : rInPrintOptions)
847     {
848         if( rOption.Name == "CopyCount" || rOption.Name == "FileName"
849             || rOption.Name == "Collate" || rOption.Name == "Pages"
850             || rOption.Name == "Wait" || rOption.Name == "PrinterName" )
851         {
852             // add an option
853             rOutPrintOptions.realloc( nIndex + 1 );
854             rOutPrintOptions[ nIndex ].Name = rOption.Name;
855             rOutPrintOptions[ nIndex++ ].Value = rOption.Value ;
856         }
857     }
858 }
859 
lcl_PrepareSaveFilterDataOptions(const uno::Sequence<beans::PropertyValue> & rInSaveFilterDataptions,uno::Sequence<beans::PropertyValue> & rOutSaveFilterDataOptions,const OUString & sPassword)860 static void lcl_PrepareSaveFilterDataOptions(
861     const uno::Sequence< beans::PropertyValue >& rInSaveFilterDataptions,
862     uno::Sequence< beans::PropertyValue >& rOutSaveFilterDataOptions,
863     const OUString& sPassword)
864 {
865     const sal_Int32 nOffset = 2;
866     rOutSaveFilterDataOptions.realloc( nOffset );
867     rOutSaveFilterDataOptions[ 0 ].Name = "EncryptFile";
868     rOutSaveFilterDataOptions[ 0 ].Value <<= true;
869     rOutSaveFilterDataOptions[ 1 ].Name = "DocumentOpenPassword";
870     rOutSaveFilterDataOptions[ 1 ].Value <<= sPassword;
871 
872     // copy other options
873     sal_Int32 nIndex = nOffset;
874     for( const beans::PropertyValue& rOption : rInSaveFilterDataptions)
875     {
876          rOutSaveFilterDataOptions.realloc( nIndex + 1 );
877          rOutSaveFilterDataOptions[ nIndex ].Name = rOption.Name;
878          rOutSaveFilterDataOptions[ nIndex++ ].Value = rOption.Value ;
879     }
880 
881 }
882 
883 
lcl_CreateWorkingDocument(const WorkingDocType aType,const SwWrtShell & rSourceWrtShell,const vcl::Window * pSourceWindow,SwDBManager ** const ppDBManager,SwView ** const pView,SwWrtShell ** const pWrtShell,SwDoc ** const pDoc)884 static SfxObjectShell* lcl_CreateWorkingDocument(
885     // input
886     const WorkingDocType aType, const SwWrtShell &rSourceWrtShell,
887     // optional input
888     const vcl::Window *pSourceWindow,
889     // optional in and output to swap the DB manager
890     SwDBManager** const ppDBManager,
891     // optional output
892     SwView** const pView, SwWrtShell** const pWrtShell, SwDoc** const pDoc )
893 {
894     const SwDoc *pSourceDoc = rSourceWrtShell.GetDoc();
895     SfxObjectShellRef xWorkObjectShell = pSourceDoc->CreateCopy( true, (aType == WorkingDocType::TARGET) );
896     SfxViewFrame* pWorkFrame = SfxViewFrame::LoadHiddenDocument( *xWorkObjectShell, SFX_INTERFACE_NONE );
897 
898     if( pSourceWindow )
899     {
900         // the created window has to be located at the same position as the source window
901         vcl::Window& rTargetWindow = pWorkFrame->GetFrame().GetWindow();
902         rTargetWindow.SetPosPixel( pSourceWindow->GetPosPixel() );
903     }
904 
905     SwView* pWorkView = static_cast< SwView* >( pWorkFrame->GetViewShell() );
906     SwWrtShell* pWorkWrtShell = pWorkView->GetWrtShellPtr();
907     pWorkWrtShell->GetViewOptions()->SetIdle( false );
908     pWorkView->AttrChangedNotify(nullptr);// in order for SelectShell to be called
909     SwDoc* pWorkDoc = pWorkWrtShell->GetDoc();
910     pWorkDoc->GetIDocumentUndoRedo().DoUndo( false );
911     pWorkDoc->ReplaceDocumentProperties( *pSourceDoc );
912 
913     // import print settings
914     const SwPrintData &rPrintData = pSourceDoc->getIDocumentDeviceAccess().getPrintData();
915     pWorkDoc->getIDocumentDeviceAccess().setPrintData(rPrintData);
916     const JobSetup *pJobSetup = pSourceDoc->getIDocumentDeviceAccess().getJobsetup();
917     if (pJobSetup)
918         pWorkDoc->getIDocumentDeviceAccess().setJobsetup(*pJobSetup);
919 
920     if( aType == WorkingDocType::TARGET )
921     {
922         assert( !ppDBManager );
923         pWorkDoc->SetInMailMerge( true );
924         pWorkWrtShell->SetLabelDoc( false );
925     }
926     else
927     {
928         // We have to swap the DBmanager of the new doc, so we also need input
929         assert(ppDBManager && *ppDBManager);
930         SwDBManager *pWorkDBManager = pWorkDoc->GetDBManager();
931         pWorkDoc->SetDBManager( *ppDBManager );
932         *ppDBManager = pWorkDBManager;
933 
934         if( aType == WorkingDocType::SOURCE )
935         {
936             // the GetDBData call constructs the data, if it's missing - kind of const...
937             pWorkWrtShell->ChgDBData( const_cast<SwDoc*>(pSourceDoc)->GetDBData() );
938             // some DocumentSettings are currently not copied by SwDoc::CreateCopy
939             pWorkWrtShell->SetLabelDoc( rSourceWrtShell.IsLabelDoc() );
940             pWorkDoc->getIDocumentState().ResetModified();
941         }
942         else
943             pWorkDoc->getIDocumentLinksAdministration().EmbedAllLinks();
944     }
945 
946     if( pView )     *pView     = pWorkView;
947     if( pWrtShell ) *pWrtShell = pWorkWrtShell;
948     if( pDoc )      *pDoc      = pWorkDoc;
949 
950     return xWorkObjectShell.get();
951 }
952 
lcl_CreateMailFromDoc(const SwMergeDescriptor & rMergeDescriptor,const OUString & sFileURL,const OUString & sMailRecipient,const OUString & sMailBodyMimeType,rtl_TextEncoding sMailEncoding,const OUString & sAttachmentMimeType)953 static rtl::Reference<SwMailMessage> lcl_CreateMailFromDoc(
954     const SwMergeDescriptor &rMergeDescriptor,
955     const OUString &sFileURL, const OUString &sMailRecipient,
956     const OUString &sMailBodyMimeType, rtl_TextEncoding sMailEncoding,
957     const OUString &sAttachmentMimeType )
958 {
959     rtl::Reference<SwMailMessage> pMessage = new SwMailMessage;
960     if( rMergeDescriptor.pMailMergeConfigItem->IsMailReplyTo() )
961         pMessage->setReplyToAddress(rMergeDescriptor.pMailMergeConfigItem->GetMailReplyTo());
962     pMessage->addRecipient( sMailRecipient );
963     pMessage->SetSenderAddress( rMergeDescriptor.pMailMergeConfigItem->GetMailAddress() );
964 
965     OUStringBuffer sBody;
966     if( rMergeDescriptor.bSendAsAttachment )
967     {
968         sBody = rMergeDescriptor.sMailBody;
969         mail::MailAttachment aAttach;
970         aAttach.Data = new SwMailTransferable( sFileURL,
971             rMergeDescriptor.sAttachmentName, sAttachmentMimeType );
972         aAttach.ReadableName = rMergeDescriptor.sAttachmentName;
973         pMessage->addAttachment( aAttach );
974     }
975     else
976     {
977         //read in the temporary file and use it as mail body
978         SfxMedium aMedium( sFileURL, StreamMode::READ );
979         SvStream* pInStream = aMedium.GetInStream();
980         assert( pInStream && "no output file created?" );
981         if( !pInStream )
982             return pMessage;
983 
984         pInStream->SetStreamCharSet( sMailEncoding );
985         OString sLine;
986         while ( pInStream->ReadLine( sLine ) )
987         {
988             sBody.append(OStringToOUString( sLine, sMailEncoding ));
989             sBody.append("\n");
990         }
991     }
992     pMessage->setSubject( rMergeDescriptor.sSubject );
993     uno::Reference< datatransfer::XTransferable> xBody =
994                 new SwMailTransferable( sBody.makeStringAndClear(), sMailBodyMimeType );
995     pMessage->setBody( xBody );
996 
997     for( const OUString& sCcRecipient : rMergeDescriptor.aCopiesTo )
998         pMessage->addCcRecipient( sCcRecipient );
999     for( const OUString& sBccRecipient : rMergeDescriptor.aBlindCopiesTo )
1000         pMessage->addBccRecipient( sBccRecipient );
1001 
1002     return pMessage;
1003 }
1004 
1005 class SwDBManager::MailDispatcherListener_Impl : public IMailDispatcherListener
1006 {
1007     SwDBManager &m_rDBManager;
1008 
1009 public:
MailDispatcherListener_Impl(SwDBManager & rDBManager)1010     explicit MailDispatcherListener_Impl( SwDBManager &rDBManager )
1011         : m_rDBManager( rDBManager ) {}
1012 
idle()1013     virtual void idle() override {}
1014 
mailDelivered(uno::Reference<mail::XMailMessage> xMessage)1015     virtual void mailDelivered( uno::Reference< mail::XMailMessage> xMessage ) override
1016     {
1017         osl::MutexGuard aGuard( m_rDBManager.m_pImpl->m_aAllEmailSendMutex );
1018         if ( m_rDBManager.m_pImpl->m_xLastMessage == xMessage )
1019             m_rDBManager.m_pImpl->m_xLastMessage.clear();
1020     }
1021 
mailDeliveryError(::rtl::Reference<MailDispatcher> xMailDispatcher,uno::Reference<mail::XMailMessage>,const OUString &)1022     virtual void mailDeliveryError( ::rtl::Reference<MailDispatcher> xMailDispatcher,
1023                 uno::Reference< mail::XMailMessage>, const OUString& ) override
1024     {
1025         osl::MutexGuard aGuard( m_rDBManager.m_pImpl->m_aAllEmailSendMutex );
1026         m_rDBManager.m_aMergeStatus = MergeStatus::Error;
1027         m_rDBManager.m_pImpl->m_xLastMessage.clear();
1028         xMailDispatcher->stop();
1029     }
1030 };
1031 
1032 /**
1033  * Please have a look at the README in the same directory, before you make
1034  * larger changes in this function!
1035  */
MergeMailFiles(SwWrtShell * pSourceShell,const SwMergeDescriptor & rMergeDescriptor)1036 bool SwDBManager::MergeMailFiles(SwWrtShell* pSourceShell,
1037                                  const SwMergeDescriptor& rMergeDescriptor)
1038 {
1039     // deconstruct mail merge type for better readability.
1040     // uppercase naming is intentional!
1041     const bool bMT_EMAIL   = rMergeDescriptor.nMergeType == DBMGR_MERGE_EMAIL;
1042     const bool bMT_SHELL   = rMergeDescriptor.nMergeType == DBMGR_MERGE_SHELL;
1043     const bool bMT_PRINTER = rMergeDescriptor.nMergeType == DBMGR_MERGE_PRINTER;
1044     const bool bMT_FILE    = rMergeDescriptor.nMergeType == DBMGR_MERGE_FILE;
1045 
1046     //check if the doc is synchronized and contains at least one linked section
1047     const bool bSynchronizedDoc = pSourceShell->IsLabelDoc() && pSourceShell->GetSectionFormatCount() > 1;
1048     const bool bNeedsTempFiles = ( bMT_EMAIL || bMT_FILE );
1049     const bool bIsMergeSilent = IsMergeSilent();
1050 
1051     bool bCheckSingleFile_ = rMergeDescriptor.bCreateSingleFile;
1052     OUString sPrefix_ = rMergeDescriptor.sPrefix;
1053     if( bMT_EMAIL )
1054     {
1055         assert( !rMergeDescriptor.bPrefixIsFilename );
1056         assert(!bCheckSingleFile_);
1057         bCheckSingleFile_ = false;
1058     }
1059     else if( bMT_SHELL || bMT_PRINTER )
1060     {
1061         assert(bCheckSingleFile_);
1062         bCheckSingleFile_ = true;
1063         assert(sPrefix_.isEmpty());
1064         sPrefix_.clear();
1065     }
1066     const bool bCreateSingleFile = bCheckSingleFile_;
1067     const OUString sDescriptorPrefix = sPrefix_;
1068 
1069     // Setup for dumping debugging documents
1070     static const sal_Int32 nMaxDumpDocs = []() {
1071         if (const char* sEnv = getenv("SW_DEBUG_MAILMERGE_DOCS"))
1072             return OUString(sEnv, strlen(sEnv), osl_getThreadTextEncoding()).toInt32();
1073         else
1074             return sal_Int32(0);
1075     }();
1076 
1077     ::rtl::Reference< MailDispatcher >          xMailDispatcher;
1078     ::rtl::Reference< IMailDispatcherListener > xMailListener;
1079     OUString                            sMailBodyMimeType;
1080     rtl_TextEncoding                    sMailEncoding = ::osl_getThreadTextEncoding();
1081 
1082     uno::Reference< beans::XPropertySet > xColumnProp;
1083     uno::Reference< beans::XPropertySet > xPasswordColumnProp;
1084 
1085     // Check for (mandatory) email or (optional) filename column
1086     SwDBFormatData aColumnDBFormat;
1087     bool bColumnName = !rMergeDescriptor.sDBcolumn.isEmpty();
1088     bool bPasswordColumnName = !rMergeDescriptor.sDBPasswordColumn.isEmpty();
1089 
1090     if( ! bColumnName )
1091     {
1092         if( bMT_EMAIL )
1093             return false;
1094     }
1095     else
1096     {
1097         uno::Reference< sdbcx::XColumnsSupplier > xColsSupp( m_pImpl->pMergeData->xResultSet, uno::UNO_QUERY );
1098         uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns();
1099         if( !xCols->hasByName( rMergeDescriptor.sDBcolumn ) )
1100             return false;
1101         uno::Any aCol = xCols->getByName( rMergeDescriptor.sDBcolumn );
1102         aCol >>= xColumnProp;
1103 
1104         if(bPasswordColumnName)
1105         {
1106             aCol = xCols->getByName( rMergeDescriptor.sDBPasswordColumn );
1107             aCol >>= xPasswordColumnProp;
1108         }
1109 
1110         aColumnDBFormat.xFormatter = m_pImpl->pMergeData->xFormatter;
1111         aColumnDBFormat.aNullDate  = m_pImpl->pMergeData->aNullDate;
1112 
1113         if( bMT_EMAIL )
1114         {
1115             // Reset internal mail accounting data
1116             m_pImpl->m_xLastMessage.clear();
1117 
1118             xMailDispatcher.set( new MailDispatcher(rMergeDescriptor.xSmtpServer) );
1119             xMailListener = new MailDispatcherListener_Impl( *this );
1120             xMailDispatcher->addListener( xMailListener );
1121             if(!rMergeDescriptor.bSendAsAttachment && rMergeDescriptor.bSendAsHTML)
1122             {
1123                 sMailBodyMimeType = "text/html; charset=" + OUString::createFromAscii(
1124                                     rtl_getBestMimeCharsetFromTextEncoding( sMailEncoding ));
1125                 SvxHtmlOptions& rHtmlOptions = SvxHtmlOptions::Get();
1126                 sMailEncoding = rHtmlOptions.GetTextEncoding();
1127             }
1128             else
1129                 sMailBodyMimeType = "text/plain; charset=UTF-8; format=flowed";
1130         }
1131     }
1132 
1133     SwDocShell  *pSourceDocSh = pSourceShell->GetView().GetDocShell();
1134 
1135     // setup the output format
1136     std::shared_ptr<const SfxFilter> pStoreToFilter = SwIoSystem::GetFileFilter(
1137         pSourceDocSh->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE));
1138     SfxFilterContainer* pFilterContainer = SwDocShell::Factory().GetFilterContainer();
1139     const OUString* pStoreToFilterOptions = nullptr;
1140 
1141     // if a save_to filter is set then use it - otherwise use the default
1142     if( bMT_EMAIL && !rMergeDescriptor.bSendAsAttachment )
1143     {
1144         OUString sExtension = rMergeDescriptor.bSendAsHTML ? OUString("html") : OUString("txt");
1145         pStoreToFilter = pFilterContainer->GetFilter4Extension(sExtension, SfxFilterFlags::EXPORT);
1146     }
1147     else if( !rMergeDescriptor.sSaveToFilter.isEmpty())
1148     {
1149         std::shared_ptr<const SfxFilter> pFilter =
1150                 pFilterContainer->GetFilter4FilterName( rMergeDescriptor.sSaveToFilter );
1151         if(pFilter)
1152         {
1153             pStoreToFilter = pFilter;
1154             if(!rMergeDescriptor.sSaveToFilterOptions.isEmpty())
1155                 pStoreToFilterOptions = &rMergeDescriptor.sSaveToFilterOptions;
1156         }
1157     }
1158     const bool bIsPDFexport = pStoreToFilter && pStoreToFilter->GetFilterName() == "writer_pdf_Export";
1159 
1160     m_aMergeStatus = MergeStatus::Ok;
1161 
1162     // in case of creating a single resulting file this has to be created here
1163     SwView*           pTargetView     = rMergeDescriptor.pMailMergeConfigItem ?
1164                                         rMergeDescriptor.pMailMergeConfigItem->GetTargetView() : nullptr;
1165     SwWrtShell*       pTargetShell    = nullptr;
1166     SwDoc*            pTargetDoc      = nullptr;
1167     SfxObjectShellRef xTargetDocShell;
1168 
1169     std::unique_ptr< utl::TempFile > aTempFile;
1170     sal_uInt16 nStartingPageNo = 0;
1171 
1172     std::shared_ptr<weld::GenericDialogController> xProgressDlg;
1173 
1174     try
1175     {
1176         vcl::Window *pSourceWindow = nullptr;
1177         if( !bIsMergeSilent )
1178         {
1179             // construct the process dialog
1180             pSourceWindow = &pSourceShell->GetView().GetEditWin();
1181             if (!bMT_PRINTER)
1182                 xProgressDlg = std::make_shared<CreateMonitor>(pSourceWindow->GetFrameWeld());
1183             else
1184             {
1185                 xProgressDlg = std::make_shared<PrintMonitor>(pSourceWindow->GetFrameWeld());
1186                 static_cast<PrintMonitor*>(xProgressDlg.get())->set_title(
1187                     pSourceDocSh->GetTitle(22));
1188             }
1189             weld::DialogController::runAsync(xProgressDlg, [this, &xProgressDlg](sal_Int32 nResult){
1190                 if (nResult == RET_CANCEL)
1191                     MergeCancel();
1192                 xProgressDlg.reset();
1193             });
1194 
1195             Application::Reschedule( true );
1196         }
1197 
1198         if( bCreateSingleFile && !pTargetView )
1199         {
1200             // create a target docshell to put the merged document into
1201             xTargetDocShell = lcl_CreateWorkingDocument( WorkingDocType::TARGET,
1202                 *pSourceShell, bMT_SHELL ? pSourceWindow : nullptr,
1203                 nullptr, &pTargetView, &pTargetShell, &pTargetDoc );
1204 
1205             if (nMaxDumpDocs)
1206                 lcl_SaveDebugDoc( xTargetDocShell.get(), "MergeDoc" );
1207         }
1208         else if( pTargetView )
1209         {
1210             pTargetShell = pTargetView->GetWrtShellPtr();
1211             pTargetDoc = pTargetShell->GetDoc();
1212             xTargetDocShell = pTargetView->GetDocShell();
1213         }
1214 
1215         if( bCreateSingleFile )
1216         {
1217             // determine the page style and number used at the start of the source document
1218             pSourceShell->SttEndDoc(true);
1219             nStartingPageNo = pSourceShell->GetVirtPageNum();
1220         }
1221 
1222         // Progress, to prohibit KeyInputs
1223         SfxProgress aProgress(pSourceDocSh, OUString(), 1);
1224 
1225         // lock all dispatchers
1226         SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst(pSourceDocSh);
1227         while (pViewFrame)
1228         {
1229             pViewFrame->GetDispatcher()->Lock(true);
1230             pViewFrame = SfxViewFrame::GetNext(*pViewFrame, pSourceDocSh);
1231         }
1232 
1233         sal_Int32 nDocNo = 1;
1234 
1235         // For single file mode, the number of pages in the target document so far, which is used
1236         // by AppendDoc() to adjust position of page-bound objects. Getting this information directly
1237         // from the target doc would require repeated layouts of the doc, which is expensive, but
1238         // it can be manually computed from the source documents (for which we do layouts, so the page
1239         // count is known, and there is a blank page between each of them in the target document).
1240         int targetDocPageCount = 0;
1241 
1242         if( !bIsMergeSilent && !bMT_PRINTER )
1243         {
1244             sal_Int32 nRecordCount = 1;
1245             lcl_getCountFromResultSet( nRecordCount, m_pImpl->pMergeData.get() );
1246 
1247             // Synchronized docs don't auto-advance the record set, but there is a
1248             // "security" check, which will always advance the record set, if there
1249             // is no "next record" field in a synchronized doc => nRecordPerDoc > 0
1250             sal_Int32 nRecordPerDoc = pSourceShell->GetDoc()
1251                     ->getIDocumentFieldsAccess().GetRecordsPerDocument();
1252             if ( bSynchronizedDoc && (nRecordPerDoc > 1) )
1253                 --nRecordPerDoc;
1254             assert( nRecordPerDoc > 0 );
1255 
1256             sal_Int32 nMaxDocs = nRecordCount / nRecordPerDoc;
1257             if ( 0 != nRecordCount % nRecordPerDoc )
1258                 nMaxDocs += 1;
1259             static_cast<CreateMonitor*>(xProgressDlg.get())->SetTotalCount(nMaxDocs);
1260         }
1261 
1262         sal_Int32 nStartRow, nEndRow;
1263         bool bFreezedLayouts = false;
1264         // to collect temporary email files
1265         std::vector< OUString> aFilesToRemove;
1266 
1267         // The SfxObjectShell will be closed explicitly later but
1268         // it is more safe to use SfxObjectShellLock here
1269         SfxObjectShellLock xWorkDocSh;
1270         SwView*            pWorkView             = nullptr;
1271         SwDoc*             pWorkDoc              = nullptr;
1272         SwDBManager*       pWorkDocOrigDBManager = nullptr;
1273         SwWrtShell*        pWorkShell            = nullptr;
1274         bool               bWorkDocInitialized   = false;
1275 
1276         do
1277         {
1278             nStartRow = m_pImpl->pMergeData ? m_pImpl->pMergeData->xResultSet->getRow() : 0;
1279 
1280             OUString sColumnData;
1281 
1282             // Read the indicated data column, which should contain a valid mail
1283             // address or an optional file name
1284             if( bMT_EMAIL || bColumnName )
1285             {
1286                 sColumnData = GetDBField( xColumnProp, aColumnDBFormat );
1287             }
1288 
1289             // create a new temporary file name - only done once in case of bCreateSingleFile
1290             if( bNeedsTempFiles && ( !bWorkDocInitialized || !bCreateSingleFile ))
1291             {
1292                 OUString sPrefix = sDescriptorPrefix;
1293                 OUString sLeading;
1294 
1295                 //#i97667# if the name is from a database field then it will be used _as is_
1296                 if( bColumnName && !bMT_EMAIL )
1297                 {
1298                     if (!sColumnData.isEmpty())
1299                         sLeading = sColumnData;
1300                     else
1301                         sLeading = "_";
1302                 }
1303                 else
1304                 {
1305                     INetURLObject aEntry( sPrefix );
1306                     sLeading = aEntry.GetBase();
1307                     aEntry.removeSegment();
1308                     sPrefix = aEntry.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1309                 }
1310 
1311                 OUString sExt(comphelper::string::stripStart(pStoreToFilter->GetDefaultExtension(), '*'));
1312                 aTempFile.reset( new utl::TempFile(sLeading, sColumnData.isEmpty(), &sExt, &sPrefix, true) );
1313                 if( !aTempFile->IsValid() )
1314                 {
1315                     ErrorHandler::HandleError( ERRCODE_IO_NOTSUPPORTED );
1316                     m_aMergeStatus = MergeStatus::Error;
1317                 }
1318             }
1319 
1320             uno::Sequence< beans::PropertyValue > aSaveToFilterDataOptions( rMergeDescriptor.aSaveToFilterData );
1321 
1322             if( bMT_EMAIL || bPasswordColumnName )
1323             {
1324                 OUString sPasswordColumnData = GetDBField( xPasswordColumnProp, aColumnDBFormat );
1325                 lcl_PrepareSaveFilterDataOptions( rMergeDescriptor.aSaveToFilterData, aSaveToFilterDataOptions, sPasswordColumnData );
1326             }
1327 
1328             if( IsMergeOk() )
1329             {
1330                 std::unique_ptr< INetURLObject > aTempFileURL;
1331                 if( bNeedsTempFiles )
1332                     aTempFileURL.reset( new INetURLObject(aTempFile->GetURL()));
1333                 if( !bIsMergeSilent ) {
1334                     if( !bMT_PRINTER )
1335                         static_cast<CreateMonitor*>(xProgressDlg.get())->SetCurrentPosition(nDocNo);
1336                     else {
1337                         PrintMonitor *pPrintMonDlg = static_cast<PrintMonitor*>(xProgressDlg.get());
1338                         pPrintMonDlg->m_xPrinter->set_label(bNeedsTempFiles
1339                             ? aTempFileURL->GetBase() : pSourceDocSh->GetTitle( 2));
1340                         OUString sStat = SwResId(STR_STATSTR_LETTER) + " " + OUString::number( nDocNo );
1341                         pPrintMonDlg->m_xPrintInfo->set_label(sStat);
1342                     }
1343                     //TODO xProgressDlg->queue_draw();
1344                 }
1345 
1346                 Scheduler::ProcessEventsToIdle();
1347 
1348                 // Create a copy of the source document and work with that one instead of the source.
1349                 // If we're not in the single file mode (which requires modifying the document for the merging),
1350                 // it is enough to do this just once. Currently PDF also has to be treated special.
1351                 if( !bWorkDocInitialized || bCreateSingleFile || bIsPDFexport )
1352                 {
1353                     assert( !xWorkDocSh.Is());
1354                     pWorkDocOrigDBManager = this;
1355                     xWorkDocSh = lcl_CreateWorkingDocument( WorkingDocType::COPY,
1356                         *pSourceShell, nullptr, &pWorkDocOrigDBManager,
1357                         &pWorkView, &pWorkShell, &pWorkDoc );
1358                     if ( (nMaxDumpDocs < 0) || (nDocNo <= nMaxDumpDocs) )
1359                         lcl_SaveDebugDoc( xWorkDocSh, "WorkDoc", nDocNo );
1360 
1361                     // #i69458# lock fields to prevent access to the result set while calculating layout
1362                     // tdf#92324: and do not unlock: keep document locked during printing to avoid
1363                     // ExpFields update during printing, generation of preview, etc.
1364                     pWorkShell->LockExpFields();
1365                     pWorkShell->CalcLayout();
1366                     // tdf#121168: Now force correct page descriptions applied to page frames. Without
1367                     // this, e.g., page frames starting with sections could have page descriptions set
1368                     // wrong. This would lead to wrong page styles applied in SwDoc::AppendDoc below.
1369                     pWorkShell->GetViewOptions()->SetIdle(true);
1370                     for (auto aLayout : pWorkShell->GetDoc()->GetAllLayouts())
1371                     {
1372                         aLayout->FreezeLayout(false);
1373                         aLayout->AllCheckPageDescs();
1374                     }
1375                 }
1376 
1377                 lcl_emitEvent(SfxEventHintId::SwEventFieldMerge, STR_SW_EVENT_FIELD_MERGE, xWorkDocSh);
1378 
1379                 // tdf#92324: Allow ExpFields update only by explicit instruction to avoid
1380                 // database cursor movement on any other fields update, for example during
1381                 // print preview and other operations
1382                 if ( pWorkShell->IsExpFieldsLocked() )
1383                     pWorkShell->UnlockExpFields();
1384                 pWorkShell->SwViewShell::UpdateFields();
1385                 pWorkShell->LockExpFields();
1386 
1387                 lcl_emitEvent(SfxEventHintId::SwEventFieldMergeFinished, STR_SW_EVENT_FIELD_MERGE_FINISHED, xWorkDocSh);
1388 
1389                 // also emit MailMergeEvent on XInterface if possible
1390                 const SwXMailMerge *pEvtSrc = GetMailMergeEvtSrc();
1391                 if(pEvtSrc)
1392                 {
1393                     rtl::Reference< SwXMailMerge > xRef(
1394                         const_cast<SwXMailMerge*>(pEvtSrc) );
1395                     text::MailMergeEvent aEvt( static_cast<text::XMailMergeBroadcaster*>(xRef.get()), xWorkDocSh->GetModel() );
1396                     SolarMutexReleaser rel;
1397                     xRef->LaunchMailMergeEvent( aEvt );
1398                 }
1399 
1400                 // working copy is merged - prepare final steps depending on merge options
1401 
1402                 if( bCreateSingleFile )
1403                 {
1404                     assert( pTargetShell && "no target shell available!" );
1405 
1406                     // prepare working copy and target to append
1407 
1408                     pWorkDoc->RemoveInvisibleContent();
1409                     // remove of invisible content has influence on page count and so on fields for page count,
1410                     // therefore layout has to be updated before fields are converted to text
1411                     pWorkShell->CalcLayout();
1412                     pWorkShell->ConvertFieldsToText();
1413                     pWorkShell->SetNumberingRestart();
1414                     if( bSynchronizedDoc )
1415                     {
1416                         lcl_RemoveSectionLinks( *pWorkShell );
1417                     }
1418 
1419                     if ( (nMaxDumpDocs < 0) || (nDocNo <= nMaxDumpDocs) )
1420                         lcl_SaveDebugDoc( xWorkDocSh, "WorkDoc", nDocNo );
1421 
1422                     // append the working document to the target document
1423                     if( targetDocPageCount % 2 == 1 )
1424                         ++targetDocPageCount; // Docs always start on odd pages (so offset must be even).
1425                     SwNodeIndex appendedDocStart = pTargetDoc->AppendDoc( *pWorkDoc,
1426                         nStartingPageNo, !bWorkDocInitialized, targetDocPageCount, nDocNo);
1427                     targetDocPageCount += pWorkShell->GetPageCnt();
1428 
1429                     if ( (nMaxDumpDocs < 0) || (nDocNo <= nMaxDumpDocs) )
1430                         lcl_SaveDebugDoc( xTargetDocShell.get(), "MergeDoc" );
1431 
1432                     if (bMT_SHELL)
1433                     {
1434                         SwDocMergeInfo aMergeInfo;
1435                         // Name of the mark is actually irrelevant, UNO bookmarks have internals names.
1436                         aMergeInfo.startPageInTarget = pTargetDoc->getIDocumentMarkAccess()->makeMark(
1437                             appendedDocStart, "", IDocumentMarkAccess::MarkType::UNO_BOOKMARK,
1438                             ::sw::mark::InsertMode::New);
1439                         aMergeInfo.nDBRow = nStartRow;
1440                         rMergeDescriptor.pMailMergeConfigItem->AddMergedDocument( aMergeInfo );
1441                     }
1442                 }
1443                 else
1444                 {
1445                     assert( bNeedsTempFiles );
1446                     assert( pWorkShell->IsExpFieldsLocked() );
1447 
1448                     // fields are locked, so it's fine to
1449                     // restore the old / empty DB manager for save
1450                     pWorkDoc->SetDBManager( pWorkDocOrigDBManager );
1451 
1452                     // save merged document
1453                     OUString sFileURL;
1454                     if( !lcl_SaveDoc( aTempFileURL.get(), pStoreToFilter, pStoreToFilterOptions,
1455                                     &aSaveToFilterDataOptions, bIsPDFexport,
1456                                     xWorkDocSh, *pWorkShell, &sFileURL ) )
1457                     {
1458                         m_aMergeStatus = MergeStatus::Error;
1459                     }
1460 
1461                     // back to the MM DB manager
1462                     pWorkDoc->SetDBManager( this );
1463 
1464                     if( bMT_EMAIL && !IsMergeError() )
1465                     {
1466                         // schedule file for later removal
1467                         aFilesToRemove.push_back( sFileURL );
1468 
1469                         if( !SwMailMergeHelper::CheckMailAddress( sColumnData ) )
1470                         {
1471                             OSL_FAIL("invalid e-Mail address in database column");
1472                         }
1473                         else
1474                         {
1475                             uno::Reference< mail::XMailMessage > xMessage = lcl_CreateMailFromDoc(
1476                                 rMergeDescriptor, sFileURL, sColumnData, sMailBodyMimeType,
1477                                 sMailEncoding, pStoreToFilter->GetMimeType() );
1478                             if( xMessage.is() )
1479                             {
1480                                 osl::MutexGuard aGuard( m_pImpl->m_aAllEmailSendMutex );
1481                                 m_pImpl->m_xLastMessage.set( xMessage );
1482                                 xMailDispatcher->enqueueMailMessage( xMessage );
1483                                 if( !xMailDispatcher->isStarted() )
1484                                     xMailDispatcher->start();
1485                             }
1486                         }
1487                     }
1488                 }
1489                 if( bCreateSingleFile || bIsPDFexport )
1490                 {
1491                     pWorkDoc->SetDBManager( pWorkDocOrigDBManager );
1492                     xWorkDocSh->DoClose();
1493                     xWorkDocSh = nullptr;
1494                 }
1495             }
1496 
1497             bWorkDocInitialized = true;
1498             nDocNo++;
1499             nEndRow = m_pImpl->pMergeData ? m_pImpl->pMergeData->xResultSet->getRow() : 0;
1500 
1501             // Freeze the layouts of the target document after the first inserted
1502             // sub-document, to get the correct PageDesc.
1503             if(!bFreezedLayouts && bCreateSingleFile)
1504             {
1505                 for ( auto aLayout : pTargetShell->GetDoc()->GetAllLayouts() )
1506                     aLayout->FreezeLayout(true);
1507                 bFreezedLayouts = true;
1508             }
1509         } while( IsMergeOk() &&
1510             ((bSynchronizedDoc && (nStartRow != nEndRow)) ? IsValidMergeRecord() : ToNextMergeRecord()));
1511 
1512         if ( xWorkDocSh.Is() && pWorkView->GetWrtShell().IsExpFieldsLocked() )
1513         {
1514             // Unlock document fields after merge complete
1515             pWorkView->GetWrtShell().UnlockExpFields();
1516         }
1517 
1518         if( !bCreateSingleFile )
1519         {
1520             if( bMT_PRINTER )
1521                 Printer::FinishPrintJob( pWorkView->GetPrinterController());
1522             if( !bIsPDFexport )
1523             {
1524                 pWorkDoc->SetDBManager( pWorkDocOrigDBManager );
1525                 xWorkDocSh->DoClose();
1526             }
1527         }
1528         else if( IsMergeOk() ) // && bCreateSingleFile
1529         {
1530             Application::Reschedule( true );
1531 
1532             // sw::DocumentLayoutManager::CopyLayoutFormat() did not generate
1533             // unique fly names, do it here once.
1534             pTargetDoc->SetInMailMerge(false);
1535             pTargetDoc->SetAllUniqueFlyNames();
1536 
1537             // Unfreeze target document layouts and correct all PageDescs.
1538             SAL_INFO( "sw.pageframe", "(MergeMailFiles pTargetShell->CalcLayout in" );
1539             pTargetShell->CalcLayout();
1540             SAL_INFO( "sw.pageframe", "MergeMailFiles pTargetShell->CalcLayout out)" );
1541             pTargetShell->GetViewOptions()->SetIdle( true );
1542             pTargetDoc->GetIDocumentUndoRedo().DoUndo( true );
1543             for ( auto aLayout : pTargetShell->GetDoc()->GetAllLayouts() )
1544             {
1545                 aLayout->FreezeLayout(false);
1546                 aLayout->AllCheckPageDescs();
1547             }
1548 
1549             Application::Reschedule( true );
1550 
1551             if( IsMergeOk() && bMT_FILE )
1552             {
1553                 // save merged document
1554                 assert( aTempFile );
1555                 INetURLObject aTempFileURL;
1556                 if (sDescriptorPrefix.isEmpty() || !rMergeDescriptor.bPrefixIsFilename)
1557                     aTempFileURL.SetURL( aTempFile->GetURL() );
1558                 else
1559                 {
1560                     aTempFileURL.SetURL(sDescriptorPrefix);
1561                     // remove the unneeded temporary file
1562                     aTempFile->EnableKillingFile();
1563                 }
1564                 if( !lcl_SaveDoc( &aTempFileURL, pStoreToFilter,
1565                         pStoreToFilterOptions, &rMergeDescriptor.aSaveToFilterData,
1566                         bIsPDFexport, xTargetDocShell.get(), *pTargetShell ) )
1567                 {
1568                     m_aMergeStatus = MergeStatus::Error;
1569                 }
1570             }
1571             else if( IsMergeOk() && bMT_PRINTER )
1572             {
1573                 // print the target document
1574                 uno::Sequence< beans::PropertyValue > aOptions( rMergeDescriptor.aPrintOptions );
1575                 lcl_PreparePrinterOptions( rMergeDescriptor.aPrintOptions, aOptions );
1576                 pTargetView->ExecPrint( aOptions, bIsMergeSilent, false/*bPrintAsync*/ );
1577             }
1578         }
1579 
1580         // we also show canceled documents, as long as there was no error
1581         if( !IsMergeError() && bMT_SHELL )
1582             // leave docshell available for caller (e.g. MM wizard)
1583             rMergeDescriptor.pMailMergeConfigItem->SetTargetView( pTargetView );
1584         else if( xTargetDocShell.is() )
1585             xTargetDocShell->DoClose();
1586 
1587         Application::Reschedule( true );
1588 
1589         if (xProgressDlg)
1590         {
1591             xProgressDlg->response(RET_OK);
1592         }
1593 
1594         // unlock all dispatchers
1595         pViewFrame = SfxViewFrame::GetFirst(pSourceDocSh);
1596         while (pViewFrame)
1597         {
1598             pViewFrame->GetDispatcher()->Lock(false);
1599             pViewFrame = SfxViewFrame::GetNext(*pViewFrame, pSourceDocSh);
1600         }
1601 
1602         SW_MOD()->SetView(&pSourceShell->GetView());
1603 
1604         if( xMailDispatcher.is() )
1605         {
1606             if( IsMergeOk() )
1607             {
1608                 // TODO: Instead of polling via an AutoTimer, post an Idle event,
1609                 // if the main loop has been made thread-safe.
1610                 AutoTimer aEmailDispatcherPollTimer;
1611                 aEmailDispatcherPollTimer.SetDebugName(
1612                     "sw::SwDBManager aEmailDispatcherPollTimer" );
1613                 aEmailDispatcherPollTimer.SetTimeout( 500 );
1614                 aEmailDispatcherPollTimer.Start();
1615                 while( IsMergeOk() && m_pImpl->m_xLastMessage.is() && !Application::IsQuit())
1616                     Application::Yield();
1617                 aEmailDispatcherPollTimer.Stop();
1618             }
1619             xMailDispatcher->stop();
1620             xMailDispatcher->shutdown();
1621         }
1622 
1623         // remove the temporary files
1624         // has to be done after xMailDispatcher is finished, as mails may be
1625         // delivered as message attachments!
1626         for( const OUString &sFileURL : aFilesToRemove )
1627             SWUnoHelper::UCB_DeleteFile( sFileURL );
1628     }
1629     catch (const uno::Exception&)
1630     {
1631         if (xProgressDlg)
1632         {
1633             xProgressDlg->response(RET_CANCEL);
1634         }
1635     }
1636 
1637     return !IsMergeError();
1638 }
1639 
MergeCancel()1640 void SwDBManager::MergeCancel()
1641 {
1642     if (m_aMergeStatus < MergeStatus::Cancel)
1643         m_aMergeStatus = MergeStatus::Cancel;
1644 }
1645 
1646 // determine the column's Numberformat and transfer to the forwarded Formatter,
1647 // if applicable.
GetColumnFormat(const OUString & rDBName,const OUString & rTableName,const OUString & rColNm,SvNumberFormatter * pNFormatr,LanguageType nLanguage)1648 sal_uLong SwDBManager::GetColumnFormat( const OUString& rDBName,
1649                                 const OUString& rTableName,
1650                                 const OUString& rColNm,
1651                                 SvNumberFormatter* pNFormatr,
1652                                 LanguageType nLanguage )
1653 {
1654     sal_uLong nRet = 0;
1655     if(pNFormatr)
1656     {
1657         uno::Reference< sdbc::XDataSource> xSource;
1658         uno::Reference< sdbc::XConnection> xConnection;
1659         bool bUseMergeData = false;
1660         uno::Reference< sdbcx::XColumnsSupplier> xColsSupp;
1661         bool bDisposeConnection = false;
1662         if(m_pImpl->pMergeData &&
1663             ((m_pImpl->pMergeData->sDataSource == rDBName && m_pImpl->pMergeData->sCommand == rTableName) ||
1664             (rDBName.isEmpty() && rTableName.isEmpty())))
1665         {
1666             xConnection = m_pImpl->pMergeData->xConnection;
1667             xSource = SwDBManager::getDataSourceAsParent(xConnection,rDBName);
1668             bUseMergeData = true;
1669             xColsSupp.set(m_pImpl->pMergeData->xResultSet, css::uno::UNO_QUERY);
1670         }
1671         if(!xConnection.is())
1672         {
1673             SwDBData aData;
1674             aData.sDataSource = rDBName;
1675             aData.sCommand = rTableName;
1676             aData.nCommandType = -1;
1677             SwDSParam* pParam = FindDSData(aData, false);
1678             if(pParam && pParam->xConnection.is())
1679             {
1680                 xConnection = pParam->xConnection;
1681                 xColsSupp.set(pParam->xResultSet, css::uno::UNO_QUERY);
1682             }
1683             else
1684             {
1685                 xConnection = RegisterConnection( rDBName );
1686                 bDisposeConnection = true;
1687             }
1688             if(bUseMergeData)
1689                 m_pImpl->pMergeData->xConnection = xConnection;
1690         }
1691         bool bDispose = !xColsSupp.is();
1692         if(bDispose)
1693         {
1694             xColsSupp = SwDBManager::GetColumnSupplier(xConnection, rTableName);
1695         }
1696         if(xColsSupp.is())
1697         {
1698             uno::Reference<container::XNameAccess> xCols;
1699             try
1700             {
1701                 xCols = xColsSupp->getColumns();
1702             }
1703             catch (const uno::Exception&)
1704             {
1705                 TOOLS_WARN_EXCEPTION("sw.mailmerge", "Exception in getColumns()");
1706             }
1707             if(!xCols.is() || !xCols->hasByName(rColNm))
1708                 return nRet;
1709             uno::Any aCol = xCols->getByName(rColNm);
1710             uno::Reference< beans::XPropertySet > xColumn;
1711             aCol >>= xColumn;
1712             nRet = GetColumnFormat(xSource, xConnection, xColumn, pNFormatr, nLanguage);
1713             if(bDispose)
1714             {
1715                 ::comphelper::disposeComponent( xColsSupp );
1716             }
1717             if(bDisposeConnection)
1718             {
1719                 ::comphelper::disposeComponent( xConnection );
1720             }
1721         }
1722         else
1723             nRet = pNFormatr->GetFormatIndex( NF_NUMBER_STANDARD, LANGUAGE_SYSTEM );
1724     }
1725     return nRet;
1726 }
1727 
GetColumnFormat(uno::Reference<sdbc::XDataSource> const & xSource_in,uno::Reference<sdbc::XConnection> const & xConnection,uno::Reference<beans::XPropertySet> const & xColumn,SvNumberFormatter * pNFormatr,LanguageType nLanguage)1728 sal_uLong SwDBManager::GetColumnFormat( uno::Reference< sdbc::XDataSource> const & xSource_in,
1729                         uno::Reference< sdbc::XConnection> const & xConnection,
1730                         uno::Reference< beans::XPropertySet> const & xColumn,
1731                         SvNumberFormatter* pNFormatr,
1732                         LanguageType nLanguage )
1733 {
1734     auto xSource = xSource_in;
1735 
1736     // set the NumberFormat in the doc if applicable
1737     sal_uLong nRet = 0;
1738 
1739     if(!xSource.is())
1740     {
1741         uno::Reference<container::XChild> xChild(xConnection, uno::UNO_QUERY);
1742         if ( xChild.is() )
1743             xSource.set(xChild->getParent(), uno::UNO_QUERY);
1744     }
1745     if(xSource.is() && xConnection.is() && xColumn.is() && pNFormatr)
1746     {
1747         rtl::Reference<SvNumberFormatsSupplierObj> pNumFormat = new SvNumberFormatsSupplierObj( pNFormatr );
1748         uno::Reference< util::XNumberFormats > xDocNumberFormats = pNumFormat->getNumberFormats();
1749         uno::Reference< util::XNumberFormatTypes > xDocNumberFormatTypes(xDocNumberFormats, uno::UNO_QUERY);
1750 
1751         css::lang::Locale aLocale( LanguageTag( nLanguage ).getLocale());
1752 
1753         //get the number formatter of the data source
1754         uno::Reference<beans::XPropertySet> xSourceProps(xSource, uno::UNO_QUERY);
1755         uno::Reference< util::XNumberFormats > xNumberFormats;
1756         if(xSourceProps.is())
1757         {
1758             uno::Any aFormats = xSourceProps->getPropertyValue("NumberFormatsSupplier");
1759             if(aFormats.hasValue())
1760             {
1761                 uno::Reference<util::XNumberFormatsSupplier> xSuppl;
1762                 aFormats >>= xSuppl;
1763                 if(xSuppl.is())
1764                 {
1765                     xNumberFormats = xSuppl->getNumberFormats();
1766                 }
1767             }
1768         }
1769         bool bUseDefault = true;
1770         try
1771         {
1772             uno::Any aFormatKey = xColumn->getPropertyValue("FormatKey");
1773             if(aFormatKey.hasValue())
1774             {
1775                 sal_Int32 nFormat = 0;
1776                 aFormatKey >>= nFormat;
1777                 if(xNumberFormats.is())
1778                 {
1779                     try
1780                     {
1781                         uno::Reference<beans::XPropertySet> xNumProps = xNumberFormats->getByKey( nFormat );
1782                         uno::Any aFormatString = xNumProps->getPropertyValue("FormatString");
1783                         uno::Any aLocaleVal = xNumProps->getPropertyValue("Locale");
1784                         OUString sFormat;
1785                         aFormatString >>= sFormat;
1786                         lang::Locale aLoc;
1787                         aLocaleVal >>= aLoc;
1788                         nFormat = xDocNumberFormats->queryKey( sFormat, aLoc, false );
1789                         if(NUMBERFORMAT_ENTRY_NOT_FOUND == sal::static_int_cast< sal_uInt32, sal_Int32>(nFormat))
1790                             nFormat = xDocNumberFormats->addNew( sFormat, aLoc );
1791                         nRet = nFormat;
1792                         bUseDefault = false;
1793                     }
1794                     catch (const uno::Exception&)
1795                     {
1796                         TOOLS_WARN_EXCEPTION("sw.mailmerge", "illegal number format key");
1797                     }
1798                 }
1799             }
1800         }
1801         catch(const uno::Exception&)
1802         {
1803             SAL_WARN("sw.mailmerge", "no FormatKey property found");
1804         }
1805         if(bUseDefault)
1806             nRet = dbtools::getDefaultNumberFormat(xColumn, xDocNumberFormatTypes,  aLocale);
1807     }
1808     return nRet;
1809 }
1810 
GetColumnType(const OUString & rDBName,const OUString & rTableName,const OUString & rColNm)1811 sal_Int32 SwDBManager::GetColumnType( const OUString& rDBName,
1812                           const OUString& rTableName,
1813                           const OUString& rColNm )
1814 {
1815     sal_Int32 nRet = sdbc::DataType::SQLNULL;
1816     SwDBData aData;
1817     aData.sDataSource = rDBName;
1818     aData.sCommand = rTableName;
1819     aData.nCommandType = -1;
1820     SwDSParam* pParam = FindDSData(aData, false);
1821     uno::Reference< sdbc::XConnection> xConnection;
1822     uno::Reference< sdbcx::XColumnsSupplier > xColsSupp;
1823     bool bDispose = false;
1824     if(pParam && pParam->xConnection.is())
1825     {
1826         xConnection = pParam->xConnection;
1827         xColsSupp.set( pParam->xResultSet, uno::UNO_QUERY );
1828     }
1829     else
1830     {
1831         xConnection = RegisterConnection( rDBName );
1832     }
1833     if( !xColsSupp.is() )
1834     {
1835         xColsSupp = SwDBManager::GetColumnSupplier(xConnection, rTableName);
1836         bDispose = true;
1837     }
1838     if(xColsSupp.is())
1839     {
1840         uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns();
1841         if(xCols->hasByName(rColNm))
1842         {
1843             uno::Any aCol = xCols->getByName(rColNm);
1844             uno::Reference<beans::XPropertySet> xCol;
1845             aCol >>= xCol;
1846             uno::Any aType = xCol->getPropertyValue("Type");
1847             aType >>= nRet;
1848         }
1849         if(bDispose)
1850             ::comphelper::disposeComponent( xColsSupp );
1851     }
1852     return nRet;
1853 }
1854 
GetConnection(const OUString & rDataSource,uno::Reference<sdbc::XDataSource> & rxSource,const SwView * pView)1855 uno::Reference< sdbc::XConnection> SwDBManager::GetConnection(const OUString& rDataSource,
1856                                                               uno::Reference<sdbc::XDataSource>& rxSource, const SwView *pView)
1857 {
1858     uno::Reference< sdbc::XConnection> xConnection;
1859     uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
1860     try
1861     {
1862         uno::Reference<sdb::XCompletedConnection> xComplConnection(dbtools::getDataSource(rDataSource, xContext), uno::UNO_QUERY);
1863         if ( xComplConnection.is() )
1864         {
1865             rxSource.set(xComplConnection, uno::UNO_QUERY);
1866             weld::Window* pWindow = pView ? pView->GetFrameWeld() : nullptr;
1867             uno::Reference< task::XInteractionHandler > xHandler( task::InteractionHandler::createWithParent(xContext, pWindow ? pWindow->GetXWindow() : nullptr), uno::UNO_QUERY_THROW );
1868             xConnection = xComplConnection->connectWithCompletion( xHandler );
1869         }
1870     }
1871     catch(const uno::Exception&)
1872     {
1873     }
1874 
1875     return xConnection;
1876 }
1877 
GetColumnSupplier(uno::Reference<sdbc::XConnection> const & xConnection,const OUString & rTableOrQuery,SwDBSelect eTableOrQuery)1878 uno::Reference< sdbcx::XColumnsSupplier> SwDBManager::GetColumnSupplier(uno::Reference<sdbc::XConnection> const & xConnection,
1879                                     const OUString& rTableOrQuery,
1880                                     SwDBSelect   eTableOrQuery)
1881 {
1882     uno::Reference< sdbcx::XColumnsSupplier> xRet;
1883     try
1884     {
1885         if(eTableOrQuery == SwDBSelect::UNKNOWN)
1886         {
1887             //search for a table with the given command name
1888             uno::Reference<sdbcx::XTablesSupplier> xTSupplier(xConnection, uno::UNO_QUERY);
1889             if(xTSupplier.is())
1890             {
1891                 uno::Reference<container::XNameAccess> xTables = xTSupplier->getTables();
1892                 eTableOrQuery = xTables->hasByName(rTableOrQuery) ?
1893                             SwDBSelect::TABLE : SwDBSelect::QUERY;
1894             }
1895         }
1896         sal_Int32 nCommandType = SwDBSelect::TABLE == eTableOrQuery ?
1897                 sdb::CommandType::TABLE : sdb::CommandType::QUERY;
1898         uno::Reference< lang::XMultiServiceFactory > xMgr( ::comphelper::getProcessServiceFactory() );
1899         uno::Reference<sdbc::XRowSet> xRowSet(xMgr->createInstance("com.sun.star.sdb.RowSet"), uno::UNO_QUERY);
1900 
1901         OUString sDataSource;
1902         uno::Reference<sdbc::XDataSource> xSource = SwDBManager::getDataSourceAsParent(xConnection, sDataSource);
1903         uno::Reference<beans::XPropertySet> xSourceProperties(xSource, uno::UNO_QUERY);
1904         if(xSourceProperties.is())
1905         {
1906             xSourceProperties->getPropertyValue("Name") >>= sDataSource;
1907         }
1908 
1909         uno::Reference<beans::XPropertySet> xRowProperties(xRowSet, uno::UNO_QUERY);
1910         xRowProperties->setPropertyValue("DataSourceName", uno::makeAny(sDataSource));
1911         xRowProperties->setPropertyValue("Command", uno::makeAny(rTableOrQuery));
1912         xRowProperties->setPropertyValue("CommandType", uno::makeAny(nCommandType));
1913         xRowProperties->setPropertyValue("FetchSize", uno::makeAny(sal_Int32(10)));
1914         xRowProperties->setPropertyValue("ActiveConnection", uno::makeAny(xConnection));
1915         xRowSet->execute();
1916         xRet.set( xRowSet, uno::UNO_QUERY );
1917     }
1918     catch (const uno::Exception&)
1919     {
1920         TOOLS_WARN_EXCEPTION("sw.mailmerge", "Exception in SwDBManager::GetColumnSupplier");
1921     }
1922 
1923     return xRet;
1924 }
1925 
GetDBField(uno::Reference<beans::XPropertySet> const & xColumnProps,const SwDBFormatData & rDBFormatData,double * pNumber)1926 OUString SwDBManager::GetDBField(uno::Reference<beans::XPropertySet> const & xColumnProps,
1927                         const SwDBFormatData& rDBFormatData,
1928                         double* pNumber)
1929 {
1930     uno::Reference< sdb::XColumn > xColumn(xColumnProps, uno::UNO_QUERY);
1931     OUString sRet;
1932     assert( xColumn.is() && "SwDBManager::::ImportDBField: illegal arguments" );
1933     if(!xColumn.is())
1934         return sRet;
1935 
1936     uno::Any aType = xColumnProps->getPropertyValue("Type");
1937     sal_Int32 eDataType = sdbc::DataType::SQLNULL;
1938     aType >>= eDataType;
1939     switch(eDataType)
1940     {
1941         case sdbc::DataType::CHAR:
1942         case sdbc::DataType::VARCHAR:
1943         case sdbc::DataType::LONGVARCHAR:
1944             try
1945             {
1946                 sRet = xColumn->getString();
1947                 sRet = sRet.replace( '\xb', '\n' ); // MSWord uses \xb as a newline
1948             }
1949             catch(const sdbc::SQLException&)
1950             {
1951             }
1952         break;
1953         case sdbc::DataType::BIT:
1954         case sdbc::DataType::BOOLEAN:
1955         case sdbc::DataType::TINYINT:
1956         case sdbc::DataType::SMALLINT:
1957         case sdbc::DataType::INTEGER:
1958         case sdbc::DataType::BIGINT:
1959         case sdbc::DataType::FLOAT:
1960         case sdbc::DataType::REAL:
1961         case sdbc::DataType::DOUBLE:
1962         case sdbc::DataType::NUMERIC:
1963         case sdbc::DataType::DECIMAL:
1964         case sdbc::DataType::DATE:
1965         case sdbc::DataType::TIME:
1966         case sdbc::DataType::TIMESTAMP:
1967         {
1968 
1969             try
1970             {
1971                 sRet = dbtools::DBTypeConversion::getFormattedValue(
1972                     xColumnProps,
1973                     rDBFormatData.xFormatter,
1974                     rDBFormatData.aLocale,
1975                     rDBFormatData.aNullDate);
1976                 if (pNumber)
1977                 {
1978                     double fVal = xColumn->getDouble();
1979                     if(!xColumn->wasNull())
1980                     {
1981                         *pNumber = fVal;
1982                     }
1983                 }
1984             }
1985             catch (const uno::Exception&)
1986             {
1987                 TOOLS_WARN_EXCEPTION("sw.mailmerge", "");
1988             }
1989 
1990         }
1991         break;
1992     }
1993 
1994     return sRet;
1995 }
1996 
1997 // checks if a desired data source table or query is open
IsDataSourceOpen(const OUString & rDataSource,const OUString & rTableOrQuery,bool bMergeShell)1998 bool    SwDBManager::IsDataSourceOpen(const OUString& rDataSource,
1999                                   const OUString& rTableOrQuery, bool bMergeShell)
2000 {
2001     if(m_pImpl->pMergeData)
2002     {
2003         return ((rDataSource == m_pImpl->pMergeData->sDataSource
2004                  && rTableOrQuery == m_pImpl->pMergeData->sCommand)
2005                 || (rDataSource.isEmpty() && rTableOrQuery.isEmpty()))
2006                && m_pImpl->pMergeData->xResultSet.is();
2007     }
2008     else if(!bMergeShell)
2009     {
2010         SwDBData aData;
2011         aData.sDataSource = rDataSource;
2012         aData.sCommand = rTableOrQuery;
2013         aData.nCommandType = -1;
2014         SwDSParam* pFound = FindDSData(aData, false);
2015         return (pFound && pFound->xResultSet.is());
2016     }
2017     return false;
2018 }
2019 
2020 // read column data at a specified position
GetColumnCnt(const OUString & rSourceName,const OUString & rTableName,const OUString & rColumnName,sal_uInt32 nAbsRecordId,LanguageType nLanguage,OUString & rResult,double * pNumber)2021 bool SwDBManager::GetColumnCnt(const OUString& rSourceName, const OUString& rTableName,
2022                            const OUString& rColumnName, sal_uInt32 nAbsRecordId,
2023                            LanguageType nLanguage,
2024                            OUString& rResult, double* pNumber)
2025 {
2026     bool bRet = false;
2027     SwDSParam* pFound = nullptr;
2028     //check if it's the merge data source
2029     if(m_pImpl->pMergeData &&
2030         rSourceName == m_pImpl->pMergeData->sDataSource &&
2031         rTableName == m_pImpl->pMergeData->sCommand)
2032     {
2033         pFound = m_pImpl->pMergeData.get();
2034     }
2035     else
2036     {
2037         SwDBData aData;
2038         aData.sDataSource = rSourceName;
2039         aData.sCommand = rTableName;
2040         aData.nCommandType = -1;
2041         pFound = FindDSData(aData, false);
2042     }
2043     if (!pFound)
2044         return false;
2045     //check validity of supplied record Id
2046     if(pFound->aSelection.hasElements())
2047     {
2048         //the destination has to be an element of the selection
2049         bool bFound = std::any_of(pFound->aSelection.begin(), pFound->aSelection.end(),
2050             [nAbsRecordId](const uno::Any& rSelection) {
2051                 sal_Int32 nSelection = 0;
2052                 rSelection >>= nSelection;
2053                 return nSelection == static_cast<sal_Int32>(nAbsRecordId);
2054             });
2055         if(!bFound)
2056             return false;
2057     }
2058     if( pFound->HasValidRecord() )
2059     {
2060         sal_Int32 nOldRow = 0;
2061         try
2062         {
2063             nOldRow = pFound->xResultSet->getRow();
2064         }
2065         catch(const uno::Exception&)
2066         {
2067             return false;
2068         }
2069         //position to the desired index
2070         bool bMove = true;
2071         if ( nOldRow != static_cast<sal_Int32>(nAbsRecordId) )
2072             bMove = lcl_MoveAbsolute(pFound, nAbsRecordId);
2073         if(bMove)
2074             bRet = lcl_GetColumnCnt(pFound, rColumnName, nLanguage, rResult, pNumber);
2075         if ( nOldRow != static_cast<sal_Int32>(nAbsRecordId) )
2076             lcl_MoveAbsolute(pFound, nOldRow);
2077     }
2078     return bRet;
2079 }
2080 
2081 // reads the column data at the current position
GetMergeColumnCnt(const OUString & rColumnName,LanguageType nLanguage,OUString & rResult,double * pNumber)2082 bool    SwDBManager::GetMergeColumnCnt(const OUString& rColumnName, LanguageType nLanguage,
2083                                    OUString &rResult, double *pNumber)
2084 {
2085     if( !IsValidMergeRecord() )
2086     {
2087         rResult.clear();
2088         return false;
2089     }
2090 
2091     bool bRet = lcl_GetColumnCnt(m_pImpl->pMergeData.get(), rColumnName, nLanguage, rResult, pNumber);
2092     return bRet;
2093 }
2094 
ToNextMergeRecord()2095 bool SwDBManager::ToNextMergeRecord()
2096 {
2097     assert( m_pImpl->pMergeData && m_pImpl->pMergeData->xResultSet.is() && "no data source in merge" );
2098     return lcl_ToNextRecord( m_pImpl->pMergeData.get() );
2099 }
2100 
FillCalcWithMergeData(SvNumberFormatter * pDocFormatter,LanguageType nLanguage,SwCalc & rCalc)2101 bool SwDBManager::FillCalcWithMergeData( SvNumberFormatter *pDocFormatter,
2102                                          LanguageType nLanguage, SwCalc &rCalc )
2103 {
2104     if( !IsValidMergeRecord() )
2105         return false;
2106 
2107     uno::Reference< sdbcx::XColumnsSupplier > xColsSupp( m_pImpl->pMergeData->xResultSet, uno::UNO_QUERY );
2108     if( !xColsSupp.is() )
2109         return false;
2110 
2111     {
2112         uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns();
2113         const uno::Sequence<OUString> aColNames = xCols->getElementNames();
2114         OUString aString;
2115 
2116         // add the "record number" variable, as SwCalc::VarLook would.
2117         rCalc.VarChange( GetAppCharClass().lowercase(
2118             SwFieldType::GetTypeStr(SwFieldTypesEnum::DatabaseSetNumber) ), GetSelectedRecordId() );
2119 
2120         for( const OUString& rColName : aColNames )
2121         {
2122             // get the column type
2123             sal_Int32 nColumnType = sdbc::DataType::SQLNULL;
2124             uno::Any aCol = xCols->getByName( rColName );
2125             uno::Reference<beans::XPropertySet> xColumnProps;
2126             aCol >>= xColumnProps;
2127             uno::Any aType = xColumnProps->getPropertyValue( "Type" );
2128             aType >>= nColumnType;
2129             double aNumber = DBL_MAX;
2130 
2131             lcl_GetColumnCnt( m_pImpl->pMergeData.get(), xColumnProps, nLanguage, aString, &aNumber );
2132 
2133             sal_uInt32 nFormat = GetColumnFormat( m_pImpl->pMergeData->sDataSource,
2134                                             m_pImpl->pMergeData->sCommand,
2135                                             rColName, pDocFormatter, nLanguage );
2136             // aNumber is overwritten by SwDBField::FormatValue, so store initial status
2137             bool colIsNumber = aNumber != DBL_MAX;
2138             bool bValidValue = SwDBField::FormatValue( pDocFormatter, aString, nFormat,
2139                                                        aNumber, nColumnType );
2140             if( colIsNumber )
2141             {
2142                 if( bValidValue )
2143                 {
2144                     SwSbxValue aValue;
2145                     aValue.PutDouble( aNumber );
2146                     aValue.SetDBvalue( true );
2147                     SAL_INFO( "sw.ui", "'" << rColName << "': " << aNumber << " / " << aString );
2148                     rCalc.VarChange( rColName, aValue );
2149                 }
2150             }
2151             else
2152             {
2153                 SwSbxValue aValue;
2154                 aValue.PutString( aString );
2155                 aValue.SetDBvalue( true );
2156                 SAL_INFO( "sw.ui", "'" << rColName << "': " << aString );
2157                 rCalc.VarChange( rColName, aValue );
2158             }
2159         }
2160     }
2161 
2162     return true;
2163 }
2164 
ToNextRecord(const OUString & rDataSource,const OUString & rCommand)2165 void SwDBManager::ToNextRecord(
2166     const OUString& rDataSource, const OUString& rCommand)
2167 {
2168     SwDSParam* pFound = nullptr;
2169     if(m_pImpl->pMergeData &&
2170         rDataSource == m_pImpl->pMergeData->sDataSource &&
2171         rCommand == m_pImpl->pMergeData->sCommand)
2172     {
2173         pFound = m_pImpl->pMergeData.get();
2174     }
2175     else
2176     {
2177         SwDBData aData;
2178         aData.sDataSource = rDataSource;
2179         aData.sCommand = rCommand;
2180         aData.nCommandType = -1;
2181         pFound = FindDSData(aData, false);
2182     }
2183     lcl_ToNextRecord( pFound );
2184 }
2185 
lcl_ToNextRecord(SwDSParam * pParam,const SwDBNextRecord action)2186 static bool lcl_ToNextRecord( SwDSParam* pParam, const SwDBNextRecord action )
2187 {
2188     bool bRet = true;
2189 
2190     assert( SwDBNextRecord::NEXT == action ||
2191          (SwDBNextRecord::FIRST == action && pParam) );
2192     if( nullptr == pParam )
2193         return false;
2194 
2195     if( action == SwDBNextRecord::FIRST )
2196     {
2197         pParam->nSelectionIndex = 0;
2198         pParam->bEndOfDB        = false;
2199     }
2200 
2201     if( !pParam->HasValidRecord() )
2202         return false;
2203 
2204     try
2205     {
2206         if( pParam->aSelection.hasElements() )
2207         {
2208             if( pParam->nSelectionIndex >= pParam->aSelection.getLength() )
2209                 pParam->bEndOfDB = true;
2210             else
2211             {
2212                 sal_Int32 nPos = 0;
2213                 pParam->aSelection.getConstArray()[ pParam->nSelectionIndex ] >>= nPos;
2214                 pParam->bEndOfDB = !pParam->xResultSet->absolute( nPos );
2215             }
2216         }
2217         else if( action == SwDBNextRecord::FIRST )
2218         {
2219             pParam->bEndOfDB = !pParam->xResultSet->first();
2220         }
2221         else
2222         {
2223             sal_Int32 nBefore = pParam->xResultSet->getRow();
2224             pParam->bEndOfDB = !pParam->xResultSet->next();
2225             if( !pParam->bEndOfDB && nBefore == pParam->xResultSet->getRow() )
2226             {
2227                 // next returned true but it didn't move
2228                 ::dbtools::throwFunctionSequenceException( pParam->xResultSet );
2229             }
2230         }
2231 
2232         ++pParam->nSelectionIndex;
2233         bRet = !pParam->bEndOfDB;
2234     }
2235     catch( const uno::Exception & )
2236     {
2237         // we allow merging with empty databases, so don't warn on init
2238         TOOLS_WARN_EXCEPTION_IF(action == SwDBNextRecord::NEXT,
2239                     "sw.mailmerge", "exception in ToNextRecord()");
2240         pParam->bEndOfDB = true;
2241         bRet = false;
2242     }
2243     return bRet;
2244 }
2245 
2246 // synchronized labels contain a next record field at their end
2247 // to assure that the next page can be created in mail merge
2248 // the cursor position must be validated
IsValidMergeRecord() const2249 bool SwDBManager::IsValidMergeRecord() const
2250 {
2251     return( m_pImpl->pMergeData && m_pImpl->pMergeData->HasValidRecord() );
2252 }
2253 
GetSelectedRecordId()2254 sal_uInt32  SwDBManager::GetSelectedRecordId()
2255 {
2256     sal_uInt32  nRet = 0;
2257     assert( m_pImpl->pMergeData &&
2258             m_pImpl->pMergeData->xResultSet.is() && "no data source in merge" );
2259     if(!m_pImpl->pMergeData || !m_pImpl->pMergeData->xResultSet.is())
2260         return 0;
2261     try
2262     {
2263         nRet = m_pImpl->pMergeData->xResultSet->getRow();
2264     }
2265     catch(const uno::Exception&)
2266     {
2267     }
2268     return nRet;
2269 }
2270 
ToRecordId(sal_Int32 nSet)2271 bool SwDBManager::ToRecordId(sal_Int32 nSet)
2272 {
2273     assert( m_pImpl->pMergeData &&
2274             m_pImpl->pMergeData->xResultSet.is() && "no data source in merge" );
2275     if(!m_pImpl->pMergeData || !m_pImpl->pMergeData->xResultSet.is()|| nSet < 0)
2276         return false;
2277     bool bRet = false;
2278     sal_Int32 nAbsPos = nSet;
2279     assert(nAbsPos >= 0);
2280     bRet = lcl_MoveAbsolute(m_pImpl->pMergeData.get(), nAbsPos);
2281     m_pImpl->pMergeData->bEndOfDB = !bRet;
2282     return bRet;
2283 }
2284 
OpenDataSource(const OUString & rDataSource,const OUString & rTableOrQuery)2285 bool SwDBManager::OpenDataSource(const OUString& rDataSource, const OUString& rTableOrQuery)
2286 {
2287     SwDBData aData;
2288     aData.sDataSource = rDataSource;
2289     aData.sCommand = rTableOrQuery;
2290     aData.nCommandType = -1;
2291 
2292     SwDSParam* pFound = FindDSData(aData, true);
2293     if(pFound->xResultSet.is())
2294         return true;
2295     SwDSParam* pParam = FindDSConnection(rDataSource, false);
2296     if(pParam && pParam->xConnection.is())
2297         pFound->xConnection = pParam->xConnection;
2298     if(pFound->xConnection.is())
2299     {
2300         try
2301         {
2302             uno::Reference< sdbc::XDatabaseMetaData >  xMetaData = pFound->xConnection->getMetaData();
2303             try
2304             {
2305                 pFound->bScrollable = xMetaData
2306                         ->supportsResultSetType(sal_Int32(sdbc::ResultSetType::SCROLL_INSENSITIVE));
2307             }
2308             catch(const uno::Exception&)
2309             {
2310                 // DB driver may not be ODBC 3.0 compliant
2311                 pFound->bScrollable = true;
2312             }
2313             pFound->xStatement = pFound->xConnection->createStatement();
2314             OUString aQuoteChar = xMetaData->getIdentifierQuoteString();
2315             OUString sStatement = "SELECT * FROM " + aQuoteChar + rTableOrQuery + aQuoteChar;
2316             pFound->xResultSet = pFound->xStatement->executeQuery( sStatement );
2317 
2318             //after executeQuery the cursor must be positioned
2319             pFound->bEndOfDB = !pFound->xResultSet->next();
2320             ++pFound->nSelectionIndex;
2321         }
2322         catch (const uno::Exception&)
2323         {
2324             pFound->xResultSet = nullptr;
2325             pFound->xStatement = nullptr;
2326             pFound->xConnection = nullptr;
2327         }
2328     }
2329     return pFound->xResultSet.is();
2330 }
2331 
RegisterConnection(OUString const & rDataSource)2332 uno::Reference< sdbc::XConnection> const & SwDBManager::RegisterConnection(OUString const& rDataSource)
2333 {
2334     SwDSParam* pFound = SwDBManager::FindDSConnection(rDataSource, true);
2335     uno::Reference< sdbc::XDataSource> xSource;
2336     if(!pFound->xConnection.is())
2337     {
2338         SwView* pView = (m_pDoc && m_pDoc->GetDocShell()) ? m_pDoc->GetDocShell()->GetView() : nullptr;
2339         pFound->xConnection = SwDBManager::GetConnection(rDataSource, xSource, pView);
2340         try
2341         {
2342             uno::Reference<lang::XComponent> xComponent(pFound->xConnection, uno::UNO_QUERY);
2343             if(xComponent.is())
2344                 xComponent->addEventListener(m_pImpl->m_xDisposeListener);
2345         }
2346         catch(const uno::Exception&)
2347         {
2348         }
2349     }
2350     return pFound->xConnection;
2351 }
2352 
GetSelectedRecordId(const OUString & rDataSource,const OUString & rTableOrQuery,sal_Int32 nCommandType)2353 sal_uInt32      SwDBManager::GetSelectedRecordId(
2354     const OUString& rDataSource, const OUString& rTableOrQuery, sal_Int32 nCommandType)
2355 {
2356     sal_uInt32 nRet = 0xffffffff;
2357     //check for merge data source first
2358     if(m_pImpl->pMergeData &&
2359         ((rDataSource == m_pImpl->pMergeData->sDataSource &&
2360         rTableOrQuery == m_pImpl->pMergeData->sCommand) ||
2361         (rDataSource.isEmpty() && rTableOrQuery.isEmpty())) &&
2362         (nCommandType == -1 || nCommandType == m_pImpl->pMergeData->nCommandType) &&
2363         m_pImpl->pMergeData->xResultSet.is())
2364     {
2365         nRet = GetSelectedRecordId();
2366     }
2367     else
2368     {
2369         SwDBData aData;
2370         aData.sDataSource = rDataSource;
2371         aData.sCommand = rTableOrQuery;
2372         aData.nCommandType = nCommandType;
2373         SwDSParam* pFound = FindDSData(aData, false);
2374         if(pFound && pFound->xResultSet.is())
2375         {
2376             try
2377             {   //if a selection array is set the current row at the result set may not be set yet
2378                 if(pFound->aSelection.hasElements())
2379                 {
2380                     sal_Int32 nSelIndex = pFound->nSelectionIndex;
2381                     if(nSelIndex >= pFound->aSelection.getLength())
2382                         nSelIndex = pFound->aSelection.getLength() -1;
2383                     pFound->aSelection.getConstArray()[nSelIndex] >>= nRet;
2384 
2385                 }
2386                 else
2387                     nRet = pFound->xResultSet->getRow();
2388             }
2389             catch(const uno::Exception&)
2390             {
2391             }
2392         }
2393     }
2394     return nRet;
2395 }
2396 
2397 // close all data sources - after fields were updated
CloseAll(bool bIncludingMerge)2398 void    SwDBManager::CloseAll(bool bIncludingMerge)
2399 {
2400     //the only thing done here is to reset the selection index
2401     //all connections stay open
2402     for (auto & pParam : m_DataSourceParams)
2403     {
2404         if (bIncludingMerge || pParam.get() != m_pImpl->pMergeData.get())
2405         {
2406             pParam->nSelectionIndex = 0;
2407             pParam->bEndOfDB = false;
2408             try
2409             {
2410                 if(!m_bInMerge && pParam->xResultSet.is())
2411                     pParam->xResultSet->first();
2412             }
2413             catch(const uno::Exception&)
2414             {}
2415         }
2416     }
2417 }
2418 
FindDSData(const SwDBData & rData,bool bCreate)2419 SwDSParam* SwDBManager::FindDSData(const SwDBData& rData, bool bCreate)
2420 {
2421     //prefer merge data if available
2422     if(m_pImpl->pMergeData &&
2423         ((rData.sDataSource == m_pImpl->pMergeData->sDataSource &&
2424         rData.sCommand == m_pImpl->pMergeData->sCommand) ||
2425         (rData.sDataSource.isEmpty() && rData.sCommand.isEmpty())) &&
2426         (rData.nCommandType == -1 || rData.nCommandType == m_pImpl->pMergeData->nCommandType ||
2427         (bCreate && m_pImpl->pMergeData->nCommandType == -1)))
2428     {
2429         return m_pImpl->pMergeData.get();
2430     }
2431 
2432     SwDSParam* pFound = nullptr;
2433     for (size_t nPos = m_DataSourceParams.size(); nPos; nPos--)
2434     {
2435         SwDSParam* pParam = m_DataSourceParams[nPos - 1].get();
2436         if(rData.sDataSource == pParam->sDataSource &&
2437             rData.sCommand == pParam->sCommand &&
2438             (rData.nCommandType == -1 || rData.nCommandType == pParam->nCommandType ||
2439             (bCreate && pParam->nCommandType == -1)))
2440             {
2441                 // calls from the calculator may add a connection with an invalid commandtype
2442                 //later added "real" data base connections have to re-use the already available
2443                 //DSData and set the correct CommandType
2444                 if(bCreate && pParam->nCommandType == -1)
2445                     pParam->nCommandType = rData.nCommandType;
2446                 pFound = pParam;
2447                 break;
2448             }
2449     }
2450     if(bCreate && !pFound)
2451     {
2452         pFound = new SwDSParam(rData);
2453         m_DataSourceParams.push_back(std::unique_ptr<SwDSParam>(pFound));
2454         try
2455         {
2456             uno::Reference<lang::XComponent> xComponent(pFound->xConnection, uno::UNO_QUERY);
2457             if(xComponent.is())
2458                 xComponent->addEventListener(m_pImpl->m_xDisposeListener);
2459         }
2460         catch(const uno::Exception&)
2461         {
2462         }
2463     }
2464     return pFound;
2465 }
2466 
FindDSConnection(const OUString & rDataSource,bool bCreate)2467 SwDSParam*  SwDBManager::FindDSConnection(const OUString& rDataSource, bool bCreate)
2468 {
2469     //prefer merge data if available
2470     if(m_pImpl->pMergeData && rDataSource == m_pImpl->pMergeData->sDataSource )
2471     {
2472         SetAsUsed(rDataSource);
2473         return m_pImpl->pMergeData.get();
2474     }
2475     SwDSParam* pFound = nullptr;
2476     for (const auto & pParam : m_DataSourceParams)
2477     {
2478         if(rDataSource == pParam->sDataSource)
2479         {
2480             SetAsUsed(rDataSource);
2481             pFound = pParam.get();
2482             break;
2483         }
2484     }
2485     if(bCreate && !pFound)
2486     {
2487         SwDBData aData;
2488         aData.sDataSource = rDataSource;
2489         pFound = new SwDSParam(aData);
2490         SetAsUsed(rDataSource);
2491         m_DataSourceParams.push_back(std::unique_ptr<SwDSParam>(pFound));
2492         try
2493         {
2494             uno::Reference<lang::XComponent> xComponent(pFound->xConnection, uno::UNO_QUERY);
2495             if(xComponent.is())
2496                 xComponent->addEventListener(m_pImpl->m_xDisposeListener);
2497         }
2498         catch(const uno::Exception&)
2499         {
2500         }
2501     }
2502     return pFound;
2503 }
2504 
GetAddressDBName()2505 const SwDBData& SwDBManager::GetAddressDBName()
2506 {
2507     return SW_MOD()->GetDBConfig()->GetAddressSource();
2508 }
2509 
GetExistingDatabaseNames()2510 uno::Sequence<OUString> SwDBManager::GetExistingDatabaseNames()
2511 {
2512     uno::Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
2513     uno::Reference<sdb::XDatabaseContext> xDBContext = sdb::DatabaseContext::create(xContext);
2514     return xDBContext->getElementNames();
2515 }
2516 
2517 namespace  sw
2518 {
GetDBunoType(const INetURLObject & rURL)2519 DBConnURIType GetDBunoType(const INetURLObject &rURL)
2520 {
2521     OUString sExt(rURL.GetFileExtension());
2522     DBConnURIType type = DBConnURIType::UNKNOWN;
2523 
2524     if (sExt == "odb")
2525     {
2526         type = DBConnURIType::ODB;
2527     }
2528     else if (sExt.equalsIgnoreAsciiCase("sxc")
2529         || sExt.equalsIgnoreAsciiCase("ods")
2530         || sExt.equalsIgnoreAsciiCase("xls")
2531         || sExt.equalsIgnoreAsciiCase("xlsx"))
2532     {
2533         type = DBConnURIType::CALC;
2534     }
2535     else if (sExt.equalsIgnoreAsciiCase("sxw") || sExt.equalsIgnoreAsciiCase("odt") || sExt.equalsIgnoreAsciiCase("doc") || sExt.equalsIgnoreAsciiCase("docx"))
2536     {
2537         type = DBConnURIType::WRITER;
2538     }
2539     else if (sExt.equalsIgnoreAsciiCase("dbf"))
2540     {
2541         type = DBConnURIType::DBASE;
2542     }
2543     else if (sExt.equalsIgnoreAsciiCase("csv") || sExt.equalsIgnoreAsciiCase("txt"))
2544     {
2545         type = DBConnURIType::FLAT;
2546     }
2547 #ifdef _WIN32
2548     else if (sExt.equalsIgnoreAsciiCase("mdb") || sExt.equalsIgnoreAsciiCase("mde"))
2549     {
2550         type = DBConnURIType::MSJET;
2551     }
2552     else if (sExt.equalsIgnoreAsciiCase("accdb") || sExt.equalsIgnoreAsciiCase("accde"))
2553     {
2554         type = DBConnURIType::MSACE;
2555     }
2556 #endif
2557     return type;
2558 }
2559 }
2560 
2561 namespace
2562 {
GetDBunoURI(const INetURLObject & rURL,DBConnURIType & rType)2563 uno::Any GetDBunoURI(const INetURLObject &rURL, DBConnURIType& rType)
2564 {
2565     uno::Any aURLAny;
2566 
2567     if (rType == DBConnURIType::UNKNOWN)
2568         rType = GetDBunoType(rURL);
2569 
2570     switch (rType) {
2571     case DBConnURIType::UNKNOWN:
2572     case DBConnURIType::ODB:
2573         break;
2574     case DBConnURIType::CALC:
2575     {
2576         OUString sDBURL = "sdbc:calc:" +
2577             rURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
2578         aURLAny <<= sDBURL;
2579     }
2580     break;
2581     case DBConnURIType::WRITER:
2582     {
2583         OUString sDBURL = "sdbc:writer:" +
2584             rURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
2585         aURLAny <<= sDBURL;
2586     }
2587     break;
2588     case DBConnURIType::DBASE:
2589     {
2590         INetURLObject aUrlTmp(rURL);
2591         aUrlTmp.removeSegment();
2592         aUrlTmp.removeFinalSlash();
2593         OUString sDBURL = "sdbc:dbase:" +
2594             aUrlTmp.GetMainURL(INetURLObject::DecodeMechanism::NONE);
2595         aURLAny <<= sDBURL;
2596     }
2597     break;
2598     case DBConnURIType::FLAT:
2599     {
2600         INetURLObject aUrlTmp(rURL);
2601         aUrlTmp.removeSegment();
2602         aUrlTmp.removeFinalSlash();
2603         OUString sDBURL = "sdbc:flat:" +
2604             //only the 'path' has to be added
2605             aUrlTmp.GetMainURL(INetURLObject::DecodeMechanism::NONE);
2606         aURLAny <<= sDBURL;
2607     }
2608     break;
2609     case DBConnURIType::MSJET:
2610 #ifdef _WIN32
2611     {
2612         OUString sDBURL("sdbc:ado:access:PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA SOURCE=" + rURL.PathToFileName());
2613         aURLAny <<= sDBURL;
2614     }
2615 #endif
2616     break;
2617     case DBConnURIType::MSACE:
2618 #ifdef _WIN32
2619     {
2620         OUString sDBURL("sdbc:ado:PROVIDER=Microsoft.ACE.OLEDB.12.0;DATA SOURCE=" + rURL.PathToFileName());
2621         aURLAny <<= sDBURL;
2622     }
2623 #endif
2624     break;
2625     }
2626     return aURLAny;
2627 }
2628 
2629 /// Returns the URL of this SwDoc.
getOwnURL(SfxObjectShell const * pDocShell)2630 OUString getOwnURL(SfxObjectShell const * pDocShell)
2631 {
2632     OUString aRet;
2633 
2634     if (!pDocShell)
2635         return aRet;
2636 
2637     const INetURLObject& rURLObject = pDocShell->GetMedium()->GetURLObject();
2638     aRet = rURLObject.GetMainURL(INetURLObject::DecodeMechanism::NONE);
2639     return aRet;
2640 }
2641 
2642 /**
2643 Loads a data source from file and registers it.
2644 
2645 In case of success it returns the registered name, otherwise an empty string.
2646 Optionally add a prefix to the registered DB name.
2647 */
LoadAndRegisterDataSource_Impl(DBConnURIType type,const uno::Reference<beans::XPropertySet> * pSettings,const INetURLObject & rURL,const OUString * pDestDir,SfxObjectShell * pDocShell)2648 OUString LoadAndRegisterDataSource_Impl(DBConnURIType type, const uno::Reference< beans::XPropertySet > *pSettings,
2649     const INetURLObject &rURL, const OUString *pDestDir, SfxObjectShell* pDocShell)
2650 {
2651     OUString sExt(rURL.GetFileExtension());
2652     uno::Any aTableFilterAny;
2653     uno::Any aSuppressVersionsAny;
2654     uno::Any aInfoAny;
2655     bool bStore = true;
2656     OUString sFind;
2657 
2658     uno::Any aURLAny = GetDBunoURI(rURL, type);
2659     switch (type) {
2660     case DBConnURIType::UNKNOWN:
2661     case DBConnURIType::CALC:
2662     case DBConnURIType::WRITER:
2663         break;
2664     case DBConnURIType::ODB:
2665         bStore = false;
2666         break;
2667     case DBConnURIType::FLAT:
2668     case DBConnURIType::DBASE:
2669         //set the filter to the file name without extension
2670         {
2671             uno::Sequence<OUString> aFilters { rURL.getBase(INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset) };
2672             aTableFilterAny <<= aFilters;
2673         }
2674         break;
2675     case DBConnURIType::MSJET:
2676     case DBConnURIType::MSACE:
2677         aSuppressVersionsAny <<= true;
2678         break;
2679     }
2680 
2681     try
2682     {
2683         uno::Reference<uno::XComponentContext> xContext(::comphelper::getProcessComponentContext());
2684         uno::Reference<sdb::XDatabaseContext> xDBContext = sdb::DatabaseContext::create(xContext);
2685 
2686         OUString sNewName = rURL.getName(
2687             INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::Unambiguous);
2688         sal_Int32 nExtLen = sExt.getLength();
2689         sNewName = sNewName.replaceAt(sNewName.getLength() - nExtLen - 1, nExtLen + 1, "");
2690 
2691         //find a unique name if sNewName already exists
2692         sFind = sNewName;
2693         sal_Int32 nIndex = 0;
2694         while (xDBContext->hasByName(sFind))
2695             sFind = sNewName + OUString::number(++nIndex);
2696 
2697         uno::Reference<uno::XInterface> xNewInstance;
2698         if (!bStore)
2699         {
2700             //odb-file
2701             uno::Any aDataSource = xDBContext->getByName(rURL.GetMainURL(INetURLObject::DecodeMechanism::NONE));
2702             aDataSource >>= xNewInstance;
2703         }
2704         else
2705         {
2706             xNewInstance = xDBContext->createInstance();
2707             uno::Reference<beans::XPropertySet> xDataProperties(xNewInstance, uno::UNO_QUERY);
2708 
2709             if (aURLAny.hasValue())
2710                 xDataProperties->setPropertyValue("URL", aURLAny);
2711             if (aTableFilterAny.hasValue())
2712                 xDataProperties->setPropertyValue("TableFilter", aTableFilterAny);
2713             if (aSuppressVersionsAny.hasValue())
2714                 xDataProperties->setPropertyValue("SuppressVersionColumns", aSuppressVersionsAny);
2715             if (aInfoAny.hasValue())
2716                 xDataProperties->setPropertyValue("Info", aInfoAny);
2717 
2718             if (DBConnURIType::FLAT == type && pSettings)
2719             {
2720                 uno::Any aSettings = xDataProperties->getPropertyValue("Settings");
2721                 uno::Reference < beans::XPropertySet > xDSSettings;
2722                 aSettings >>= xDSSettings;
2723                 ::comphelper::copyProperties(*pSettings, xDSSettings);
2724                 xDSSettings->setPropertyValue("Extension", uno::makeAny(sExt));
2725             }
2726 
2727             uno::Reference<sdb::XDocumentDataSource> xDS(xNewInstance, uno::UNO_QUERY_THROW);
2728             uno::Reference<frame::XStorable> xStore(xDS->getDatabaseDocument(), uno::UNO_QUERY_THROW);
2729             OUString aOwnURL = getOwnURL(pDocShell);
2730             if (aOwnURL.isEmpty())
2731             {
2732                 // Cannot embed, as embedded data source would need the URL of the parent document.
2733                 OUString const sOutputExt = ".odb";
2734                 OUString sHomePath(SvtPathOptions().GetWorkPath());
2735                 utl::TempFile aTempFile(sNewName, true, &sOutputExt, pDestDir ? pDestDir : &sHomePath);
2736                 const OUString& sTmpName = aTempFile.GetURL();
2737                 xStore->storeAsURL(sTmpName, uno::Sequence<beans::PropertyValue>());
2738             }
2739             else
2740             {
2741                 // Embed.
2742                 OUString aStreamRelPath = "EmbeddedDatabase";
2743                 uno::Reference<embed::XStorage> xStorage = pDocShell->GetStorage();
2744 
2745                 // Refer to the sub-storage name in the document settings, so
2746                 // we can load it again next time the file is imported.
2747                 uno::Reference<lang::XMultiServiceFactory> xFactory(pDocShell->GetModel(), uno::UNO_QUERY);
2748                 uno::Reference<beans::XPropertySet> xPropertySet(xFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY);
2749                 xPropertySet->setPropertyValue("EmbeddedDatabaseName", uno::makeAny(aStreamRelPath));
2750 
2751                 // Store it only after setting the above property, so that only one data source gets registered.
2752                 SwDBManager::StoreEmbeddedDataSource(xStore, xStorage, aStreamRelPath, aOwnURL);
2753             }
2754         }
2755         xDBContext->registerObject(sFind, xNewInstance);
2756     }
2757     catch (const uno::Exception&)
2758     {
2759         sFind.clear();
2760     }
2761     return sFind;
2762 }
2763 
2764 // Construct vnd.sun.star.pkg:// URL
ConstructVndSunStarPkgUrl(const OUString & rMainURL,std::u16string_view rStreamRelPath)2765 OUString ConstructVndSunStarPkgUrl(const OUString& rMainURL, std::u16string_view rStreamRelPath)
2766 {
2767     auto xContext(comphelper::getProcessComponentContext());
2768     auto xUri = css::uri::UriReferenceFactory::create(xContext)->parse(rMainURL);
2769     assert(xUri.is());
2770     xUri = css::uri::VndSunStarPkgUrlReferenceFactory::create(xContext)
2771         ->createVndSunStarPkgUrlReference(xUri);
2772     assert(xUri.is());
2773     return xUri->getUriReference() + "/"
2774         + INetURLObject::encode(
2775             rStreamRelPath, INetURLObject::PART_FPATH,
2776             INetURLObject::EncodeMechanism::All);
2777 }
2778 }
2779 
LoadAndRegisterDataSource(weld::Window * pParent,SwDocShell * pDocShell)2780 OUString SwDBManager::LoadAndRegisterDataSource(weld::Window* pParent, SwDocShell* pDocShell)
2781 {
2782     sfx2::FileDialogHelper aDlgHelper(ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE, FileDialogFlags::NONE, pParent);
2783     uno::Reference < ui::dialogs::XFilePicker3 > xFP = aDlgHelper.GetFilePicker();
2784 
2785     OUString sHomePath(SvtPathOptions().GetWorkPath());
2786     aDlgHelper.SetDisplayDirectory( sHomePath );
2787 
2788     OUString sFilterAll(SwResId(STR_FILTER_ALL));
2789     OUString sFilterAllData(SwResId(STR_FILTER_ALL_DATA));
2790     OUString sFilterSXB(SwResId(STR_FILTER_SXB));
2791     OUString sFilterSXC(SwResId(STR_FILTER_SXC));
2792     OUString sFilterSXW(SwResId(STR_FILTER_SXW));
2793     OUString sFilterDBF(SwResId(STR_FILTER_DBF));
2794     OUString sFilterXLS(SwResId(STR_FILTER_XLS));
2795     OUString sFilterDOC(SwResId(STR_FILTER_DOC));
2796     OUString sFilterTXT(SwResId(STR_FILTER_TXT));
2797     OUString sFilterCSV(SwResId(STR_FILTER_CSV));
2798 #ifdef _WIN32
2799     OUString sFilterMDB(SwResId(STR_FILTER_MDB));
2800     OUString sFilterACCDB(SwResId(STR_FILTER_ACCDB));
2801 #endif
2802     xFP->appendFilter( sFilterAll, "*" );
2803     xFP->appendFilter( sFilterAllData, "*.ods;*.sxc;*.odt;*.sxw;*.dbf;*.xls;*.xlsx;*.doc;*.docx;*.txt;*.csv");
2804 
2805     xFP->appendFilter( sFilterSXB, "*.odb" );
2806     xFP->appendFilter( sFilterSXC, "*.ods;*.sxc" );
2807     xFP->appendFilter( sFilterSXW, "*.odt;*.sxw" );
2808     xFP->appendFilter( sFilterDBF, "*.dbf" );
2809     xFP->appendFilter( sFilterXLS, "*.xls;*.xlsx" );
2810     xFP->appendFilter( sFilterDOC, "*.doc;*.docx" );
2811     xFP->appendFilter( sFilterTXT, "*.txt" );
2812     xFP->appendFilter( sFilterCSV, "*.csv" );
2813 #ifdef _WIN32
2814     xFP->appendFilter(sFilterMDB, "*.mdb;*.mde");
2815     xFP->appendFilter(sFilterACCDB, "*.accdb;*.accde");
2816 #endif
2817 
2818     xFP->setCurrentFilter( sFilterAll ) ;
2819     OUString sFind;
2820     if( ERRCODE_NONE == aDlgHelper.Execute() )
2821     {
2822         uno::Reference< beans::XPropertySet > aSettings;
2823         const INetURLObject aURL( xFP->getSelectedFiles().getConstArray()[0] );
2824         const DBConnURIType type = GetDBunoType( aURL );
2825 
2826         if( DBConnURIType::FLAT == type )
2827         {
2828             uno::Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
2829             uno::Reference < sdb::XTextConnectionSettings > xSettingsDlg = sdb::TextConnectionSettings::create(xContext);
2830             if( xSettingsDlg->execute() )
2831                 aSettings.set( uno::Reference < beans::XPropertySet >( xSettingsDlg, uno::UNO_QUERY_THROW ) );
2832         }
2833         sFind = LoadAndRegisterDataSource_Impl( type, DBConnURIType::FLAT == type ? &aSettings : nullptr, aURL, nullptr, pDocShell );
2834 
2835         m_aUncommittedRegistrations.push_back(std::pair<SwDocShell*, OUString>(pDocShell, sFind));
2836     }
2837     return sFind;
2838 }
2839 
StoreEmbeddedDataSource(const uno::Reference<frame::XStorable> & xStorable,const uno::Reference<embed::XStorage> & xStorage,const OUString & rStreamRelPath,const OUString & rOwnURL,bool bCopyTo)2840 void SwDBManager::StoreEmbeddedDataSource(const uno::Reference<frame::XStorable>& xStorable,
2841                                           const uno::Reference<embed::XStorage>& xStorage,
2842                                           const OUString& rStreamRelPath,
2843                                           const OUString& rOwnURL, bool bCopyTo)
2844 {
2845     // Construct vnd.sun.star.pkg:// URL for later loading, and TargetStorage/StreamRelPath for storing.
2846     OUString const sTmpName = ConstructVndSunStarPkgUrl(rOwnURL, rStreamRelPath);
2847 
2848     uno::Sequence<beans::PropertyValue> aSequence = comphelper::InitPropertySequence(
2849     {
2850         {"TargetStorage", uno::makeAny(xStorage)},
2851         {"StreamRelPath", uno::makeAny(rStreamRelPath)},
2852         {"BaseURI", uno::makeAny(rOwnURL)}
2853     });
2854     if (bCopyTo)
2855         xStorable->storeToURL(sTmpName, aSequence);
2856     else
2857         xStorable->storeAsURL(sTmpName, aSequence);
2858 }
2859 
LoadAndRegisterDataSource(const OUString & rURI,const OUString * pDestDir)2860 OUString SwDBManager::LoadAndRegisterDataSource(const OUString &rURI, const OUString *pDestDir)
2861 {
2862     return LoadAndRegisterDataSource_Impl( DBConnURIType::UNKNOWN, nullptr, INetURLObject(rURI), pDestDir, nullptr );
2863 }
2864 
2865 namespace
2866 {
2867     // tdf#117824 switch the embedded database away from using its current storage and point it to temporary storage
2868     // which allows the original storage to be deleted
switchEmbeddedDatabaseStorage(const uno::Reference<sdb::XDatabaseContext> & rDatabaseContext,const OUString & rName)2869     void switchEmbeddedDatabaseStorage(const uno::Reference<sdb::XDatabaseContext>& rDatabaseContext, const OUString& rName)
2870     {
2871         uno::Reference<sdb::XDocumentDataSource> xDS(rDatabaseContext->getByName(rName), uno::UNO_QUERY);
2872         if (!xDS)
2873             return;
2874         uno::Reference<document::XStorageBasedDocument> xStorageDoc(xDS->getDatabaseDocument(), uno::UNO_QUERY);
2875         if (!xStorageDoc)
2876             return;
2877         xStorageDoc->switchToStorage(comphelper::OStorageHelper::GetTemporaryStorage());
2878     }
2879 }
2880 
RevokeDataSource(const OUString & rName)2881 void SwDBManager::RevokeDataSource(const OUString& rName)
2882 {
2883     uno::Reference<sdb::XDatabaseContext> xDatabaseContext = sdb::DatabaseContext::create(comphelper::getProcessComponentContext());
2884     if (xDatabaseContext->hasByName(rName))
2885     {
2886         switchEmbeddedDatabaseStorage(xDatabaseContext, rName);
2887         xDatabaseContext->revokeObject(rName);
2888     }
2889 }
2890 
LoadAndRegisterEmbeddedDataSource(const SwDBData & rData,const SwDocShell & rDocShell)2891 void SwDBManager::LoadAndRegisterEmbeddedDataSource(const SwDBData& rData, const SwDocShell& rDocShell)
2892 {
2893     uno::Reference<sdb::XDatabaseContext> xDatabaseContext = sdb::DatabaseContext::create(comphelper::getProcessComponentContext());
2894 
2895     OUString sDataSource = rData.sDataSource;
2896 
2897     // Fallback, just in case the document would contain an embedded data source, but no DB fields.
2898     if (sDataSource.isEmpty())
2899         sDataSource = "EmbeddedDatabase";
2900 
2901     SwDBManager::RevokeDataSource( sDataSource );
2902 
2903     // Encode the stream name and the real path into a single URL.
2904     const INetURLObject& rURLObject = rDocShell.GetMedium()->GetURLObject();
2905     OUString const aURL = ConstructVndSunStarPkgUrl(
2906         rURLObject.GetMainURL(INetURLObject::DecodeMechanism::NONE),
2907         m_sEmbeddedName);
2908 
2909     uno::Reference<uno::XInterface> xDataSource(xDatabaseContext->getByName(aURL), uno::UNO_QUERY);
2910     xDatabaseContext->registerObject( sDataSource, xDataSource );
2911 
2912     // temp file - don't remember connection
2913     if (rData.sDataSource.isEmpty())
2914         m_aUncommittedRegistrations.push_back(std::pair<SwDocShell*, OUString>(nullptr, sDataSource));
2915 }
2916 
ExecuteFormLetter(SwWrtShell & rSh,const uno::Sequence<beans::PropertyValue> & rProperties)2917 void SwDBManager::ExecuteFormLetter( SwWrtShell& rSh,
2918                         const uno::Sequence<beans::PropertyValue>& rProperties)
2919 {
2920     //prevent second call
2921     if(m_pImpl->pMergeDialog)
2922         return ;
2923     OUString sDataSource, sDataTableOrQuery;
2924     uno::Sequence<uno::Any> aSelection;
2925 
2926     sal_Int32 nCmdType = sdb::CommandType::TABLE;
2927     uno::Reference< sdbc::XConnection> xConnection;
2928 
2929     svx::ODataAccessDescriptor aDescriptor(rProperties);
2930     sDataSource = aDescriptor.getDataSource();
2931     OSL_VERIFY(aDescriptor[svx::DataAccessDescriptorProperty::Command]      >>= sDataTableOrQuery);
2932     OSL_VERIFY(aDescriptor[svx::DataAccessDescriptorProperty::CommandType]  >>= nCmdType);
2933 
2934     if ( aDescriptor.has(svx::DataAccessDescriptorProperty::Selection) )
2935         aDescriptor[svx::DataAccessDescriptorProperty::Selection] >>= aSelection;
2936     if ( aDescriptor.has(svx::DataAccessDescriptorProperty::Connection) )
2937         aDescriptor[svx::DataAccessDescriptorProperty::Connection] >>= xConnection;
2938 
2939     if(sDataSource.isEmpty() || sDataTableOrQuery.isEmpty())
2940     {
2941         OSL_FAIL("PropertyValues missing or unset");
2942         return;
2943     }
2944 
2945     //always create a connection for the dialog and dispose it after the dialog has been closed
2946     SwDSParam* pFound = nullptr;
2947     if(!xConnection.is())
2948     {
2949         xConnection = SwDBManager::RegisterConnection(sDataSource);
2950         pFound = FindDSConnection(sDataSource, true);
2951     }
2952     SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create();
2953     m_pImpl->pMergeDialog = pFact->CreateMailMergeDlg(rSh.GetView().GetViewFrame()->GetFrameWeld(), rSh,
2954                                                      sDataSource,
2955                                                      sDataTableOrQuery,
2956                                                      nCmdType,
2957                                                      xConnection);
2958     if(m_pImpl->pMergeDialog->Execute() == RET_OK)
2959     {
2960         aDescriptor[svx::DataAccessDescriptorProperty::Selection] <<= m_pImpl->pMergeDialog->GetSelection();
2961 
2962         uno::Reference<sdbc::XResultSet> xResSet = m_pImpl->pMergeDialog->GetResultSet();
2963         if(xResSet.is())
2964             aDescriptor[svx::DataAccessDescriptorProperty::Cursor] <<= xResSet;
2965 
2966         // SfxObjectShellRef is ok, since there should be no control over the document lifetime here
2967         SfxObjectShellRef xDocShell = rSh.GetView().GetViewFrame()->GetObjectShell();
2968 
2969         lcl_emitEvent(SfxEventHintId::SwMailMerge, STR_SW_EVENT_MAIL_MERGE, xDocShell.get());
2970 
2971         // prepare mail merge descriptor
2972         SwMergeDescriptor aMergeDesc( m_pImpl->pMergeDialog->GetMergeType(), rSh, aDescriptor );
2973         aMergeDesc.sSaveToFilter = m_pImpl->pMergeDialog->GetSaveFilter();
2974         aMergeDesc.bCreateSingleFile = m_pImpl->pMergeDialog->IsSaveSingleDoc();
2975         aMergeDesc.bPrefixIsFilename = aMergeDesc.bCreateSingleFile;
2976         aMergeDesc.sPrefix = m_pImpl->pMergeDialog->GetTargetURL();
2977 
2978         if(!aMergeDesc.bCreateSingleFile)
2979         {
2980             if(m_pImpl->pMergeDialog->IsGenerateFromDataBase())
2981                 aMergeDesc.sDBcolumn = m_pImpl->pMergeDialog->GetColumnName();
2982 
2983             if(m_pImpl->pMergeDialog->IsFileEncryptedFromDataBase())
2984                 aMergeDesc.sDBPasswordColumn = m_pImpl->pMergeDialog->GetPasswordColumnName();
2985         }
2986 
2987         Merge( aMergeDesc );
2988 
2989         lcl_emitEvent(SfxEventHintId::SwMailMergeEnd, STR_SW_EVENT_MAIL_MERGE_END, xDocShell.get());
2990 
2991         // reset the cursor inside
2992         xResSet = nullptr;
2993         aDescriptor[svx::DataAccessDescriptorProperty::Cursor] <<= xResSet;
2994     }
2995     if(pFound)
2996     {
2997         for (const auto & pParam : m_DataSourceParams)
2998         {
2999             if (pParam.get() == pFound)
3000             {
3001                 try
3002                 {
3003                     uno::Reference<lang::XComponent> xComp(pParam->xConnection, uno::UNO_QUERY);
3004                     if(xComp.is())
3005                         xComp->dispose();
3006                 }
3007                 catch(const uno::RuntimeException&)
3008                 {
3009                     //may be disposed already since multiple entries may have used the same connection
3010                 }
3011                 break;
3012             }
3013             //pFound doesn't need to be removed/deleted -
3014             //this has been done by the SwConnectionDisposedListener_Impl already
3015         }
3016     }
3017     m_pImpl->pMergeDialog.disposeAndClear();
3018 }
3019 
InsertText(SwWrtShell & rSh,const uno::Sequence<beans::PropertyValue> & rProperties)3020 void SwDBManager::InsertText(SwWrtShell& rSh,
3021                         const uno::Sequence< beans::PropertyValue>& rProperties)
3022 {
3023     OUString sDataSource, sDataTableOrQuery;
3024     uno::Reference<sdbc::XResultSet>  xResSet;
3025     uno::Sequence<uno::Any> aSelection;
3026     sal_Int16 nCmdType = sdb::CommandType::TABLE;
3027     uno::Reference< sdbc::XConnection> xConnection;
3028     for(const beans::PropertyValue& rValue : rProperties)
3029     {
3030         if ( rValue.Name == "DataSourceName" )
3031             rValue.Value >>= sDataSource;
3032         else if ( rValue.Name == "Command" )
3033             rValue.Value >>= sDataTableOrQuery;
3034         else if ( rValue.Name == "Cursor" )
3035             rValue.Value >>= xResSet;
3036         else if ( rValue.Name == "Selection" )
3037             rValue.Value >>= aSelection;
3038         else if ( rValue.Name == "CommandType" )
3039             rValue.Value >>= nCmdType;
3040         else if ( rValue.Name == "ActiveConnection" )
3041             rValue.Value >>= xConnection;
3042     }
3043     if(sDataSource.isEmpty() || sDataTableOrQuery.isEmpty() || !xResSet.is())
3044     {
3045         OSL_FAIL("PropertyValues missing or unset");
3046         return;
3047     }
3048     uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
3049     uno::Reference<sdbc::XDataSource> xSource;
3050     uno::Reference<container::XChild> xChild(xConnection, uno::UNO_QUERY);
3051     if(xChild.is())
3052         xSource.set(xChild->getParent(), uno::UNO_QUERY);
3053     if(!xSource.is())
3054         xSource = dbtools::getDataSource(sDataSource, xContext);
3055     uno::Reference< sdbcx::XColumnsSupplier > xColSupp( xResSet, uno::UNO_QUERY );
3056     SwDBData aDBData;
3057     aDBData.sDataSource = sDataSource;
3058     aDBData.sCommand = sDataTableOrQuery;
3059     aDBData.nCommandType = nCmdType;
3060 
3061     SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create();
3062     ScopedVclPtr<AbstractSwInsertDBColAutoPilot> pDlg(pFact->CreateSwInsertDBColAutoPilot( rSh.GetView(),
3063                                                                                 xSource,
3064                                                                                 xColSupp,
3065                                                                                 aDBData ));
3066     if( RET_OK != pDlg->Execute() )
3067         return;
3068 
3069     OUString sDummy;
3070     if(!xConnection.is())
3071         xConnection = xSource->getConnection(sDummy, sDummy);
3072     try
3073     {
3074         pDlg->DataToDoc( aSelection , xSource, xConnection, xResSet);
3075     }
3076     catch (const uno::Exception&)
3077     {
3078         TOOLS_WARN_EXCEPTION("sw.mailmerge", "");
3079     }
3080 }
3081 
getDataSourceAsParent(const uno::Reference<sdbc::XConnection> & _xConnection,const OUString & _sDataSourceName)3082 uno::Reference<sdbc::XDataSource> SwDBManager::getDataSourceAsParent(const uno::Reference< sdbc::XConnection>& _xConnection,const OUString& _sDataSourceName)
3083 {
3084     uno::Reference<sdbc::XDataSource> xSource;
3085     try
3086     {
3087         uno::Reference<container::XChild> xChild(_xConnection, uno::UNO_QUERY);
3088         if ( xChild.is() )
3089             xSource.set(xChild->getParent(), uno::UNO_QUERY);
3090         if ( !xSource.is() )
3091             xSource = dbtools::getDataSource(_sDataSourceName, ::comphelper::getProcessComponentContext());
3092     }
3093     catch (const uno::Exception&)
3094     {
3095         TOOLS_WARN_EXCEPTION("sw.mailmerge", "getDataSourceAsParent()");
3096     }
3097     return xSource;
3098 }
3099 
createCursor(const OUString & _sDataSourceName,const OUString & _sCommand,sal_Int32 _nCommandType,const uno::Reference<sdbc::XConnection> & _xConnection,const SwView * pView)3100 uno::Reference<sdbc::XResultSet> SwDBManager::createCursor(const OUString& _sDataSourceName,
3101                                        const OUString& _sCommand,
3102                                        sal_Int32 _nCommandType,
3103                                        const uno::Reference<sdbc::XConnection>& _xConnection,
3104                                        const SwView* pView)
3105 {
3106     uno::Reference<sdbc::XResultSet> xResultSet;
3107     try
3108     {
3109         uno::Reference< lang::XMultiServiceFactory > xMgr( ::comphelper::getProcessServiceFactory() );
3110         if( xMgr.is() )
3111         {
3112             uno::Reference<uno::XInterface> xInstance = xMgr->createInstance("com.sun.star.sdb.RowSet");
3113             uno::Reference<beans::XPropertySet> xRowSetPropSet(xInstance, uno::UNO_QUERY);
3114             if(xRowSetPropSet.is())
3115             {
3116                 xRowSetPropSet->setPropertyValue("DataSourceName", uno::makeAny(_sDataSourceName));
3117                 xRowSetPropSet->setPropertyValue("ActiveConnection", uno::makeAny(_xConnection));
3118                 xRowSetPropSet->setPropertyValue("Command", uno::makeAny(_sCommand));
3119                 xRowSetPropSet->setPropertyValue("CommandType", uno::makeAny(_nCommandType));
3120 
3121                 uno::Reference< sdb::XCompletedExecution > xRowSet(xInstance, uno::UNO_QUERY);
3122 
3123                 if ( xRowSet.is() )
3124                 {
3125                     weld::Window* pWindow = pView ? pView->GetFrameWeld() : nullptr;
3126                     uno::Reference< task::XInteractionHandler > xHandler( task::InteractionHandler::createWithParent(comphelper::getComponentContext(xMgr), pWindow ? pWindow->GetXWindow() : nullptr), uno::UNO_QUERY_THROW );
3127                     xRowSet->executeWithCompletion(xHandler);
3128                 }
3129                 xResultSet.set(xRowSet, uno::UNO_QUERY);
3130             }
3131         }
3132     }
3133     catch (const uno::Exception&)
3134     {
3135         TOOLS_WARN_EXCEPTION("sw.mailmerge", "Caught exception while creating a new RowSet");
3136     }
3137     return xResultSet;
3138 }
3139 
setEmbeddedName(const OUString & rEmbeddedName,SwDocShell & rDocShell)3140 void SwDBManager::setEmbeddedName(const OUString& rEmbeddedName, SwDocShell& rDocShell)
3141 {
3142     bool bLoad = m_sEmbeddedName != rEmbeddedName && !rEmbeddedName.isEmpty();
3143     bool bRegisterListener = m_sEmbeddedName.isEmpty() && !rEmbeddedName.isEmpty();
3144 
3145     m_sEmbeddedName = rEmbeddedName;
3146 
3147     if (bLoad)
3148     {
3149         uno::Reference<embed::XStorage> xStorage = rDocShell.GetStorage();
3150         // It's OK that we don't have the named sub-storage yet, in case
3151         // we're in the process of creating it.
3152         if (xStorage->hasByName(rEmbeddedName))
3153             LoadAndRegisterEmbeddedDataSource(rDocShell.GetDoc()->GetDBData(), rDocShell);
3154     }
3155 
3156     if (bRegisterListener)
3157         // Register a remove listener, so we know when the embedded data source is removed.
3158         m_pImpl->m_xDataSourceRemovedListener = new SwDataSourceRemovedListener(*this);
3159 }
3160 
getEmbeddedName() const3161 const OUString& SwDBManager::getEmbeddedName() const
3162 {
3163     return m_sEmbeddedName;
3164 }
3165 
getDoc() const3166 SwDoc* SwDBManager::getDoc() const
3167 {
3168     return m_pDoc;
3169 }
3170 
releaseRevokeListener()3171 void SwDBManager::releaseRevokeListener()
3172 {
3173     if (m_pImpl->m_xDataSourceRemovedListener.is())
3174     {
3175         m_pImpl->m_xDataSourceRemovedListener->Dispose();
3176         m_pImpl->m_xDataSourceRemovedListener.clear();
3177     }
3178 }
3179 
ConnectionDisposedListener_Impl(SwDBManager & rManager)3180 SwDBManager::ConnectionDisposedListener_Impl::ConnectionDisposedListener_Impl(SwDBManager& rManager)
3181     : m_pDBManager(&rManager)
3182 {
3183 }
3184 
disposing(const lang::EventObject & rSource)3185 void SwDBManager::ConnectionDisposedListener_Impl::disposing( const lang::EventObject& rSource )
3186 {
3187     ::SolarMutexGuard aGuard;
3188 
3189     if (!m_pDBManager) return; // we're disposed too!
3190 
3191     uno::Reference<sdbc::XConnection> xSource(rSource.Source, uno::UNO_QUERY);
3192     for (size_t nPos = m_pDBManager->m_DataSourceParams.size(); nPos; nPos--)
3193     {
3194         SwDSParam* pParam = m_pDBManager->m_DataSourceParams[nPos - 1].get();
3195         if(pParam->xConnection.is() &&
3196                 (xSource == pParam->xConnection))
3197         {
3198             m_pDBManager->m_DataSourceParams.erase(
3199                     m_pDBManager->m_DataSourceParams.begin() + nPos - 1);
3200         }
3201     }
3202 }
3203 
PerformMailMerge(SwView const * pView)3204 std::shared_ptr<SwMailMergeConfigItem> SwDBManager::PerformMailMerge(SwView const * pView)
3205 {
3206     std::shared_ptr<SwMailMergeConfigItem> xConfigItem = pView->GetMailMergeConfigItem();
3207     if (!xConfigItem)
3208         return xConfigItem;
3209 
3210     svx::ODataAccessDescriptor aDescriptor;
3211     aDescriptor.setDataSource(xConfigItem->GetCurrentDBData().sDataSource);
3212     aDescriptor[ svx::DataAccessDescriptorProperty::Connection ]  <<= xConfigItem->GetConnection().getTyped();
3213     aDescriptor[ svx::DataAccessDescriptorProperty::Cursor ]      <<= xConfigItem->GetResultSet();
3214     aDescriptor[ svx::DataAccessDescriptorProperty::Command ]     <<= xConfigItem->GetCurrentDBData().sCommand;
3215     aDescriptor[ svx::DataAccessDescriptorProperty::CommandType ] <<= xConfigItem->GetCurrentDBData().nCommandType;
3216     aDescriptor[ svx::DataAccessDescriptorProperty::Selection ]   <<= xConfigItem->GetSelection();
3217 
3218     SwWrtShell& rSh = pView->GetWrtShell();
3219     xConfigItem->SetTargetView(nullptr);
3220 
3221     SwMergeDescriptor aMergeDesc(DBMGR_MERGE_SHELL, rSh, aDescriptor);
3222     aMergeDesc.pMailMergeConfigItem = xConfigItem.get();
3223     aMergeDesc.bCreateSingleFile = true;
3224     rSh.GetDBManager()->Merge(aMergeDesc);
3225 
3226     return xConfigItem;
3227 }
3228 
RevokeLastRegistrations()3229 void SwDBManager::RevokeLastRegistrations()
3230 {
3231     if (m_aUncommittedRegistrations.empty())
3232         return;
3233 
3234     SwView* pView = ( m_pDoc && m_pDoc->GetDocShell() ) ? m_pDoc->GetDocShell()->GetView() : nullptr;
3235     if (pView)
3236     {
3237         const std::shared_ptr<SwMailMergeConfigItem>& xConfigItem = pView->GetMailMergeConfigItem();
3238         if (xConfigItem)
3239         {
3240             xConfigItem->DisposeResultSet();
3241             xConfigItem->DocumentReloaded();
3242         }
3243     }
3244 
3245     for (auto it = m_aUncommittedRegistrations.begin(); it != m_aUncommittedRegistrations.end();)
3246     {
3247         if ((m_pDoc && it->first == m_pDoc->GetDocShell()) || it->first == nullptr)
3248         {
3249             RevokeDataSource(it->second);
3250             it = m_aUncommittedRegistrations.erase(it);
3251         }
3252         else
3253             ++it;
3254     }
3255 }
3256 
CommitLastRegistrations()3257 void SwDBManager::CommitLastRegistrations()
3258 {
3259     for (auto aIt = m_aUncommittedRegistrations.begin(); aIt != m_aUncommittedRegistrations.end();)
3260     {
3261         if (aIt->first == m_pDoc->GetDocShell() || aIt->first == nullptr)
3262         {
3263             m_aNotUsedConnections.push_back(aIt->second);
3264             aIt = m_aUncommittedRegistrations.erase(aIt);
3265         }
3266         else
3267             aIt++;
3268     }
3269 }
3270 
SetAsUsed(const OUString & rName)3271 void SwDBManager::SetAsUsed(const OUString& rName)
3272 {
3273     auto aFound = std::find(m_aNotUsedConnections.begin(), m_aNotUsedConnections.end(), rName);
3274     if (aFound != m_aNotUsedConnections.end())
3275         m_aNotUsedConnections.erase(aFound);
3276 }
3277 
RevokeNotUsedConnections()3278 void SwDBManager::RevokeNotUsedConnections()
3279 {
3280     for (auto aIt = m_aNotUsedConnections.begin(); aIt != m_aNotUsedConnections.end();)
3281     {
3282         RevokeDataSource(*aIt);
3283         aIt = m_aNotUsedConnections.erase(aIt);
3284     }
3285 }
3286 
3287 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3288