Coverage Report

Created: 2026-02-14 09:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sfx2/source/dialog/mailmodel.cxx
Line
Count
Source
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 <com/sun/star/beans/PropertyValue.hpp>
21
#include <com/sun/star/beans/XPropertyAccess.hpp>
22
#include <com/sun/star/container/XContainerQuery.hpp>
23
#include <com/sun/star/document/XExporter.hpp>
24
#include <com/sun/star/embed/XStorage.hpp>
25
#include <com/sun/star/frame/XDispatchProvider.hpp>
26
#include <com/sun/star/frame/XDispatch.hpp>
27
#include <com/sun/star/frame/XStatusListener.hpp>
28
#include <com/sun/star/frame/XFrame.hpp>
29
#include <com/sun/star/frame/XModel.hpp>
30
#include <com/sun/star/frame/ModuleManager.hpp>
31
#include <com/sun/star/frame/XStorable.hpp>
32
#include <com/sun/star/io/IOException.hpp>
33
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
34
#include <com/sun/star/system/SimpleSystemMail.hpp>
35
#include <com/sun/star/system/SimpleCommandMail.hpp>
36
#include <com/sun/star/system/XSimpleMailClientSupplier.hpp>
37
#include <com/sun/star/system/SimpleMailClientFlags.hpp>
38
#include <com/sun/star/ucb/CommandAbortedException.hpp>
39
#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
40
#include <com/sun/star/uno/Reference.h>
41
#include <com/sun/star/util/URLTransformer.hpp>
42
#include <com/sun/star/util/XURLTransformer.hpp>
43
#include <com/sun/star/util/XModifiable.hpp>
44
#include <vcl/weld/Builder.hxx>
45
#include <vcl/weld/MessageDialog.hxx>
46
#include <vcl/weld/weld.hxx>
47
#include <osl/diagnose.h>
48
49
#include <sfx2/mailmodelapi.hxx>
50
#include <sfx2/sfxresid.hxx>
51
#include <sfx2/strings.hrc>
52
53
#include <unotools/tempfile.hxx>
54
#include <tools/urlobj.hxx>
55
#include <unotools/useroptions.hxx>
56
#include <comphelper/processfactory.hxx>
57
#include <comphelper/propertyvalue.hxx>
58
#include <comphelper/sequenceashashmap.hxx>
59
#include <comphelper/string.hxx>
60
#include <vcl/svapp.hxx>
61
#include <cppuhelper/implbase.hxx>
62
63
64
using namespace ::com::sun::star;
65
using namespace ::com::sun::star::beans;
66
using namespace ::com::sun::star::frame;
67
using namespace ::com::sun::star::io;
68
using namespace ::com::sun::star::lang;
69
using namespace ::com::sun::star::uno;
70
using namespace ::com::sun::star::util;
71
using namespace ::com::sun::star::system;
72
73
namespace {
74
75
// - class PrepareListener_Impl ------------------------------------------
76
class PrepareListener_Impl : public ::cppu::WeakImplHelper< css::frame::XStatusListener >
77
{
78
    bool m_bState;
79
public:
80
        PrepareListener_Impl();
81
82
        // css.frame.XStatusListener
83
        virtual void SAL_CALL statusChanged(const css::frame::FeatureStateEvent& aEvent) override;
84
85
        // css.lang.XEventListener
86
        virtual void SAL_CALL disposing(const css::lang::EventObject& aEvent) override;
87
88
0
        bool IsSet() const {return m_bState;}
89
};
90
91
}
92
93
PrepareListener_Impl::PrepareListener_Impl() :
94
0
    m_bState( false )
95
0
{
96
0
}
97
98
void PrepareListener_Impl::statusChanged(const css::frame::FeatureStateEvent& rEvent)
99
0
{
100
0
    if( rEvent.IsEnabled )
101
0
        rEvent.State >>= m_bState;
102
0
    else
103
0
        m_bState = false;
104
0
}
105
106
void PrepareListener_Impl::disposing(const css::lang::EventObject& /*rEvent*/)
107
0
{
108
0
}
109
110
// class SfxMailModel -----------------------------------------------
111
112
const char16_t   PDF_DOCUMENT_TYPE[]   = u"pdf_Portable_Document_Format";
113
114
SfxMailModel::SaveResult SfxMailModel::ShowFilterOptionsDialog(
115
    const uno::Reference< lang::XMultiServiceFactory >& xSMGR,
116
    const uno::Reference< frame::XModel >& xModel,
117
    const OUString& rFilterName,
118
    std::u16string_view rType,
119
    bool bModified,
120
    sal_Int32& rNumArgs,
121
    css::uno::Sequence< css::beans::PropertyValue >& rArgs )
122
0
{
123
0
    SaveResult eRet( SAVE_ERROR );
124
125
0
    try
126
0
    {
127
0
        uno::Sequence < beans::PropertyValue > aProps;
128
0
        css::uno::Reference< css::container::XNameAccess > xFilterCFG(
129
0
                    xSMGR->createInstance( u"com.sun.star.document.FilterFactory"_ustr ), uno::UNO_QUERY );
130
0
        css::uno::Reference< css::util::XModifiable > xModifiable( xModel, css::uno::UNO_QUERY );
131
132
0
        if ( !xFilterCFG.is() )
133
0
            return eRet;
134
135
0
        uno::Any aAny = xFilterCFG->getByName( rFilterName );
136
137
0
        if ( aAny >>= aProps )
138
0
        {
139
0
            for (const auto& rProp : aProps)
140
0
            {
141
0
                if( rProp.Name == "UIComponent" )
142
0
                {
143
0
                    OUString aServiceName;
144
0
                    rProp.Value >>= aServiceName;
145
0
                    if( !aServiceName.isEmpty() )
146
0
                    {
147
0
                        uno::Reference< ui::dialogs::XExecutableDialog > xFilterDialog(
148
0
                            xSMGR->createInstance( aServiceName ), uno::UNO_QUERY );
149
0
                        uno::Reference< beans::XPropertyAccess > xFilterProperties(
150
0
                            xFilterDialog, uno::UNO_QUERY );
151
152
0
                        if( xFilterDialog.is() && xFilterProperties.is() )
153
0
                        {
154
0
                            uno::Reference< document::XExporter > xExporter( xFilterDialog, uno::UNO_QUERY );
155
156
0
                            if ( rType == PDF_DOCUMENT_TYPE )
157
0
                            {
158
                                //add an internal property, used to tell the dialog we want to set a different
159
                                //string for the ok button
160
                                //used in filter/source/pdf/impdialog.cxx
161
0
                                uno::Sequence< beans::PropertyValue > aFilterDataValue{
162
0
                                    comphelper::makePropertyValue(u"_OkButtonString"_ustr,
163
0
                                                                  SfxResId(STR_PDF_EXPORT_SEND ))
164
0
                                };
165
166
                                //add to the filterdata property, the only one the PDF export filter dialog will care for
167
0
                                uno::Sequence< beans::PropertyValue > aPropsForDialog{
168
0
                                    comphelper::makePropertyValue(u"FilterData"_ustr, aFilterDataValue)
169
0
                                };
170
171
                                //when executing the dialog will merge the persistent FilterData properties
172
0
                                xFilterProperties->setPropertyValues( aPropsForDialog );
173
0
                            }
174
175
0
                            if( xExporter.is() )
176
0
                                xExporter->setSourceDocument( xModel );
177
178
0
                            if( xFilterDialog->execute() )
179
0
                            {
180
                                //get the filter data
181
0
                                const uno::Sequence< beans::PropertyValue > aPropsFromDialog = xFilterProperties->getPropertyValues();
182
183
                                //add them to the args
184
0
                                auto pProp = std::find_if(aPropsFromDialog.begin(), aPropsFromDialog.end(),
185
0
                                    [](const beans::PropertyValue& rDialogProp) { return rDialogProp.Name == "FilterData"; });
186
0
                                if (pProp != aPropsFromDialog.end())
187
0
                                {
188
                                    //found the filterdata, add to the storing argument
189
0
                                    rArgs.realloc( ++rNumArgs );
190
0
                                    auto pArgs = rArgs.getArray();
191
0
                                    pArgs[rNumArgs-1].Name = pProp->Name;
192
0
                                    pArgs[rNumArgs-1].Value = pProp->Value;
193
0
                                }
194
0
                                eRet = SAVE_SUCCESSFUL;
195
0
                            }
196
0
                            else
197
0
                            {
198
                                // cancel from dialog, then do not send
199
                                // If the model is not modified, it could be modified by the dispatch calls.
200
                                // Therefore set back to modified = false. This should not hurt if we call
201
                                // on a non-modified model.
202
0
                                if ( !bModified )
203
0
                                {
204
0
                                    try
205
0
                                    {
206
0
                                        xModifiable->setModified( false );
207
0
                                    }
208
0
                                    catch( css::beans::PropertyVetoException& )
209
0
                                    {
210
0
                                    }
211
0
                                }
212
0
                                eRet = SAVE_CANCELLED;
213
0
                            }
214
0
                        }
215
0
                        break;
216
0
                    }
217
0
                }
218
0
            }
219
0
        }
220
0
    }
221
0
    catch( css::uno::RuntimeException& )
222
0
    {
223
0
        throw;
224
0
    }
225
0
    catch( uno::Exception& )
226
0
    {
227
0
    }
228
229
0
    return eRet;
230
0
}
231
232
bool SfxMailModel::IsEmpty() const
233
0
{
234
0
    return maAttachedDocuments.empty();
235
0
}
236
237
SfxMailModel::SaveResult SfxMailModel::SaveDocumentAsFormat(
238
    const OUString& aSaveFileName,
239
    const css::uno::Reference< css::uno::XInterface >& xFrameOrModel,
240
    const OUString& rType,
241
    OUString& rFileNamePath )
242
0
{
243
0
    SaveResult  eRet( SAVE_ERROR );
244
0
    bool        bSendAsPDF = ( rType == PDF_DOCUMENT_TYPE );
245
246
0
    css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR  = ::comphelper::getProcessServiceFactory();
247
0
    const css::uno::Reference< css::uno::XComponentContext >& xContext  = ::comphelper::getProcessComponentContext();
248
0
    if (!xContext.is())
249
0
        return eRet;
250
251
0
    css::uno::Reference< css::frame::XModuleManager2 > xModuleManager( css::frame::ModuleManager::create(xContext) );
252
253
0
    OUString aModule;
254
0
    try
255
0
    {
256
0
         aModule = xModuleManager->identify( xFrameOrModel );
257
0
    }
258
0
    catch ( css::uno::RuntimeException& )
259
0
    {
260
0
        throw;
261
0
    }
262
0
    catch ( css::uno::Exception& )
263
0
    {
264
0
    }
265
266
0
    css::uno::Reference< css::frame::XFrame > xFrame( xFrameOrModel, css::uno::UNO_QUERY );
267
0
    css::uno::Reference< css::frame::XModel > xModel( xFrameOrModel, css::uno::UNO_QUERY );
268
0
    if ( xFrame.is() )
269
0
    {
270
0
        css::uno::Reference< css::frame::XController > xController = xFrame->getController();
271
0
        if ( xController.is() )
272
0
            xModel = xController->getModel();
273
0
    }
274
275
    // We need at least a valid module name and model reference
276
0
    if ( !aModule.isEmpty()  && xModel.is() )
277
0
    {
278
0
        bool bModified( false );
279
0
        bool bHasLocation( false );
280
0
        bool bStoreTo( false );
281
282
0
        css::uno::Reference< css::util::XModifiable > xModifiable( xModel, css::uno::UNO_QUERY );
283
0
        css::uno::Reference< css::frame::XStorable > xStorable( xModel, css::uno::UNO_QUERY );
284
285
0
        if ( xModifiable.is() )
286
0
            bModified = xModifiable->isModified();
287
0
        if ( xStorable.is() )
288
0
        {
289
0
            OUString aLocation = xStorable->getLocation();
290
0
            INetURLObject aFileObj( aLocation );
291
292
0
            bool bPrivateProtocol = ( aFileObj.GetProtocol() == INetProtocol::PrivSoffice );
293
294
0
            bHasLocation =  !aLocation.isEmpty() && !bPrivateProtocol;
295
0
            OSL_ASSERT( !bPrivateProtocol );
296
0
        }
297
0
        if ( !rType.isEmpty() )
298
0
            bStoreTo = true;
299
300
0
        if ( xStorable.is() )
301
0
        {
302
0
            OUString aFilterName;
303
0
            OUString aTypeName( rType );
304
0
            OUString aFileName;
305
0
            OUString aExtension;
306
307
0
            css::uno::Reference< css::container::XContainerQuery > xContainerQuery(
308
0
                xSMGR->createInstance( u"com.sun.star.document.FilterFactory"_ustr ),
309
0
                css::uno::UNO_QUERY );
310
311
0
            if ( bStoreTo )
312
0
            {
313
                // Retrieve filter from type
314
0
                css::uno::Sequence< css::beans::NamedValue > aQuery( bSendAsPDF ? 3 : 2 );
315
0
                auto pQuery = aQuery.getArray();
316
0
                pQuery[0].Name  = "Type";
317
0
                pQuery[0].Value <<= aTypeName;
318
0
                pQuery[1].Name  = "DocumentService";
319
0
                pQuery[1].Value <<= aModule;
320
0
                if( bSendAsPDF )
321
0
                {
322
                    // #i91419#
323
                    // FIXME: we want just an export filter. However currently we need
324
                    // exact flag value as detailed in the filter configuration to get it
325
                    // this seems to be a bug
326
                    // without flags we get an import filter here, which is also unwanted
327
0
                    pQuery[2].Name  = "Flags";
328
0
                    pQuery[2].Value <<= sal_Int32(0x80042); // SfxFilterFlags: EXPORT ALIEN 3RDPARTY
329
0
                }
330
331
0
                css::uno::Reference< css::container::XEnumeration > xEnumeration =
332
0
                    xContainerQuery->createSubSetEnumerationByProperties( aQuery );
333
334
0
                if ( xEnumeration->hasMoreElements() )
335
0
                {
336
0
                    ::comphelper::SequenceAsHashMap aFilterPropsHM( xEnumeration->nextElement() );
337
0
                    aFilterName = aFilterPropsHM.getUnpackedValueOrDefault(
338
0
                                                u"Name"_ustr,
339
0
                                                OUString() );
340
0
                }
341
342
0
                if ( bHasLocation )
343
0
                {
344
                    // Retrieve filter from media descriptor
345
0
                    ::comphelper::SequenceAsHashMap aMediaDescrPropsHM( xModel->getArgs() );
346
0
                    OUString aOrgFilterName = aMediaDescrPropsHM.getUnpackedValueOrDefault(
347
0
                                    u"FilterName"_ustr,
348
0
                                    OUString() );
349
0
                    if ( aOrgFilterName == aFilterName )
350
0
                    {
351
                        // We should save the document in the original format. Therefore this
352
                        // is not a storeTo operation. To support signing in this case, reset
353
                        // bStoreTo flag.
354
0
                        bStoreTo = false;
355
0
                    }
356
0
                }
357
0
            }
358
0
            else
359
0
            {
360
0
                if ( bHasLocation )
361
0
                {
362
                    // Retrieve filter from media descriptor
363
0
                    ::comphelper::SequenceAsHashMap aMediaDescrPropsHM( xModel->getArgs() );
364
0
                    aFilterName = aMediaDescrPropsHM.getUnpackedValueOrDefault(
365
0
                                    u"FilterName"_ustr,
366
0
                                    OUString() );
367
0
                }
368
369
0
                if ( !bHasLocation ||  aFilterName.isEmpty())
370
0
                {
371
                    // Retrieve the user defined default filter
372
0
                    try
373
0
                    {
374
0
                        ::comphelper::SequenceAsHashMap aFilterPropsHM( xModuleManager->getByName( aModule ) );
375
0
                        aFilterName = aFilterPropsHM.getUnpackedValueOrDefault(
376
0
                                                    u"ooSetupFactoryDefaultFilter"_ustr,
377
0
                                                    OUString() );
378
0
                        css::uno::Reference< css::container::XNameAccess > xNameAccess(
379
0
                            xContainerQuery, css::uno::UNO_QUERY );
380
0
                        if ( xNameAccess.is() )
381
0
                        {
382
0
                            ::comphelper::SequenceAsHashMap aFilterPropsHM2( xNameAccess->getByName( aFilterName ) );
383
0
                            aTypeName = aFilterPropsHM2.getUnpackedValueOrDefault(
384
0
                                                        u"Type"_ustr,
385
0
                                                        OUString() );
386
0
                        }
387
0
                    }
388
0
                    catch ( css::container::NoSuchElementException& )
389
0
                    {
390
0
                    }
391
0
                    catch ( css::beans::UnknownPropertyException& )
392
0
                    {
393
0
                    }
394
0
                }
395
0
            }
396
397
            // No filter found => error
398
            // No type and no location => error
399
0
            if (( aFilterName.isEmpty() ) ||
400
0
                ( aTypeName.isEmpty()  && !bHasLocation ))
401
0
                return eRet;
402
403
            // Determine file name and extension
404
0
            if ( bHasLocation && !bStoreTo )
405
0
            {
406
0
                INetURLObject aFileObj( xStorable->getLocation() );
407
0
                aExtension = aFileObj.getExtension();
408
0
            }
409
0
            else
410
0
            {
411
0
                css::uno::Reference< container::XNameAccess > xTypeDetection(
412
0
                    xSMGR->createInstance( u"com.sun.star.document.TypeDetection"_ustr ),
413
0
                    css::uno::UNO_QUERY );
414
415
416
0
                if ( xTypeDetection.is() )
417
0
                {
418
0
                    try
419
0
                    {
420
0
                        ::comphelper::SequenceAsHashMap aTypeNamePropsHM( xTypeDetection->getByName( aTypeName ) );
421
0
                        uno::Sequence< OUString > aExtensions = aTypeNamePropsHM.getUnpackedValueOrDefault(
422
0
                                                        u"Extensions"_ustr,
423
0
                                                        ::uno::Sequence< OUString >() );
424
0
                        if ( aExtensions.hasElements() )
425
0
                            aExtension = aExtensions[0];
426
0
                    }
427
0
                    catch ( css::container::NoSuchElementException& )
428
0
                    {
429
0
                    }
430
0
                }
431
0
            }
432
433
            // Use provided save file name. If empty determine file name
434
0
            aFileName = aSaveFileName;
435
0
            if ( aFileName.isEmpty() )
436
0
            {
437
0
                if ( !bHasLocation )
438
0
                {
439
                    // Create a noname file name with the correct extension
440
0
                    aFileName = "noname";
441
0
                }
442
0
                else
443
0
                {
444
                    // Determine file name from model
445
0
                    INetURLObject aFileObj( xStorable->getLocation() );
446
0
                    aFileName = aFileObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::NONE );
447
0
                }
448
0
            }
449
450
            // No file name => error
451
0
            if ( aFileName.isEmpty() )
452
0
                return eRet;
453
454
0
            OSL_ASSERT( !aFilterName.isEmpty() );
455
0
            OSL_ASSERT( !aFileName.isEmpty() );
456
457
            // Creates a temporary directory to store a predefined file into it.
458
            // This makes it possible to store the file for "send document as e-mail"
459
            // with the original file name. We cannot use the original file as
460
            // some mail programs need exclusive access.
461
0
            INetURLObject aFilePathObj( ::utl::CreateTempURL(nullptr, true) );
462
0
            aFilePathObj.insertName( aFileName );
463
0
            aFilePathObj.setExtension( aExtension );
464
465
0
            OUString aFileURL = aFilePathObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
466
467
0
            sal_Int32 nNumArgs(1);
468
0
            static constexpr OUString aPasswordPropName( u"Password"_ustr );
469
0
            css::uno::Sequence< css::beans::PropertyValue > aArgs{ comphelper::makePropertyValue(
470
0
                u"FilterName"_ustr, aFilterName) };
471
472
0
            ::comphelper::SequenceAsHashMap aMediaDescrPropsHM( xModel->getArgs() );
473
0
            OUString aPassword = aMediaDescrPropsHM.getUnpackedValueOrDefault(
474
0
                                            aPasswordPropName,
475
0
                                            OUString() );
476
0
            if ( !aPassword.isEmpty() )
477
0
            {
478
0
                aArgs.realloc( ++nNumArgs );
479
0
                auto pArgs = aArgs.getArray();
480
0
                pArgs[nNumArgs-1].Name = aPasswordPropName;
481
0
                pArgs[nNumArgs-1].Value <<= aPassword;
482
0
            }
483
484
0
            bool bNeedsPreparation = false;
485
0
            css::util::URL aPrepareURL;
486
0
            css::uno::Reference< css::frame::XDispatch > xPrepareDispatch;
487
0
            css::uno::Reference< css::frame::XDispatchProvider > xDispatchProvider( xFrame, css::uno::UNO_QUERY );
488
0
            css::uno::Reference< css::util::XURLTransformer > xURLTransformer( css::util::URLTransformer::create( xContext ) );
489
0
            if( !bSendAsPDF )
490
0
            {
491
0
                try
492
0
                {
493
                    // check if the document needs to be prepared for sending as mail (embedding of links, removal of invisible content)
494
495
0
                    aPrepareURL.Complete = ".uno:PrepareMailExport";
496
0
                    xURLTransformer->parseStrict( aPrepareURL );
497
498
0
                    if ( xDispatchProvider.is() )
499
0
                    {
500
0
                        xPrepareDispatch.set( xDispatchProvider->queryDispatch( aPrepareURL, OUString(), 0 ));
501
0
                        if ( xPrepareDispatch.is() )
502
0
                        {
503
0
                                rtl::Reference<PrepareListener_Impl> pPrepareListener = new PrepareListener_Impl;
504
0
                                xPrepareDispatch->addStatusListener( pPrepareListener, aPrepareURL );
505
0
                                bNeedsPreparation = pPrepareListener->IsSet();
506
0
                                xPrepareDispatch->removeStatusListener( pPrepareListener, aPrepareURL );
507
0
                        }
508
0
                    }
509
0
                }
510
0
                catch ( css::uno::RuntimeException& )
511
0
                {
512
0
                    throw;
513
0
                }
514
0
                catch ( css::uno::Exception& )
515
0
                {
516
0
                }
517
0
            }
518
519
0
            if ( bModified || !bHasLocation || bStoreTo || bNeedsPreparation )
520
0
            {
521
                // Document is modified, is newly created or should be stored in a special format
522
0
                try
523
0
                {
524
0
                    if( bNeedsPreparation && xPrepareDispatch.is() )
525
0
                    {
526
0
                        try
527
0
                        {
528
0
                            css::uno::Sequence< css::beans::PropertyValue > aDispatchArgs;
529
0
                            xPrepareDispatch->dispatch( aPrepareURL, aDispatchArgs );
530
0
                        }
531
0
                        catch ( css::uno::RuntimeException& )
532
0
                        {
533
0
                            throw;
534
0
                        }
535
0
                        catch ( css::uno::Exception& )
536
0
                        {
537
0
                        }
538
0
                    }
539
540
                    //check if this is the pdf output filter (i#64555)
541
0
                    if( bSendAsPDF )
542
0
                    {
543
0
                        SaveResult eShowPDFFilterDialog = ShowFilterOptionsDialog(
544
0
                                                            xSMGR, xModel, aFilterName, rType, bModified, nNumArgs, aArgs );
545
546
                        // don't continue on dialog cancel or error
547
0
                        if ( eShowPDFFilterDialog != SAVE_SUCCESSFUL )
548
0
                            return eShowPDFFilterDialog;
549
0
                    }
550
551
0
                    xStorable->storeToURL( aFileURL, aArgs );
552
0
                    rFileNamePath = aFileURL;
553
0
                    eRet = SAVE_SUCCESSFUL;
554
555
0
                    if( !bSendAsPDF )
556
0
                    {
557
0
                        css::util::URL aURL;
558
                        // #i30432# notify that export is finished - the Writer may want to restore removed content
559
0
                        aURL.Complete = ".uno:MailExportFinished";
560
0
                        xURLTransformer->parseStrict( aURL );
561
562
0
                        if ( xDispatchProvider.is() )
563
0
                        {
564
0
                            css::uno::Reference< css::frame::XDispatch > xDispatch(
565
0
                                xDispatchProvider->queryDispatch( aURL, OUString(), 0 ));
566
0
                            if ( xDispatch.is() )
567
0
                            {
568
0
                                try
569
0
                                {
570
0
                                    css::uno::Sequence< css::beans::PropertyValue > aDispatchArgs;
571
0
                                    xDispatch->dispatch( aURL, aDispatchArgs );
572
0
                                }
573
0
                                catch ( css::uno::RuntimeException& )
574
0
                                {
575
0
                                    throw;
576
0
                                }
577
0
                                catch ( css::uno::Exception& )
578
0
                                {
579
0
                                }
580
0
                            }
581
0
                        }
582
0
                    }
583
                    // If the model is not modified, it could be modified by the dispatch calls.
584
                    // Therefore set back to modified = false. This should not hurt if we call
585
                    // on a non-modified model.
586
0
                    if ( !bModified )
587
0
                    {
588
0
                        try
589
0
                        {
590
0
                            xModifiable->setModified( false );
591
0
                        }
592
0
                        catch( css::beans::PropertyVetoException& )
593
0
                        {
594
0
                        }
595
0
                    }
596
0
                }
597
0
                catch ( css::io::IOException& )
598
0
                {
599
0
                    eRet = SAVE_ERROR;
600
0
                }
601
0
            }
602
0
            else
603
0
            {
604
                // We need 1:1 copy of the document to preserve an added signature.
605
0
                aArgs.realloc( ++nNumArgs );
606
0
                auto pArgs = aArgs.getArray();
607
0
                pArgs[nNumArgs-1].Name = "CopyStreamIfPossible";
608
0
                pArgs[nNumArgs-1].Value <<= true;
609
610
0
                try
611
0
                {
612
0
                    xStorable->storeToURL( aFileURL, aArgs );
613
0
                    rFileNamePath = aFileURL;
614
0
                    eRet = SAVE_SUCCESSFUL;
615
0
                }
616
0
                catch ( css::io::IOException& )
617
0
                {
618
0
                    eRet = SAVE_ERROR;
619
0
                }
620
0
            }
621
0
        }
622
0
    }
623
624
0
    return eRet;
625
0
}
626
627
SfxMailModel::SfxMailModel()
628
0
{
629
0
}
630
631
SfxMailModel::~SfxMailModel()
632
0
{
633
0
}
634
635
void SfxMailModel::AddToAddress( const OUString& rAddress )
636
0
{
637
    // don't add an empty address
638
0
    if ( !rAddress.isEmpty() )
639
0
    {
640
0
        if ( !mpToList )
641
            // create the list
642
0
            mpToList.reset(new AddressList_Impl);
643
644
        // add address to list
645
0
        mpToList->push_back( rAddress );
646
0
    }
647
0
}
648
649
SfxMailModel::SendMailResult SfxMailModel::AttachDocument(
650
    const css::uno::Reference< css::uno::XInterface >& xFrameOrModel,
651
    const OUString& sAttachmentTitle )
652
0
{
653
0
    OUString sFileName;
654
655
0
    SaveResult eSaveResult = SaveDocumentAsFormat( sAttachmentTitle, xFrameOrModel, OUString()/*sDocumentType*/, sFileName );
656
0
    if ( eSaveResult == SAVE_SUCCESSFUL &&  !sFileName.isEmpty() )
657
0
        maAttachedDocuments.push_back(sFileName);
658
0
    return eSaveResult == SAVE_SUCCESSFUL ? SEND_MAIL_OK : SEND_MAIL_ERROR;
659
0
}
660
661
SfxMailModel::SendMailResult SfxMailModel::Send( const css::uno::Reference< css::frame::XFrame >& xFrame )
662
0
{
663
0
    OSL_ENSURE(!maAttachedDocuments.empty(),"No document added!");
664
0
    SendMailResult  eResult = SEND_MAIL_ERROR;
665
0
    if ( !maAttachedDocuments.empty() )
666
0
    {
667
0
        const css::uno::Reference < XComponentContext >& xContext = ::comphelper::getProcessComponentContext();
668
669
0
        css::uno::Reference< XSimpleMailClientSupplier >    xSimpleMailClientSupplier;
670
671
        // Prefer the SimpleSystemMail service if available
672
0
        try {
673
0
            xSimpleMailClientSupplier = SimpleSystemMail::create( xContext );
674
0
        }
675
0
        catch ( const uno::Exception & )
676
0
        {}
677
678
0
        if ( ! xSimpleMailClientSupplier.is() )
679
0
        {
680
0
            try {
681
0
                xSimpleMailClientSupplier = SimpleCommandMail::create( xContext );
682
0
            }
683
0
            catch ( const uno::Exception & )
684
0
            {}
685
0
        }
686
687
0
        if ( xSimpleMailClientSupplier.is() )
688
0
        {
689
0
            css::uno::Reference< XSimpleMailClient > xSimpleMailClient = xSimpleMailClientSupplier->querySimpleMailClient();
690
691
0
            if ( !xSimpleMailClient.is() )
692
0
            {
693
                // no mail client support => message box!
694
0
                return SEND_MAIL_ERROR;
695
0
            }
696
697
            // we have a simple mail client
698
0
            css::uno::Reference< XSimpleMailMessage > xSimpleMailMessage = xSimpleMailClient->createSimpleMailMessage();
699
0
            if ( xSimpleMailMessage.is() )
700
0
            {
701
0
                sal_Int32 nSendFlags = SimpleMailClientFlags::DEFAULTS;
702
0
                if ( maFromAddress.isEmpty() )
703
0
                {
704
                    // from address not set, try figure out users e-mail address
705
0
                    CreateFromAddress_Impl( maFromAddress );
706
0
                }
707
0
                xSimpleMailMessage->setOriginator( maFromAddress );
708
709
0
                size_t nToCount     = mpToList ? mpToList->size() : 0;
710
711
                // set recipient (only one) for this simple mail server!!
712
0
                if ( nToCount >= 1 )
713
0
                {
714
0
                    xSimpleMailMessage->setRecipient( mpToList->at( 0 ) );
715
0
                    nSendFlags = SimpleMailClientFlags::NO_USER_INTERFACE;
716
0
                }
717
718
                // all other recipient must be handled with CC recipients!
719
0
                if ( nToCount > 1 )
720
0
                {
721
0
                    Sequence< OUString >    aCcRecipientSeq( nToCount - 1 );
722
0
                    std::copy_n(std::next(mpToList->begin()), aCcRecipientSeq.getLength(),
723
0
                                aCcRecipientSeq.getArray());
724
0
                    xSimpleMailMessage->setCcRecipient( aCcRecipientSeq );
725
0
                }
726
727
0
                Sequence< OUString > aAttachmentSeq(maAttachedDocuments.data(),maAttachedDocuments.size());
728
729
0
                if ( xSimpleMailMessage->getSubject().isEmpty() ) {
730
0
                    INetURLObject url(
731
0
                        maAttachedDocuments[0], INetURLObject::EncodeMechanism::WasEncoded);
732
0
                    OUString subject(
733
0
                        url.getBase(
734
0
                            INetURLObject::LAST_SEGMENT, false,
735
0
                            INetURLObject::DecodeMechanism::WithCharset));
736
0
                    if (subject.isEmpty()) {
737
0
                        subject = maAttachedDocuments[0];
738
0
                    }
739
0
                    if ( maAttachedDocuments.size() > 1 )
740
0
                        subject += ", ...";
741
0
                    xSimpleMailMessage->setSubject( subject );
742
0
                }
743
0
                xSimpleMailMessage->setAttachement( aAttachmentSeq );
744
745
0
                bool bSend( false );
746
0
                try
747
0
                {
748
0
                    xSimpleMailClient->sendSimpleMailMessage( xSimpleMailMessage, nSendFlags );
749
0
                    bSend = true;
750
0
                }
751
0
                catch ( IllegalArgumentException& )
752
0
                {
753
0
                }
754
0
                catch ( Exception& )
755
0
                {
756
0
                }
757
758
0
                if ( !bSend )
759
0
                {
760
0
                    css::uno::Reference< css::awt::XWindow > xParentWindow = xFrame->getContainerWindow();
761
762
0
                    SolarMutexGuard aGuard;
763
764
0
                    std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(Application::GetFrameWeld(xParentWindow), u"sfx/ui/errorfindemaildialog.ui"_ustr));
765
0
                    std::unique_ptr<weld::MessageDialog> xBox(xBuilder->weld_message_dialog(u"ErrorFindEmailDialog"_ustr));
766
0
                    xBox->run();
767
0
                    eResult = SEND_MAIL_CANCELLED;
768
0
                }
769
0
                else
770
0
                    eResult = SEND_MAIL_OK;
771
0
            }
772
0
        }
773
0
    }
774
0
    else
775
0
        eResult = SEND_MAIL_CANCELLED;
776
777
0
    return eResult;
778
0
}
779
780
SfxMailModel::SendMailResult SfxMailModel::SaveAndSend( const css::uno::Reference< css::frame::XFrame >& xFrame, const OUString& rTypeName )
781
0
{
782
0
    SaveResult      eSaveResult;
783
0
    SendMailResult  eResult = SEND_MAIL_ERROR;
784
0
    OUString   aFileName;
785
786
0
    eSaveResult = SaveDocumentAsFormat( OUString(), xFrame, rTypeName, aFileName );
787
788
0
    if ( eSaveResult == SAVE_SUCCESSFUL )
789
0
    {
790
0
        maAttachedDocuments.push_back( aFileName );
791
0
        return Send( xFrame );
792
0
    }
793
0
    else if ( eSaveResult == SAVE_CANCELLED )
794
0
        eResult = SEND_MAIL_CANCELLED;
795
796
0
    return eResult;
797
0
}
798
799
// functions -------------------------------------------------------------
800
801
bool CreateFromAddress_Impl( OUString& rFrom )
802
803
/* [Description]
804
805
    This function tries to create a From-address with the help of IniManagers.
806
    For this the fields 'first name', 'Name' and 'Email' are read from the
807
    application-ini-data. If these fields are not set, FALSE is returned.
808
809
    [Return value]
810
811
    sal_True:       Address could be created.
812
    sal_False:      Address could not be created.
813
*/
814
815
0
{
816
0
    SvtUserOptions aUserCFG;
817
0
    OUString aName        = aUserCFG.GetLastName  ();
818
0
    OUString aFirstName   = aUserCFG.GetFirstName ();
819
0
    if ( !aFirstName.isEmpty() || !aName.isEmpty() )
820
0
    {
821
0
        if ( !aFirstName.isEmpty() )
822
0
        {
823
0
            rFrom = comphelper::string::strip(aFirstName, ' ');
824
825
0
            if ( !aName.isEmpty() )
826
0
                rFrom += " ";
827
0
        }
828
0
        rFrom += comphelper::string::strip(aName, ' ');
829
        // remove illegal characters
830
0
        rFrom = rFrom.replaceAll("<", "").replaceAll(">", "").replaceAll("@", "");
831
0
    }
832
0
    OUString aEmailName = aUserCFG.GetEmail();
833
834
    // remove illegal characters
835
0
    aEmailName = aEmailName.replaceAll("<", "").replaceAll(">", "");
836
837
0
    if ( !aEmailName.isEmpty() )
838
0
    {
839
0
        if ( !rFrom.isEmpty() )
840
0
            rFrom += " ";
841
0
        rFrom += "<" + comphelper::string::strip(aEmailName, ' ') + ">";
842
0
    }
843
0
    else
844
0
        rFrom.clear();
845
0
    return !rFrom.isEmpty();
846
0
}
847
848
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */