1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "mmoutputtypepage.hxx"
21 #include <mailmergewizard.hxx>
22 #include <mmconfigitem.hxx>
23 #include <strings.hrc>
24 #include <bitmaps.hlst>
25 #include <swtypes.hxx>
27 #include <osl/diagnose.h>
28 #include <rtl/ref.hxx>
29 #include <com/sun/star/mail/XSmtpService.hpp>
30 #include <vcl/idle.hxx>
31 #include <vcl/svapp.hxx>
32 #include <vcl/weld.hxx>
34 #include <swunohelper.hxx>
35 #include <mmresultdialogs.hxx>
36 #include <maildispatcher.hxx>
37 #include <imaildsplistener.hxx>
40 using namespace ::com::sun::star
;
42 SwMailMergeOutputTypePage::SwMailMergeOutputTypePage(weld::Container
* pPage
, SwMailMergeWizard
* pWizard
)
43 : vcl::OWizardPage(pPage
, pWizard
, "modules/swriter/ui/mmoutputtypepage.ui", "MMOutputTypePage")
45 , m_xLetterRB(m_xBuilder
->weld_radio_button("letter"))
46 , m_xMailRB(m_xBuilder
->weld_radio_button("email"))
47 , m_xLetterHint(m_xBuilder
->weld_label("letterft"))
48 , m_xMailHint(m_xBuilder
->weld_label("emailft"))
50 Link
<weld::Toggleable
&,void> aLink
= LINK(this, SwMailMergeOutputTypePage
, TypeHdl_Impl
);
51 m_xLetterRB
->connect_toggled(aLink
);
52 m_xMailRB
->connect_toggled(aLink
);
54 SwMailMergeConfigItem
& rConfigItem
= m_pWizard
->GetConfigItem();
55 if(rConfigItem
.IsOutputToLetter())
56 m_xLetterRB
->set_active(true);
58 m_xMailRB
->set_active(true);
59 TypeHdl_Impl(*m_xLetterRB
);
62 SwMailMergeOutputTypePage::~SwMailMergeOutputTypePage()
66 IMPL_LINK_NOARG(SwMailMergeOutputTypePage
, TypeHdl_Impl
, weld::Toggleable
&, void)
68 bool bLetter
= m_xLetterRB
->get_active();
69 m_xLetterHint
->set_visible(bLetter
);
70 m_xMailHint
->set_visible(!bLetter
);
71 m_pWizard
->GetConfigItem().SetOutputToLetter(bLetter
);
72 m_pWizard
->UpdateRoadmap();
75 struct SwSendMailDialog_Impl
77 friend class SwSendMailDialog
;
78 // The mutex is locked in SwSendMailDialog_Impl::GetNextDescriptor, which may be called
79 // both with mutex unlocked (inside SwSendMailDialog::SendMails), and with mutex locked
80 // (inside SwSendMailDialog::AddDocument).
81 std::recursive_mutex aDescriptorMutex
;
83 std::vector
< SwMailDescriptor
> aDescriptors
;
84 sal_uInt32 nCurrentDescriptor
;
85 ::rtl::Reference
< MailDispatcher
> xMailDispatcher
;
86 ::rtl::Reference
< IMailDispatcherListener
> xMailListener
;
87 uno::Reference
< mail::XMailService
> xConnectedInMailService
;
90 SwSendMailDialog_Impl() :
91 nCurrentDescriptor(0), aRemoveIdle("SwSendMailDialog_Impl aRemoveIdle")
93 aRemoveIdle
.SetPriority(TaskPriority::LOWEST
);
96 ~SwSendMailDialog_Impl()
98 // Shutdown must be called when the last reference to the
99 // mail dispatcher will be released in order to force a
100 // shutdown of the mail dispatcher thread.
101 // 'join' with the mail dispatcher thread leads to a
102 // deadlock (SolarMutex).
103 if( xMailDispatcher
.is() && !xMailDispatcher
->isShutdownRequested() )
104 xMailDispatcher
->shutdown();
106 const SwMailDescriptor
* GetNextDescriptor();
109 const SwMailDescriptor
* SwSendMailDialog_Impl::GetNextDescriptor()
111 std::scoped_lock
aGuard(aDescriptorMutex
);
112 if(nCurrentDescriptor
< aDescriptors
.size())
114 ++nCurrentDescriptor
;
115 return &aDescriptors
[nCurrentDescriptor
- 1];
122 class SwMailDispatcherListener_Impl
: public IMailDispatcherListener
124 SwSendMailDialog
& m_rSendMailDialog
;
127 explicit SwMailDispatcherListener_Impl(SwSendMailDialog
& rParentDlg
);
129 virtual void idle() override
;
130 virtual void mailDelivered(uno::Reference
< mail::XMailMessage
> xMailMessage
) override
;
131 virtual void mailDeliveryError(::rtl::Reference
<MailDispatcher
> xMailDispatcher
,
132 uno::Reference
< mail::XMailMessage
> xMailMessage
, const OUString
& sErrorMessage
) override
;
134 static void DeleteAttachments( uno::Reference
< mail::XMailMessage
> const & xMessage
);
139 SwMailDispatcherListener_Impl::SwMailDispatcherListener_Impl(SwSendMailDialog
& rParentDlg
)
140 : m_rSendMailDialog(rParentDlg
)
144 void SwMailDispatcherListener_Impl::idle()
146 SolarMutexGuard aGuard
;
147 m_rSendMailDialog
.AllMailsSent();
150 void SwMailDispatcherListener_Impl::mailDelivered(
151 uno::Reference
< mail::XMailMessage
> xMailMessage
)
153 SolarMutexGuard aGuard
;
154 m_rSendMailDialog
.DocumentSent( xMailMessage
, true, nullptr );
155 DeleteAttachments( xMailMessage
);
158 void SwMailDispatcherListener_Impl::mailDeliveryError(
159 ::rtl::Reference
<MailDispatcher
> /*xMailDispatcher*/,
160 uno::Reference
< mail::XMailMessage
> xMailMessage
,
161 const OUString
& sErrorMessage
)
163 SolarMutexGuard aGuard
;
164 m_rSendMailDialog
.DocumentSent( xMailMessage
, false, &sErrorMessage
);
165 DeleteAttachments( xMailMessage
);
168 void SwMailDispatcherListener_Impl::DeleteAttachments( uno::Reference
< mail::XMailMessage
> const & xMessage
)
170 const uno::Sequence
< mail::MailAttachment
> aAttachments
= xMessage
->getAttachments();
172 for(const auto& rAttachment
: aAttachments
)
176 uno::Reference
< beans::XPropertySet
> xTransferableProperties( rAttachment
.Data
, uno::UNO_QUERY_THROW
);
178 xTransferableProperties
->getPropertyValue("URL") >>= sURL
;
180 SWUnoHelper::UCB_DeleteFile( sURL
);
182 catch (const uno::Exception
&)
190 class SwSendWarningBox_Impl
: public weld::MessageDialogController
192 std::unique_ptr
<weld::TextView
> m_xDetailED
;
194 SwSendWarningBox_Impl(weld::Window
* pParent
, const OUString
& rDetails
)
195 : MessageDialogController(pParent
, "modules/swriter/ui/warnemaildialog.ui", "WarnEmailDialog", "grid")
196 , m_xDetailED(m_xBuilder
->weld_text_view("errors"))
198 m_xDetailED
->set_size_request(80 * m_xDetailED
->get_approximate_digit_width(),
199 8 * m_xDetailED
->get_text_height());
200 m_xDetailED
->set_text(rDetails
);
206 SwSendMailDialog::SwSendMailDialog(weld::Window
*pParent
, SwMailMergeConfigItem
& rConfigItem
)
207 : GenericDialogController(pParent
, "modules/swriter/ui/mmsendmails.ui", "SendMailsDialog")
208 , m_sContinue(SwResId( ST_CONTINUE
))
209 , m_sClose(SwResId(ST_CLOSE_DIALOG
))
210 , m_sSendingTo( SwResId(ST_SENDINGTO
))
211 , m_sCompleted( SwResId(ST_COMPLETED
))
212 , m_sFailed( SwResId(ST_FAILED
))
213 , m_sAddressInvalid(SwResId(ST_ADDRESS_INVALID
))
215 , m_bDestructionEnabled(false)
216 , m_pImpl(new SwSendMailDialog_Impl
)
217 , m_pConfigItem(&rConfigItem
)
218 , m_nExpectedCount(0)
219 , m_nProcessedCount(0)
221 , m_xTransferStatus(m_xBuilder
->weld_label("transferstatus"))
222 , m_xPaused(m_xBuilder
->weld_label("paused"))
223 , m_xProgressBar(m_xBuilder
->weld_progress_bar("progress"))
224 , m_xErrorStatus(m_xBuilder
->weld_label("errorstatus"))
225 , m_xStatus(m_xBuilder
->weld_tree_view("container"))
226 , m_xStop(m_xBuilder
->weld_button("stop"))
227 , m_xCancel(m_xBuilder
->weld_button("cancel"))
229 m_sStop
= m_xStop
->get_label();
230 m_sTransferStatus
= m_xTransferStatus
->get_label();
231 m_sErrorStatus
= m_xErrorStatus
->get_label();
233 Size
aSize(m_xStatus
->get_approximate_digit_width() * 28,
234 m_xStatus
->get_height_rows(20));
235 m_xStatus
->set_size_request(aSize
.Width(), aSize
.Height());
237 m_xStop
->connect_clicked(LINK( this, SwSendMailDialog
, StopHdl_Impl
));
238 m_xCancel
->connect_clicked(LINK( this, SwSendMailDialog
, CancelHdl_Impl
));
240 std::vector
<int> aWidths
242 o3tl::narrowing
<int>(m_xStatus
->get_checkbox_column_width()),
243 o3tl::narrowing
<int>(aSize
.Width()/3 * 2)
245 m_xStatus
->set_column_fixed_widths(aWidths
);
247 m_xPaused
->set_visible(false);
248 UpdateTransferStatus();
251 SwSendMailDialog::~SwSendMailDialog()
253 if(!m_pImpl
->xMailDispatcher
.is())
258 if(m_pImpl
->xMailDispatcher
->isStarted())
259 m_pImpl
->xMailDispatcher
->stop();
260 if(m_pImpl
->xConnectedInMailService
.is() && m_pImpl
->xConnectedInMailService
->isConnected())
261 m_pImpl
->xConnectedInMailService
->disconnect();
263 uno::Reference
<mail::XMailMessage
> xMessage
=
264 m_pImpl
->xMailDispatcher
->dequeueMailMessage();
267 SwMailDispatcherListener_Impl::DeleteAttachments( xMessage
);
268 xMessage
= m_pImpl
->xMailDispatcher
->dequeueMailMessage();
271 catch (const uno::Exception
&)
276 void SwSendMailDialog::AddDocument( SwMailDescriptor
const & rDesc
)
278 std::scoped_lock
aGuard(m_pImpl
->aDescriptorMutex
);
279 m_pImpl
->aDescriptors
.push_back(rDesc
);
280 // if the dialog is already running then continue sending of documents
281 if(m_pImpl
->xMailDispatcher
.is())
287 IMPL_LINK( SwSendMailDialog
, StopHdl_Impl
, weld::Button
&, rButton
, void )
290 if(!m_pImpl
->xMailDispatcher
.is())
293 if(m_pImpl
->xMailDispatcher
->isStarted())
295 m_pImpl
->xMailDispatcher
->stop();
296 rButton
.set_label(m_sContinue
);
301 m_pImpl
->xMailDispatcher
->start();
302 rButton
.set_label(m_sStop
);
307 IMPL_LINK_NOARG(SwSendMailDialog
, CancelHdl_Impl
, weld::Button
&, void)
311 if (m_bDestructionEnabled
)
312 m_xDialog
->response(RET_CANCEL
);
315 m_pImpl
->aRemoveIdle
.SetInvokeHandler( LINK( this, SwSendMailDialog
, RemoveThis
) );
316 m_pImpl
->aRemoveIdle
.Start();
320 IMPL_STATIC_LINK( SwSendMailDialog
, StartSendMails
, void*, pDialog
, void )
322 static_cast<SwSendMailDialog
*>(pDialog
)->SendMails();
325 IMPL_LINK( SwSendMailDialog
, RemoveThis
, Timer
*, pTimer
, void )
327 if( m_pImpl
->xMailDispatcher
.is() )
329 if(m_pImpl
->xMailDispatcher
->isStarted())
330 m_pImpl
->xMailDispatcher
->stop();
331 if(!m_pImpl
->xMailDispatcher
->isShutdownRequested())
332 m_pImpl
->xMailDispatcher
->shutdown();
335 if( m_bDestructionEnabled
&&
336 (!m_pImpl
->xMailDispatcher
.is() ||
337 !m_pImpl
->xMailDispatcher
->isRunning()))
339 m_xDialog
->response(RET_CANCEL
);
347 IMPL_STATIC_LINK( SwSendMailDialog
, StopSendMails
, void*, p
, void )
349 SwSendMailDialog
* pDialog
= static_cast<SwSendMailDialog
*>(p
);
350 if(pDialog
->m_pImpl
->xMailDispatcher
.is() &&
351 pDialog
->m_pImpl
->xMailDispatcher
->isStarted())
353 pDialog
->m_pImpl
->xMailDispatcher
->stop();
354 pDialog
->m_xStop
->set_label(pDialog
->m_sContinue
);
355 pDialog
->m_xPaused
->show();
359 void SwSendMailDialog::SendMails()
363 OSL_FAIL("config item not set");
366 auto xWait(std::make_unique
<weld::WaitObject
>(m_xDialog
.get()));
367 //get a mail server connection
368 uno::Reference
< mail::XSmtpService
> xSmtpServer
=
369 SwMailMergeHelper::ConnectToSmtpServer( *m_pConfigItem
,
370 m_pImpl
->xConnectedInMailService
,
371 OUString(), OUString(), m_xDialog
.get());
372 bool bIsLoggedIn
= xSmtpServer
.is() && xSmtpServer
->isConnected();
376 OSL_FAIL("create error message");
379 m_pImpl
->xMailDispatcher
.set( new MailDispatcher(xSmtpServer
));
381 m_pImpl
->xMailListener
= new SwMailDispatcherListener_Impl(*this);
382 m_pImpl
->xMailDispatcher
->addListener(m_pImpl
->xMailListener
);
385 m_pImpl
->xMailDispatcher
->start();
389 void SwSendMailDialog::IterateMails()
391 const SwMailDescriptor
* pCurrentMailDescriptor
= m_pImpl
->GetNextDescriptor();
392 while( pCurrentMailDescriptor
)
394 if (!SwMailMergeHelper::CheckMailAddress( pCurrentMailDescriptor
->sEMail
))
396 OUString sMessage
= m_sSendingTo
;
398 m_xStatus
->set_image(m_nProcessedCount
, RID_BMP_FORMULA_CANCEL
, 0);
399 m_xStatus
->set_text(m_nProcessedCount
, sMessage
.replaceFirst("%1", pCurrentMailDescriptor
->sEMail
), 1);
400 m_xStatus
->set_text(m_nProcessedCount
, m_sAddressInvalid
, 2);
403 UpdateTransferStatus( );
404 pCurrentMailDescriptor
= m_pImpl
->GetNextDescriptor();
407 rtl::Reference
<SwMailMessage
> pMessage
= new SwMailMessage
;
408 if(m_pConfigItem
->IsMailReplyTo())
409 pMessage
->setReplyToAddress(m_pConfigItem
->GetMailReplyTo());
410 pMessage
->addRecipient( pCurrentMailDescriptor
->sEMail
);
411 pMessage
->SetSenderName( m_pConfigItem
->GetMailDisplayName() );
412 pMessage
->SetSenderAddress( m_pConfigItem
->GetMailAddress() );
413 if(!pCurrentMailDescriptor
->sAttachmentURL
.isEmpty())
415 mail::MailAttachment aAttach
;
417 new SwMailTransferable(
418 pCurrentMailDescriptor
->sAttachmentURL
,
419 pCurrentMailDescriptor
->sAttachmentName
,
420 pCurrentMailDescriptor
->sMimeType
);
421 aAttach
.ReadableName
= pCurrentMailDescriptor
->sAttachmentName
;
422 pMessage
->addAttachment( aAttach
);
424 pMessage
->setSubject( pCurrentMailDescriptor
->sSubject
);
425 uno::Reference
< datatransfer::XTransferable
> xBody
=
426 new SwMailTransferable(
427 pCurrentMailDescriptor
->sBodyContent
,
428 pCurrentMailDescriptor
->sBodyMimeType
);
429 pMessage
->setBody( xBody
);
431 //CC and BCC are tokenized by ';'
432 if(!pCurrentMailDescriptor
->sCC
.isEmpty())
437 OUString sTmp
= pCurrentMailDescriptor
->sCC
.getToken( 0, ';', nPos
);
438 if( !sTmp
.isEmpty() )
439 pMessage
->addCcRecipient( sTmp
);
443 if(!pCurrentMailDescriptor
->sBCC
.isEmpty())
448 OUString sTmp
= pCurrentMailDescriptor
->sBCC
.getToken( 0, ';', nPos
);
449 if( !sTmp
.isEmpty() )
450 pMessage
->addBccRecipient( sTmp
);
454 m_pImpl
->xMailDispatcher
->enqueueMailMessage( pMessage
);
455 pCurrentMailDescriptor
= m_pImpl
->GetNextDescriptor();
457 UpdateTransferStatus();
460 void SwSendMailDialog::StartSend(sal_Int32 nExpectedCount
)
462 Application::PostUserEvent( LINK( this, SwSendMailDialog
,
463 StartSendMails
), this );
464 m_nExpectedCount
= nExpectedCount
> 0 ? nExpectedCount
: 1;
467 void SwSendMailDialog::DocumentSent( uno::Reference
< mail::XMailMessage
> const & xMessage
,
469 const OUString
* pError
)
471 //sending should stop on send errors, except after last error - it will stop in AllMailsSent
472 if (pError
&& m_nProcessedCount
+ 1 < m_nExpectedCount
&&
473 m_pImpl
->xMailDispatcher
.is() && m_pImpl
->xMailDispatcher
->isStarted())
475 Application::PostUserEvent( LINK( this, SwSendMailDialog
,
476 StopSendMails
), this );
478 OUString
sInsertImg(bResult
? OUString(RID_BMP_FORMULA_APPLY
) : OUString(RID_BMP_FORMULA_CANCEL
));
480 OUString sMessage
= m_sSendingTo
;
482 m_xStatus
->set_image(m_nProcessedCount
, sInsertImg
, 0);
483 m_xStatus
->set_text(m_nProcessedCount
, sMessage
.replaceFirst("%1", xMessage
->getRecipients()[0]), 1);
484 m_xStatus
->set_text(m_nProcessedCount
, bResult
? m_sCompleted
: m_sFailed
, 2);
489 UpdateTransferStatus( );
493 SwSendWarningBox_Impl
aDlg(m_xDialog
.get(), *pError
);
498 void SwSendMailDialog::UpdateTransferStatus()
500 OUString
sStatus( m_sTransferStatus
);
501 sStatus
= sStatus
.replaceFirst("%1", OUString::number(m_nProcessedCount
) );
502 sStatus
= sStatus
.replaceFirst("%2", OUString::number(m_nExpectedCount
));
503 m_xTransferStatus
->set_label(sStatus
);
505 sStatus
= m_sErrorStatus
.replaceFirst("%1", OUString::number(m_nErrorCount
) );
506 m_xErrorStatus
->set_label(sStatus
);
508 if (!m_pImpl
->aDescriptors
.empty())
510 assert(m_nExpectedCount
&& "div-by-zero");
511 m_xProgressBar
->set_percentage(m_nProcessedCount
* 100 / m_nExpectedCount
);
514 m_xProgressBar
->set_percentage(0);
517 void SwSendMailDialog::AllMailsSent()
519 if (m_nProcessedCount
== m_nExpectedCount
)
521 m_xStop
->set_sensitive(false);
522 m_xCancel
->set_label(m_sClose
);
523 // Leave open if some kind of error occurred
524 if (m_nErrorCount
== 0)
527 m_xDialog
->response(RET_CANCEL
);
532 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */