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 "mmoutputtypepage.hxx"
21 #include <mailmergewizard.hxx>
22 #include <mmconfigitem.hxx>
23 #include <dbui.hrc>
24 #include <strings.hrc>
25 #include <bitmaps.hlst>
26 #include <swtypes.hxx>
27
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>
33
34 #include <cmdid.h>
35 #include <swunohelper.hxx>
36 #include <mmresultdialogs.hxx>
37 #include <maildispatcher.hxx>
38 #include <imaildsplistener.hxx>
39
40 using namespace ::com::sun::star;
41
SwMailMergeOutputTypePage(weld::Container * pPage,SwMailMergeWizard * pWizard)42 SwMailMergeOutputTypePage::SwMailMergeOutputTypePage(weld::Container* pPage, SwMailMergeWizard* pWizard)
43 : vcl::OWizardPage(pPage, pWizard, "modules/swriter/ui/mmoutputtypepage.ui", "MMOutputTypePage")
44 , m_pWizard(pWizard)
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"))
49 {
50 Link<weld::ToggleButton&,void> aLink = LINK(this, SwMailMergeOutputTypePage, TypeHdl_Impl);
51 m_xLetterRB->connect_toggled(aLink);
52 m_xMailRB->connect_toggled(aLink);
53
54 SwMailMergeConfigItem& rConfigItem = m_pWizard->GetConfigItem();
55 if(rConfigItem.IsOutputToLetter())
56 m_xLetterRB->set_active(true);
57 else
58 m_xMailRB->set_active(true);
59 TypeHdl_Impl(*m_xLetterRB);
60 }
61
~SwMailMergeOutputTypePage()62 SwMailMergeOutputTypePage::~SwMailMergeOutputTypePage()
63 {
64 }
65
IMPL_LINK_NOARG(SwMailMergeOutputTypePage,TypeHdl_Impl,weld::ToggleButton &,void)66 IMPL_LINK_NOARG(SwMailMergeOutputTypePage, TypeHdl_Impl, weld::ToggleButton&, void)
67 {
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();
73 }
74
75 struct SwSendMailDialog_Impl
76 {
77 friend class SwSendMailDialog;
78 ::osl::Mutex aDescriptorMutex;
79
80 std::vector< SwMailDescriptor > aDescriptors;
81 sal_uInt32 nCurrentDescriptor;
82 ::rtl::Reference< MailDispatcher > xMailDispatcher;
83 ::rtl::Reference< IMailDispatcherListener> xMailListener;
84 uno::Reference< mail::XMailService > xConnectedInMailService;
85 Idle aRemoveIdle;
86
SwSendMailDialog_ImplSwSendMailDialog_Impl87 SwSendMailDialog_Impl() :
88 nCurrentDescriptor(0)
89 {
90 aRemoveIdle.SetPriority(TaskPriority::LOWEST);
91 }
92
~SwSendMailDialog_ImplSwSendMailDialog_Impl93 ~SwSendMailDialog_Impl()
94 {
95 // Shutdown must be called when the last reference to the
96 // mail dispatcher will be released in order to force a
97 // shutdown of the mail dispatcher thread.
98 // 'join' with the mail dispatcher thread leads to a
99 // deadlock (SolarMutex).
100 if( xMailDispatcher.is() && !xMailDispatcher->isShutdownRequested() )
101 xMailDispatcher->shutdown();
102 }
103 const SwMailDescriptor* GetNextDescriptor();
104 };
105
GetNextDescriptor()106 const SwMailDescriptor* SwSendMailDialog_Impl::GetNextDescriptor()
107 {
108 ::osl::MutexGuard aGuard(aDescriptorMutex);
109 if(nCurrentDescriptor < aDescriptors.size())
110 {
111 ++nCurrentDescriptor;
112 return &aDescriptors[nCurrentDescriptor - 1];
113 }
114 return nullptr;
115 }
116
117 class SwMailDispatcherListener_Impl : public IMailDispatcherListener
118 {
119 SwSendMailDialog& m_rSendMailDialog;
120
121 public:
122 explicit SwMailDispatcherListener_Impl(SwSendMailDialog& rParentDlg);
123
124 virtual void idle() override;
125 virtual void mailDelivered(uno::Reference< mail::XMailMessage> xMailMessage) override;
126 virtual void mailDeliveryError(::rtl::Reference<MailDispatcher> xMailDispatcher,
127 uno::Reference< mail::XMailMessage> xMailMessage, const OUString& sErrorMessage) override;
128
129 static void DeleteAttachments( uno::Reference< mail::XMailMessage > const & xMessage );
130 };
131
SwMailDispatcherListener_Impl(SwSendMailDialog & rParentDlg)132 SwMailDispatcherListener_Impl::SwMailDispatcherListener_Impl(SwSendMailDialog& rParentDlg)
133 : m_rSendMailDialog(rParentDlg)
134 {
135 }
136
idle()137 void SwMailDispatcherListener_Impl::idle()
138 {
139 SolarMutexGuard aGuard;
140 m_rSendMailDialog.AllMailsSent();
141 }
142
mailDelivered(uno::Reference<mail::XMailMessage> xMailMessage)143 void SwMailDispatcherListener_Impl::mailDelivered(
144 uno::Reference< mail::XMailMessage> xMailMessage)
145 {
146 SolarMutexGuard aGuard;
147 m_rSendMailDialog.DocumentSent( xMailMessage, true, nullptr );
148 DeleteAttachments( xMailMessage );
149 }
150
mailDeliveryError(::rtl::Reference<MailDispatcher>,uno::Reference<mail::XMailMessage> xMailMessage,const OUString & sErrorMessage)151 void SwMailDispatcherListener_Impl::mailDeliveryError(
152 ::rtl::Reference<MailDispatcher> /*xMailDispatcher*/,
153 uno::Reference< mail::XMailMessage> xMailMessage,
154 const OUString& sErrorMessage)
155 {
156 SolarMutexGuard aGuard;
157 m_rSendMailDialog.DocumentSent( xMailMessage, false, &sErrorMessage );
158 DeleteAttachments( xMailMessage );
159 }
160
DeleteAttachments(uno::Reference<mail::XMailMessage> const & xMessage)161 void SwMailDispatcherListener_Impl::DeleteAttachments( uno::Reference< mail::XMailMessage > const & xMessage )
162 {
163 const uno::Sequence< mail::MailAttachment > aAttachments = xMessage->getAttachments();
164
165 for(const auto& rAttachment : aAttachments)
166 {
167 try
168 {
169 uno::Reference< beans::XPropertySet > xTransferableProperties( rAttachment.Data, uno::UNO_QUERY_THROW);
170 OUString sURL;
171 xTransferableProperties->getPropertyValue("URL") >>= sURL;
172 if(!sURL.isEmpty())
173 SWUnoHelper::UCB_DeleteFile( sURL );
174 }
175 catch (const uno::Exception&)
176 {
177 }
178 }
179 }
180
181 class SwSendWarningBox_Impl : public weld::MessageDialogController
182 {
183 std::unique_ptr<weld::TextView> m_xDetailED;
184 public:
SwSendWarningBox_Impl(weld::Window * pParent,const OUString & rDetails)185 SwSendWarningBox_Impl(weld::Window* pParent, const OUString& rDetails)
186 : MessageDialogController(pParent, "modules/swriter/ui/warnemaildialog.ui", "WarnEmailDialog", "grid")
187 , m_xDetailED(m_xBuilder->weld_text_view("errors"))
188 {
189 m_xDetailED->set_size_request(80 * m_xDetailED->get_approximate_digit_width(),
190 8 * m_xDetailED->get_text_height());
191 m_xDetailED->set_text(rDetails);
192 }
193 };
194
SwSendMailDialog(weld::Window * pParent,SwMailMergeConfigItem & rConfigItem)195 SwSendMailDialog::SwSendMailDialog(weld::Window *pParent, SwMailMergeConfigItem& rConfigItem)
196 : GenericDialogController(pParent, "modules/swriter/ui/mmsendmails.ui", "SendMailsDialog")
197 , m_sContinue(SwResId( ST_CONTINUE ))
198 , m_sSendingTo( SwResId(ST_SENDINGTO ))
199 , m_sCompleted( SwResId(ST_COMPLETED ))
200 , m_sFailed( SwResId(ST_FAILED ))
201 , m_bCancel(false)
202 , m_bDestructionEnabled(false)
203 , m_pImpl(new SwSendMailDialog_Impl)
204 , m_pConfigItem(&rConfigItem)
205 , m_nExpectedCount(0)
206 , m_nSendCount(0)
207 , m_nErrorCount(0)
208 , m_xTransferStatus(m_xBuilder->weld_label("transferstatus"))
209 , m_xPaused(m_xBuilder->weld_label("paused"))
210 , m_xProgressBar(m_xBuilder->weld_progress_bar("progress"))
211 , m_xErrorStatus(m_xBuilder->weld_label("errorstatus"))
212 , m_xStatus(m_xBuilder->weld_tree_view("container"))
213 , m_xStop(m_xBuilder->weld_button("stop"))
214 , m_xClose(m_xBuilder->weld_button("cancel"))
215 , m_xExpander(m_xBuilder->weld_expander("details"))
216 {
217 m_sStop = m_xStop->get_label();
218 m_sTransferStatus = m_xTransferStatus->get_label();
219 m_sErrorStatus = m_xErrorStatus->get_label();
220
221 Size aSize(m_xStatus->get_approximate_digit_width() * 28,
222 m_xStatus->get_height_rows(20));
223 m_xStatus->set_size_request(aSize.Width(), aSize.Height());
224
225 m_xStop->connect_clicked(LINK( this, SwSendMailDialog, StopHdl_Impl));
226 m_xClose->connect_clicked(LINK( this, SwSendMailDialog, CloseHdl_Impl));
227
228 std::vector<int> aWidths;
229 aWidths.push_back(m_xStatus->get_checkbox_column_width());
230 aWidths.push_back(aSize.Width()/3 * 2);
231 m_xStatus->set_column_fixed_widths(aWidths);
232
233 m_xPaused->set_visible(false);
234 UpdateTransferStatus();
235 }
236
~SwSendMailDialog()237 SwSendMailDialog::~SwSendMailDialog()
238 {
239 if(m_pImpl->xMailDispatcher.is())
240 {
241 try
242 {
243 if(m_pImpl->xMailDispatcher->isStarted())
244 m_pImpl->xMailDispatcher->stop();
245 if(m_pImpl->xConnectedInMailService.is() && m_pImpl->xConnectedInMailService->isConnected())
246 m_pImpl->xConnectedInMailService->disconnect();
247
248 uno::Reference<mail::XMailMessage> xMessage =
249 m_pImpl->xMailDispatcher->dequeueMailMessage();
250 while(xMessage.is())
251 {
252 SwMailDispatcherListener_Impl::DeleteAttachments( xMessage );
253 xMessage = m_pImpl->xMailDispatcher->dequeueMailMessage();
254 }
255 }
256 catch (const uno::Exception&)
257 {
258 }
259 }
260 }
261
AddDocument(SwMailDescriptor const & rDesc)262 void SwSendMailDialog::AddDocument( SwMailDescriptor const & rDesc )
263 {
264 ::osl::MutexGuard aGuard(m_pImpl->aDescriptorMutex);
265 m_pImpl->aDescriptors.push_back(rDesc);
266 // if the dialog is already running then continue sending of documents
267 if(m_pImpl->xMailDispatcher.is())
268 {
269 IterateMails();
270 }
271 }
272
IMPL_LINK(SwSendMailDialog,StopHdl_Impl,weld::Button &,rButton,void)273 IMPL_LINK( SwSendMailDialog, StopHdl_Impl, weld::Button&, rButton, void )
274 {
275 m_bCancel = true;
276 if(m_pImpl->xMailDispatcher.is())
277 {
278 if(m_pImpl->xMailDispatcher->isStarted())
279 {
280 m_pImpl->xMailDispatcher->stop();
281 rButton.set_label(m_sContinue);
282 m_xPaused->show();
283 }
284 else
285 {
286 m_pImpl->xMailDispatcher->start();
287 rButton.set_label(m_sStop);
288 m_xPaused->hide();
289 }
290 }
291 }
292
IMPL_LINK_NOARG(SwSendMailDialog,CloseHdl_Impl,weld::Button &,void)293 IMPL_LINK_NOARG(SwSendMailDialog, CloseHdl_Impl, weld::Button&, void)
294 {
295 m_xDialog->hide();
296
297 if (m_bDestructionEnabled)
298 m_xDialog->response(RET_CANCEL);
299 else
300 {
301 m_pImpl->aRemoveIdle.SetInvokeHandler( LINK( this, SwSendMailDialog, RemoveThis ) );
302 m_pImpl->aRemoveIdle.Start();
303 }
304 }
305
IMPL_STATIC_LINK(SwSendMailDialog,StartSendMails,void *,pDialog,void)306 IMPL_STATIC_LINK( SwSendMailDialog, StartSendMails, void*, pDialog, void )
307 {
308 static_cast<SwSendMailDialog*>(pDialog)->SendMails();
309 }
310
IMPL_LINK(SwSendMailDialog,RemoveThis,Timer *,pTimer,void)311 IMPL_LINK( SwSendMailDialog, RemoveThis, Timer*, pTimer, void )
312 {
313 if( m_pImpl->xMailDispatcher.is() )
314 {
315 if(m_pImpl->xMailDispatcher->isStarted())
316 m_pImpl->xMailDispatcher->stop();
317 if(!m_pImpl->xMailDispatcher->isShutdownRequested())
318 m_pImpl->xMailDispatcher->shutdown();
319 }
320
321 if( m_bDestructionEnabled &&
322 (!m_pImpl->xMailDispatcher.is() ||
323 !m_pImpl->xMailDispatcher->isRunning()))
324 {
325 m_xDialog->response(RET_CANCEL);
326 }
327 else
328 {
329 pTimer->Start();
330 }
331 }
332
IMPL_STATIC_LINK(SwSendMailDialog,StopSendMails,void *,p,void)333 IMPL_STATIC_LINK( SwSendMailDialog, StopSendMails, void*, p, void )
334 {
335 SwSendMailDialog* pDialog = static_cast<SwSendMailDialog*>(p);
336 if(pDialog->m_pImpl->xMailDispatcher.is() &&
337 pDialog->m_pImpl->xMailDispatcher->isStarted())
338 {
339 pDialog->m_pImpl->xMailDispatcher->stop();
340 pDialog->m_xStop->set_label(pDialog->m_sContinue);
341 pDialog->m_xPaused->show();
342 }
343 }
344
SendMails()345 void SwSendMailDialog::SendMails()
346 {
347 if(!m_pConfigItem)
348 {
349 OSL_FAIL("config item not set");
350 return;
351 }
352 auto xWait(std::make_unique<weld::WaitObject>(m_xDialog.get()));
353 //get a mail server connection
354 uno::Reference< mail::XSmtpService > xSmtpServer =
355 SwMailMergeHelper::ConnectToSmtpServer( *m_pConfigItem,
356 m_pImpl->xConnectedInMailService,
357 OUString(), OUString(), m_xDialog.get());
358 bool bIsLoggedIn = xSmtpServer.is() && xSmtpServer->isConnected();
359 xWait.reset();
360 if(!bIsLoggedIn)
361 {
362 OSL_FAIL("create error message");
363 return;
364 }
365 m_pImpl->xMailDispatcher.set( new MailDispatcher(xSmtpServer));
366 IterateMails();
367 m_pImpl->xMailListener = new SwMailDispatcherListener_Impl(*this);
368 m_pImpl->xMailDispatcher->addListener(m_pImpl->xMailListener);
369 if(!m_bCancel)
370 {
371 m_pImpl->xMailDispatcher->start();
372 }
373 }
374
IterateMails()375 void SwSendMailDialog::IterateMails()
376 {
377 const SwMailDescriptor* pCurrentMailDescriptor = m_pImpl->GetNextDescriptor();
378 while( pCurrentMailDescriptor )
379 {
380 if (!SwMailMergeHelper::CheckMailAddress( pCurrentMailDescriptor->sEMail))
381 {
382 OUString sMessage = m_sSendingTo;
383 m_xStatus->append();
384 m_xStatus->set_image(m_nSendCount, RID_BMP_FORMULA_CANCEL, 0);
385 m_xStatus->set_text(m_nSendCount, sMessage.replaceFirst("%1", pCurrentMailDescriptor->sEMail), 1);
386 m_xStatus->set_text(m_nSendCount, m_sFailed, 1);
387 ++m_nSendCount;
388 ++m_nErrorCount;
389 UpdateTransferStatus( );
390 pCurrentMailDescriptor = m_pImpl->GetNextDescriptor();
391 continue;
392 }
393 SwMailMessage* pMessage = new SwMailMessage;
394 uno::Reference< mail::XMailMessage > xMessage = pMessage;
395 if(m_pConfigItem->IsMailReplyTo())
396 pMessage->setReplyToAddress(m_pConfigItem->GetMailReplyTo());
397 pMessage->addRecipient( pCurrentMailDescriptor->sEMail );
398 pMessage->SetSenderName( m_pConfigItem->GetMailDisplayName() );
399 pMessage->SetSenderAddress( m_pConfigItem->GetMailAddress() );
400 if(!pCurrentMailDescriptor->sAttachmentURL.isEmpty())
401 {
402 mail::MailAttachment aAttach;
403 aAttach.Data =
404 new SwMailTransferable(
405 pCurrentMailDescriptor->sAttachmentURL,
406 pCurrentMailDescriptor->sAttachmentName,
407 pCurrentMailDescriptor->sMimeType );
408 aAttach.ReadableName = pCurrentMailDescriptor->sAttachmentName;
409 pMessage->addAttachment( aAttach );
410 }
411 pMessage->setSubject( pCurrentMailDescriptor->sSubject );
412 uno::Reference< datatransfer::XTransferable> xBody =
413 new SwMailTransferable(
414 pCurrentMailDescriptor->sBodyContent,
415 pCurrentMailDescriptor->sBodyMimeType);
416 pMessage->setBody( xBody );
417
418 //CC and BCC are tokenized by ';'
419 if(!pCurrentMailDescriptor->sCC.isEmpty())
420 {
421 sal_Int32 nPos = 0;
422 do
423 {
424 OUString sTmp = pCurrentMailDescriptor->sCC.getToken( 0, ';', nPos );
425 if( !sTmp.isEmpty() )
426 pMessage->addCcRecipient( sTmp );
427 }
428 while (nPos >= 0);
429 }
430 if(!pCurrentMailDescriptor->sBCC.isEmpty())
431 {
432 sal_Int32 nPos = 0;
433 do
434 {
435 OUString sTmp = pCurrentMailDescriptor->sBCC.getToken( 0, ';', nPos );
436 if( !sTmp.isEmpty() )
437 pMessage->addBccRecipient( sTmp );
438 }
439 while (nPos >= 0);
440 }
441 m_pImpl->xMailDispatcher->enqueueMailMessage( xMessage );
442 pCurrentMailDescriptor = m_pImpl->GetNextDescriptor();
443 }
444 UpdateTransferStatus();
445 }
446
StartSend(sal_Int32 nExpectedCount)447 void SwSendMailDialog::StartSend(sal_Int32 nExpectedCount)
448 {
449 Application::PostUserEvent( LINK( this, SwSendMailDialog,
450 StartSendMails ), this );
451 m_nExpectedCount = nExpectedCount > 0 ? nExpectedCount : 1;
452 }
453
DocumentSent(uno::Reference<mail::XMailMessage> const & xMessage,bool bResult,const OUString * pError)454 void SwSendMailDialog::DocumentSent( uno::Reference< mail::XMailMessage> const & xMessage,
455 bool bResult,
456 const OUString* pError )
457 {
458 //sending should stop on send errors
459 if(pError &&
460 m_pImpl->xMailDispatcher.is() && m_pImpl->xMailDispatcher->isStarted())
461 {
462 Application::PostUserEvent( LINK( this, SwSendMailDialog,
463 StopSendMails ), this );
464 }
465 OUString sInsertImg(bResult ? OUString(RID_BMP_FORMULA_APPLY) : OUString(RID_BMP_FORMULA_CANCEL));
466
467 OUString sMessage = m_sSendingTo;
468 m_xStatus->append();
469 m_xStatus->set_image(m_nSendCount, sInsertImg, 0);
470 m_xStatus->set_text(m_nSendCount, sMessage.replaceFirst("%1", xMessage->getRecipients()[0]), 1);
471 m_xStatus->set_text(m_nSendCount, bResult ? m_sCompleted : m_sFailed, 1);
472 ++m_nSendCount;
473 if(!bResult)
474 ++m_nErrorCount;
475
476 UpdateTransferStatus( );
477
478 if (pError)
479 {
480 SwSendWarningBox_Impl aDlg(m_xDialog.get(), *pError);
481 aDlg.run();
482 }
483 }
484
UpdateTransferStatus()485 void SwSendMailDialog::UpdateTransferStatus()
486 {
487 OUString sStatus( m_sTransferStatus );
488 sStatus = sStatus.replaceFirst("%1", OUString::number(m_nSendCount) );
489 sStatus = sStatus.replaceFirst("%2", OUString::number(m_nExpectedCount));
490 m_xTransferStatus->set_label(sStatus);
491
492 sStatus = m_sErrorStatus.replaceFirst("%1", OUString::number(m_nErrorCount) );
493 m_xErrorStatus->set_label(sStatus);
494
495 if (!m_pImpl->aDescriptors.empty())
496 {
497 assert(m_nExpectedCount && "div-by-zero");
498 m_xProgressBar->set_percentage(m_nSendCount * 100 / m_nExpectedCount);
499 }
500 else
501 m_xProgressBar->set_percentage(0);
502 }
503
AllMailsSent()504 void SwSendMailDialog::AllMailsSent()
505 {
506 // Leave open if some kind of error occurred
507 if (m_nSendCount == m_nExpectedCount)
508 {
509 m_xStop->set_sensitive(false);
510 m_xDialog->hide();
511 m_xDialog->response(RET_CANCEL);
512 }
513 }
514
515 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
516