LibreOffice Module sw (master)  1
mmoutputtypepage.cxx
Go to the documentation of this file.
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 <strings.hrc>
24 #include <bitmaps.hlst>
25 #include <swtypes.hxx>
26 
27 #include <rtl/ref.hxx>
28 #include <com/sun/star/mail/XSmtpService.hpp>
29 #include <vcl/idle.hxx>
30 #include <vcl/svapp.hxx>
31 #include <vcl/weld.hxx>
32 
33 #include <swunohelper.hxx>
34 #include <mmresultdialogs.hxx>
35 #include <maildispatcher.hxx>
36 #include <imaildsplistener.hxx>
37 #include <mutex>
38 
39 using namespace ::com::sun::star;
40 
42  : vcl::OWizardPage(pPage, pWizard, "modules/swriter/ui/mmoutputtypepage.ui", "MMOutputTypePage")
43  , m_pWizard(pWizard)
44  , m_xLetterRB(m_xBuilder->weld_radio_button("letter"))
45  , m_xMailRB(m_xBuilder->weld_radio_button("email"))
46  , m_xLetterHint(m_xBuilder->weld_label("letterft"))
47  , m_xMailHint(m_xBuilder->weld_label("emailft"))
48 {
49  Link<weld::Toggleable&,void> aLink = LINK(this, SwMailMergeOutputTypePage, TypeHdl_Impl);
50  m_xLetterRB->connect_toggled(aLink);
51  m_xMailRB->connect_toggled(aLink);
52 
54  if(rConfigItem.IsOutputToLetter())
55  m_xLetterRB->set_active(true);
56  else
57  m_xMailRB->set_active(true);
58  TypeHdl_Impl(*m_xLetterRB);
59 }
60 
62 {
63 }
64 
66 {
67  bool bLetter = m_xLetterRB->get_active();
68  m_xLetterHint->set_visible(bLetter);
69  m_xMailHint->set_visible(!bLetter);
70  m_pWizard->GetConfigItem().SetOutputToLetter(bLetter);
71  m_pWizard->UpdateRoadmap();
72 }
73 
75 {
76  friend class SwSendMailDialog;
77  // The mutex is locked in SwSendMailDialog_Impl::GetNextDescriptor, which may be called
78  // both with mutex unlocked (inside SwSendMailDialog::SendMails), and with mutex locked
79  // (inside SwSendMailDialog::AddDocument).
80  std::recursive_mutex aDescriptorMutex;
81 
82  std::vector< SwMailDescriptor > aDescriptors;
83  sal_uInt32 nCurrentDescriptor;
86  uno::Reference< mail::XMailService > xConnectedInMailService;
88 
90  nCurrentDescriptor(0), aRemoveIdle("SwSendMailDialog_Impl aRemoveIdle")
91  {
92  aRemoveIdle.SetPriority(TaskPriority::LOWEST);
93  }
94 
96  {
97  // Shutdown must be called when the last reference to the
98  // mail dispatcher will be released in order to force a
99  // shutdown of the mail dispatcher thread.
100  // 'join' with the mail dispatcher thread leads to a
101  // deadlock (SolarMutex).
102  if( xMailDispatcher.is() && !xMailDispatcher->isShutdownRequested() )
103  xMailDispatcher->shutdown();
104  }
106 };
107 
109 {
110  std::scoped_lock aGuard(aDescriptorMutex);
111  if(nCurrentDescriptor < aDescriptors.size())
112  {
114  return &aDescriptors[nCurrentDescriptor - 1];
115  }
116  return nullptr;
117 }
118 
119 namespace {
120 
121 class SwMailDispatcherListener_Impl : public IMailDispatcherListener
122 {
123  SwSendMailDialog& m_rSendMailDialog;
124 
125 public:
126  explicit SwMailDispatcherListener_Impl(SwSendMailDialog& rParentDlg);
127 
128  virtual void idle() override;
129  virtual void mailDelivered(uno::Reference< mail::XMailMessage> xMailMessage) override;
130  virtual void mailDeliveryError(::rtl::Reference<MailDispatcher> xMailDispatcher,
131  uno::Reference< mail::XMailMessage> xMailMessage, const OUString& sErrorMessage) override;
132 
133  static void DeleteAttachments( uno::Reference< mail::XMailMessage > const & xMessage );
134 };
135 
136 }
137 
138 SwMailDispatcherListener_Impl::SwMailDispatcherListener_Impl(SwSendMailDialog& rParentDlg)
139  : m_rSendMailDialog(rParentDlg)
140 {
141 }
142 
143 void SwMailDispatcherListener_Impl::idle()
144 {
145  SolarMutexGuard aGuard;
146  m_rSendMailDialog.AllMailsSent();
147 }
148 
149 void SwMailDispatcherListener_Impl::mailDelivered(
150  uno::Reference< mail::XMailMessage> xMailMessage)
151 {
152  SolarMutexGuard aGuard;
153  m_rSendMailDialog.DocumentSent( xMailMessage, true, nullptr );
154  DeleteAttachments( xMailMessage );
155 }
156 
157 void SwMailDispatcherListener_Impl::mailDeliveryError(
158  ::rtl::Reference<MailDispatcher> /*xMailDispatcher*/,
159  uno::Reference< mail::XMailMessage> xMailMessage,
160  const OUString& sErrorMessage)
161 {
162  SolarMutexGuard aGuard;
163  m_rSendMailDialog.DocumentSent( xMailMessage, false, &sErrorMessage );
164  DeleteAttachments( xMailMessage );
165 }
166 
167 void SwMailDispatcherListener_Impl::DeleteAttachments( uno::Reference< mail::XMailMessage > const & xMessage )
168 {
169  const uno::Sequence< mail::MailAttachment > aAttachments = xMessage->getAttachments();
170 
171  for(const auto& rAttachment : aAttachments)
172  {
173  try
174  {
175  uno::Reference< beans::XPropertySet > xTransferableProperties( rAttachment.Data, uno::UNO_QUERY_THROW);
176  OUString sURL;
177  xTransferableProperties->getPropertyValue("URL") >>= sURL;
178  if(!sURL.isEmpty())
180  }
181  catch (const uno::Exception&)
182  {
183  }
184  }
185 }
186 
187 namespace {
188 
189 class SwSendWarningBox_Impl : public weld::MessageDialogController
190 {
191  std::unique_ptr<weld::TextView> m_xDetailED;
192 public:
193  SwSendWarningBox_Impl(weld::Window* pParent, const OUString& rDetails)
194  : MessageDialogController(pParent, "modules/swriter/ui/warnemaildialog.ui", "WarnEmailDialog", "grid")
195  , m_xDetailED(m_xBuilder->weld_text_view("errors"))
196  {
197  m_xDetailED->set_size_request(80 * m_xDetailED->get_approximate_digit_width(),
198  8 * m_xDetailED->get_text_height());
199  m_xDetailED->set_text(rDetails);
200  }
201 };
202 
203 }
204 
206  : GenericDialogController(pParent, "modules/swriter/ui/mmsendmails.ui", "SendMailsDialog")
207  , m_sContinue(SwResId( ST_CONTINUE ))
208  , m_sClose(SwResId(ST_CLOSE_DIALOG))
209  , m_sSendingTo( SwResId(ST_SENDINGTO ))
210  , m_sCompleted( SwResId(ST_COMPLETED ))
211  , m_sFailed( SwResId(ST_FAILED ))
212  , m_sAddressInvalid(SwResId(ST_ADDRESS_INVALID))
213  , m_bCancel(false)
214  , m_bDestructionEnabled(false)
216  , m_pConfigItem(&rConfigItem)
217  , m_nExpectedCount(0)
218  , m_nProcessedCount(0)
219  , m_nErrorCount(0)
220  , m_xTransferStatus(m_xBuilder->weld_label("transferstatus"))
221  , m_xPaused(m_xBuilder->weld_label("paused"))
222  , m_xProgressBar(m_xBuilder->weld_progress_bar("progress"))
223  , m_xErrorStatus(m_xBuilder->weld_label("errorstatus"))
224  , m_xStatus(m_xBuilder->weld_tree_view("container"))
225  , m_xStop(m_xBuilder->weld_button("stop"))
226  , m_xCancel(m_xBuilder->weld_button("cancel"))
227  , m_xExpander(m_xBuilder->weld_expander("details"))
228 {
229  m_sStop = m_xStop->get_label();
230  m_sTransferStatus = m_xTransferStatus->get_label();
231  m_sErrorStatus = m_xErrorStatus->get_label();
232 
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());
236 
237  m_xStop->connect_clicked(LINK( this, SwSendMailDialog, StopHdl_Impl));
238  m_xCancel->connect_clicked(LINK( this, SwSendMailDialog, CancelHdl_Impl));
239 
240  std::vector<int> aWidths
241  {
242  o3tl::narrowing<int>(m_xStatus->get_checkbox_column_width()),
243  o3tl::narrowing<int>(aSize.Width()/3 * 2)
244  };
245  m_xStatus->set_column_fixed_widths(aWidths);
246 
247  m_xPaused->set_visible(false);
249 }
250 
252 {
253  if(!m_pImpl->xMailDispatcher.is())
254  return;
255 
256  try
257  {
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();
262 
263  uno::Reference<mail::XMailMessage> xMessage =
264  m_pImpl->xMailDispatcher->dequeueMailMessage();
265  while(xMessage.is())
266  {
267  SwMailDispatcherListener_Impl::DeleteAttachments( xMessage );
268  xMessage = m_pImpl->xMailDispatcher->dequeueMailMessage();
269  }
270  }
271  catch (const uno::Exception&)
272  {
273  }
274 }
275 
277 {
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())
282  {
283  IterateMails();
284  }
285 }
286 
287 IMPL_LINK( SwSendMailDialog, StopHdl_Impl, weld::Button&, rButton, void )
288 {
289  m_bCancel = true;
290  if(!m_pImpl->xMailDispatcher.is())
291  return;
292 
293  if(m_pImpl->xMailDispatcher->isStarted())
294  {
295  m_pImpl->xMailDispatcher->stop();
296  rButton.set_label(m_sContinue);
297  m_xPaused->show();
298  }
299  else
300  {
301  m_pImpl->xMailDispatcher->start();
302  rButton.set_label(m_sStop);
303  m_xPaused->hide();
304  }
305 }
306 
308 {
309  m_xDialog->hide();
310 
312  m_xDialog->response(RET_CANCEL);
313  else
314  {
315  m_pImpl->aRemoveIdle.SetInvokeHandler( LINK( this, SwSendMailDialog, RemoveThis ) );
316  m_pImpl->aRemoveIdle.Start();
317  }
318 }
319 
320 IMPL_STATIC_LINK( SwSendMailDialog, StartSendMails, void*, pDialog, void )
321 {
322  static_cast<SwSendMailDialog*>(pDialog)->SendMails();
323 }
324 
325 IMPL_LINK( SwSendMailDialog, RemoveThis, Timer*, pTimer, void )
326 {
327  if( m_pImpl->xMailDispatcher.is() )
328  {
329  if(m_pImpl->xMailDispatcher->isStarted())
330  m_pImpl->xMailDispatcher->stop();
331  if(!m_pImpl->xMailDispatcher->isShutdownRequested())
332  m_pImpl->xMailDispatcher->shutdown();
333  }
334 
335  if( m_bDestructionEnabled &&
336  (!m_pImpl->xMailDispatcher.is() ||
337  !m_pImpl->xMailDispatcher->isRunning()))
338  {
339  m_xDialog->response(RET_CANCEL);
340  }
341  else
342  {
343  pTimer->Start();
344  }
345 }
346 
347 IMPL_STATIC_LINK( SwSendMailDialog, StopSendMails, void*, p, void )
348 {
349  SwSendMailDialog* pDialog = static_cast<SwSendMailDialog*>(p);
350  if(pDialog->m_pImpl->xMailDispatcher.is() &&
351  pDialog->m_pImpl->xMailDispatcher->isStarted())
352  {
353  pDialog->m_pImpl->xMailDispatcher->stop();
354  pDialog->m_xStop->set_label(pDialog->m_sContinue);
355  pDialog->m_xPaused->show();
356  }
357 }
358 
360 {
361  if(!m_pConfigItem)
362  {
363  OSL_FAIL("config item not set");
364  return;
365  }
366  auto xWait(std::make_unique<weld::WaitObject>(m_xDialog.get()));
367  //get a mail server connection
368  uno::Reference< mail::XSmtpService > xSmtpServer =
370  m_pImpl->xConnectedInMailService,
371  OUString(), OUString(), m_xDialog.get());
372  bool bIsLoggedIn = xSmtpServer.is() && xSmtpServer->isConnected();
373  xWait.reset();
374  if(!bIsLoggedIn)
375  {
376  OSL_FAIL("create error message");
377  return;
378  }
379  m_pImpl->xMailDispatcher.set( new MailDispatcher(xSmtpServer));
380  IterateMails();
381  m_pImpl->xMailListener = new SwMailDispatcherListener_Impl(*this);
382  m_pImpl->xMailDispatcher->addListener(m_pImpl->xMailListener);
383  if(!m_bCancel)
384  {
385  m_pImpl->xMailDispatcher->start();
386  }
387 }
388 
390 {
391  const SwMailDescriptor* pCurrentMailDescriptor = m_pImpl->GetNextDescriptor();
392  while( pCurrentMailDescriptor )
393  {
394  if (!SwMailMergeHelper::CheckMailAddress( pCurrentMailDescriptor->sEMail))
395  {
396  OUString sMessage = m_sSendingTo;
397  m_xStatus->append();
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);
402  ++m_nErrorCount;
404  pCurrentMailDescriptor = m_pImpl->GetNextDescriptor();
405  continue;
406  }
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())
414  {
415  mail::MailAttachment aAttach;
416  aAttach.Data =
417  new SwMailTransferable(
418  pCurrentMailDescriptor->sAttachmentURL,
419  pCurrentMailDescriptor->sAttachmentName,
420  pCurrentMailDescriptor->sMimeType );
421  aAttach.ReadableName = pCurrentMailDescriptor->sAttachmentName;
422  pMessage->addAttachment( aAttach );
423  }
424  pMessage->setSubject( pCurrentMailDescriptor->sSubject );
425  uno::Reference< datatransfer::XTransferable> xBody =
426  new SwMailTransferable(
427  pCurrentMailDescriptor->sBodyContent,
428  pCurrentMailDescriptor->sBodyMimeType);
429  pMessage->setBody( xBody );
430 
431  //CC and BCC are tokenized by ';'
432  if(!pCurrentMailDescriptor->sCC.isEmpty())
433  {
434  sal_Int32 nPos = 0;
435  do
436  {
437  OUString sTmp = pCurrentMailDescriptor->sCC.getToken( 0, ';', nPos );
438  if( !sTmp.isEmpty() )
439  pMessage->addCcRecipient( sTmp );
440  }
441  while (nPos >= 0);
442  }
443  if(!pCurrentMailDescriptor->sBCC.isEmpty())
444  {
445  sal_Int32 nPos = 0;
446  do
447  {
448  OUString sTmp = pCurrentMailDescriptor->sBCC.getToken( 0, ';', nPos );
449  if( !sTmp.isEmpty() )
450  pMessage->addBccRecipient( sTmp );
451  }
452  while (nPos >= 0);
453  }
454  m_pImpl->xMailDispatcher->enqueueMailMessage( pMessage );
455  pCurrentMailDescriptor = m_pImpl->GetNextDescriptor();
456  }
458 }
459 
460 void SwSendMailDialog::StartSend(sal_Int32 nExpectedCount)
461 {
463  StartSendMails ), this );
464  m_nExpectedCount = nExpectedCount > 0 ? nExpectedCount : 1;
465 }
466 
467 void SwSendMailDialog::DocumentSent( uno::Reference< mail::XMailMessage> const & xMessage,
468  bool bResult,
469  const OUString* pError )
470 {
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())
474  {
476  StopSendMails ), this );
477  }
478  OUString sInsertImg(bResult ? OUString(RID_BMP_FORMULA_APPLY) : OUString(RID_BMP_FORMULA_CANCEL));
479 
480  OUString sMessage = m_sSendingTo;
481  m_xStatus->append();
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);
486  if(!bResult)
487  ++m_nErrorCount;
488 
490 
491  if (pError)
492  {
493  SwSendWarningBox_Impl aDlg(m_xDialog.get(), *pError);
494  aDlg.run();
495  }
496 }
497 
499 {
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);
504 
505  sStatus = m_sErrorStatus.replaceFirst("%1", OUString::number(m_nErrorCount) );
506  m_xErrorStatus->set_label(sStatus);
507 
508  if (!m_pImpl->aDescriptors.empty())
509  {
510  assert(m_nExpectedCount && "div-by-zero");
511  m_xProgressBar->set_percentage(m_nProcessedCount * 100 / m_nExpectedCount);
512  }
513  else
514  m_xProgressBar->set_percentage(0);
515 }
516 
518 {
520  {
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)
525  {
526  m_xDialog->hide();
527  m_xDialog->response(RET_CANCEL);
528  }
529  }
530 }
531 
532 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
std::shared_ptr< weld::Dialog > m_xDialog
OUString sMessage
std::unique_ptr< weld::TreeView > m_xStatus
std::unique_ptr< weld::Button > m_xStop
uno::Reference< mail::XSmtpService > ConnectToSmtpServer(SwMailMergeConfigItem const &rConfigItem, uno::Reference< mail::XMailService > &rxInMailService, const OUString &rInMailServerPassword, const OUString &rOutMailServerPassword, weld::Window *pDialogParentWindow)
SwMailMergeConfigItem & GetConfigItem()
OUString const & GetMailAddress() const
IMPL_LINK(SwSendMailDialog, StopHdl_Impl, weld::Button &, rButton, void)
std::unique_ptr< SwSendMailDialog_Impl > m_pImpl
sal_Int32 m_nProcessedCount
MailDispatcher listener interface.
static ImplSVEvent * PostUserEvent(const Link< void *, void > &rLink, void *pCaller=nullptr, bool bReferenceLink=false)
sal_Int32 m_nExpectedCount
bool UCB_DeleteFile(const OUString &rURL)
Definition: swunohelper.cxx:59
RET_CANCEL
std::recursive_mutex aDescriptorMutex
::rtl::Reference< IMailDispatcherListener > xMailListener
SwSendMailDialog(weld::Window *pParent, SwMailMergeConfigItem &)
OUString SwResId(TranslateId aId)
Definition: swmodule.cxx:164
SwMailMergeOutputTypePage(weld::Container *pPage, SwMailMergeWizard *pWizard)
virtual ~SwMailMergeOutputTypePage() override
std::unique_ptr< weld::Button > m_xCancel
std::vector< SwMailDescriptor > aDescriptors
void AddDocument(SwMailDescriptor const &rDesc)
void DocumentSent(css::uno::Reference< css::mail::XMailMessage > const &xMessage, bool bResult, const OUString *pError)
void StartSend(sal_Int32 nExpectedCount)
SwMailMergeConfigItem * m_pConfigItem
OUString const & GetMailReplyTo() const
std::unique_ptr< weld::RadioButton > m_xMailRB
const SwMailDescriptor * GetNextDescriptor()
IMPL_STATIC_LINK(SwSendMailDialog, StartSendMails, void *, pDialog, void)
SwMailMergeWizard * m_pWizard
OUString const & GetMailDisplayName() const
std::unique_ptr< weld::Label > m_xTransferStatus
std::unique_ptr< weld::Label > m_xPaused
void * p
IMPL_LINK_NOARG(SwMailMergeOutputTypePage, TypeHdl_Impl, weld::Toggleable &, void)
std::unique_ptr< weld::ProgressBar > m_xProgressBar
bool CheckMailAddress(const OUString &rMailAddress)
void SetPriority(TaskPriority ePriority)
std::unique_ptr< weld::RadioButton > m_xLetterRB
A MailDispatcher should be used for sending a bunch a mail messages asynchronously.
::std::unique_ptr< XmlIdRegistry_Impl > m_pImpl
bool IsMailReplyTo() const
::rtl::Reference< MailDispatcher > xMailDispatcher
virtual ~SwSendMailDialog() override
uno::Reference< mail::XMailService > xConnectedInMailService
sal_uInt16 nPos
std::unique_ptr< weld::Label > m_xErrorStatus
bool IsOutputToLetter() const
bool m_bDetectedRangeSegmentation false