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