Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/comphelper/source/misc/storagehelper.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 <config_gpgme.h>
21
22
#include <com/sun/star/embed/ElementModes.hpp>
23
#include <com/sun/star/embed/XEncryptionProtectedStorage.hpp>
24
#include <com/sun/star/embed/XStorage.hpp>
25
#include <com/sun/star/embed/XTransactedObject.hpp>
26
#include <com/sun/star/embed/StorageFactory.hpp>
27
#include <com/sun/star/embed/FileSystemStorageFactory.hpp>
28
#include <com/sun/star/io/IOException.hpp>
29
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
30
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
31
#include <com/sun/star/ucb/SimpleFileAccess.hpp>
32
#include <com/sun/star/beans/XPropertySet.hpp>
33
#include <com/sun/star/beans/PropertyValue.hpp>
34
#include <com/sun/star/beans/NamedValue.hpp>
35
#include <com/sun/star/beans/IllegalTypeException.hpp>
36
#include <com/sun/star/security/DocumentDigitalSignatures.hpp>
37
#include <com/sun/star/security/XCertificate.hpp>
38
39
#include <vector>
40
41
#include <rtl/digest.h>
42
#include <rtl/random.h>
43
#include <sal/log.hxx>
44
45
#include <ucbhelper/content.hxx>
46
47
#include <comphelper/bytereader.hxx>
48
#include <comphelper/diagnose_ex.hxx>
49
#include <comphelper/fileformat.h>
50
#include <comphelper/hash.hxx>
51
#include <comphelper/processfactory.hxx>
52
#include <comphelper/documentconstants.hxx>
53
#include <comphelper/propertyvalue.hxx>
54
#include <comphelper/storagehelper.hxx>
55
#include <comphelper/sequence.hxx>
56
#include <comphelper/xmlsechelper.hxx>
57
#include <cppuhelper/exc_hlp.hxx>
58
#include <o3tl/string_view.hxx>
59
60
#if HAVE_FEATURE_GPGME
61
# include <context.h>
62
# include <encryptionresult.h>
63
# include <key.h>
64
# include <data.h>
65
#endif
66
67
using namespace ::com::sun::star;
68
69
namespace comphelper {
70
71
72
uno::Reference< lang::XSingleServiceFactory > OStorageHelper::GetStorageFactory(
73
                            const uno::Reference< uno::XComponentContext >& rxContext )
74
263k
{
75
263k
    uno::Reference< uno::XComponentContext> xContext = rxContext.is() ? rxContext : ::comphelper::getProcessComponentContext();
76
77
263k
    return embed::StorageFactory::create( xContext );
78
263k
}
79
80
81
uno::Reference< lang::XSingleServiceFactory > OStorageHelper::GetFileSystemStorageFactory(
82
                            const uno::Reference< uno::XComponentContext >& rxContext )
83
0
{
84
0
    return embed::FileSystemStorageFactory::create(rxContext);
85
0
}
86
87
88
uno::Reference< embed::XStorage > OStorageHelper::GetTemporaryStorage(
89
            const uno::Reference< uno::XComponentContext >& rxContext )
90
203k
{
91
203k
    uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstance(),
92
203k
                                                    uno::UNO_QUERY_THROW );
93
203k
    return xTempStorage;
94
203k
}
95
96
97
uno::Reference< embed::XStorage > OStorageHelper::GetStorageFromURL(
98
            const OUString& aURL,
99
            sal_Int32 nStorageMode,
100
            const uno::Reference< uno::XComponentContext >& rxContext )
101
0
{
102
0
    uno::Sequence< uno::Any > aArgs{ uno::Any(aURL), uno::Any(nStorageMode) };
103
0
    uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ),
104
0
                                                    uno::UNO_QUERY_THROW );
105
0
    return xTempStorage;
106
0
}
107
108
109
uno::Reference< embed::XStorage > OStorageHelper::GetStorageFromURL2(
110
            const OUString& aURL,
111
            sal_Int32 nStorageMode,
112
            const uno::Reference< uno::XComponentContext >& rxContext )
113
0
{
114
0
    uno::Sequence< uno::Any > aArgs{ uno::Any(aURL), uno::Any(nStorageMode) };
115
116
0
    uno::Reference< lang::XSingleServiceFactory > xFact;
117
0
    css::uno::Any anyEx;
118
0
    try {
119
0
        ::ucbhelper::Content aCntnt( aURL,
120
0
            uno::Reference< css::ucb::XCommandEnvironment > (),
121
0
            getProcessComponentContext() );
122
0
        if (aCntnt.isDocument()) {
123
0
            xFact = GetStorageFactory( rxContext );
124
0
        } else {
125
0
            xFact = GetFileSystemStorageFactory( rxContext );
126
0
        }
127
0
    } catch (uno::Exception &)
128
0
    {
129
0
        anyEx = cppu::getCaughtException();
130
0
    }
131
132
0
    if (!xFact.is())
133
0
    {
134
0
        if (anyEx.hasValue())
135
0
            throw css::lang::WrappedTargetRuntimeException( u""_ustr, nullptr, anyEx );
136
0
        else
137
0
            throw uno::RuntimeException();
138
0
    }
139
140
0
    uno::Reference< embed::XStorage > xTempStorage(
141
0
        xFact->createInstanceWithArguments( aArgs ), uno::UNO_QUERY_THROW );
142
0
    return xTempStorage;
143
0
}
144
145
146
uno::Reference< embed::XStorage > OStorageHelper::GetStorageFromInputStream(
147
            const uno::Reference < io::XInputStream >& xStream,
148
            const uno::Reference< uno::XComponentContext >& rxContext )
149
0
{
150
0
    uno::Sequence< uno::Any > aArgs{ uno::Any(xStream), uno::Any(embed::ElementModes::READ) };
151
0
    uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ),
152
0
                                                    uno::UNO_QUERY_THROW );
153
0
    return xTempStorage;
154
0
}
155
156
157
uno::Reference< embed::XStorage > OStorageHelper::GetStorageFromStream(
158
            const uno::Reference < io::XStream >& xStream,
159
            sal_Int32 nStorageMode,
160
            const uno::Reference< uno::XComponentContext >& rxContext )
161
0
{
162
0
    uno::Sequence< uno::Any > aArgs{ uno::Any(xStream), uno::Any(nStorageMode) };
163
0
    uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ),
164
0
                                                    uno::UNO_QUERY_THROW );
165
0
    return xTempStorage;
166
0
}
167
168
169
void OStorageHelper::CopyInputToOutput(
170
            const uno::Reference< io::XInputStream >& xInput,
171
            const uno::Reference< io::XOutputStream >& xOutput )
172
15.7k
{
173
15.7k
    static const sal_Int32 nConstBufferSize = 32000;
174
175
15.7k
    if (auto pByteReader = dynamic_cast< comphelper::ByteReader* >( xInput.get() ))
176
15.7k
    {
177
15.7k
        if (auto pByteWriter = dynamic_cast< comphelper::ByteWriter* >( xOutput.get() ))
178
15.7k
        {
179
15.7k
            sal_Int32 nRead;
180
15.7k
            sal_Int8 aTempBuf[ nConstBufferSize ];
181
15.7k
            do
182
16.0k
            {
183
16.0k
                nRead = pByteReader->readSomeBytes ( aTempBuf, nConstBufferSize );
184
16.0k
                pByteWriter->writeBytes ( aTempBuf, nRead );
185
16.0k
            }
186
16.0k
            while ( nRead == nConstBufferSize );
187
15.7k
            return;
188
15.7k
        }
189
15.7k
    }
190
191
0
    sal_Int32 nRead;
192
0
    uno::Sequence < sal_Int8 > aSequence ( nConstBufferSize );
193
0
    do
194
0
    {
195
0
        nRead = xInput->readBytes ( aSequence, nConstBufferSize );
196
0
        if ( nRead < nConstBufferSize )
197
0
            aSequence.realloc( nRead );
198
0
        xOutput->writeBytes ( aSequence );
199
0
    }
200
0
    while ( nRead == nConstBufferSize );
201
0
}
202
203
204
uno::Reference< io::XInputStream > OStorageHelper::GetInputStreamFromURL(
205
            const OUString& aURL,
206
            const uno::Reference< uno::XComponentContext >& context )
207
0
{
208
0
    uno::Reference< io::XInputStream > xInputStream = ucb::SimpleFileAccess::create(context)->openFileRead( aURL );
209
0
    if ( !xInputStream.is() )
210
0
        throw uno::RuntimeException();
211
212
0
    return xInputStream;
213
0
}
214
215
216
void OStorageHelper::SetCommonStorageEncryptionData(
217
            const uno::Reference< embed::XStorage >& xStorage,
218
            const uno::Sequence< beans::NamedValue >& aEncryptionData )
219
0
{
220
0
    uno::Reference< embed::XEncryptionProtectedStorage > xEncrSet( xStorage, uno::UNO_QUERY );
221
0
    if ( !xEncrSet.is() )
222
0
        throw io::IOException(u"no XEncryptionProtectedStorage"_ustr); // TODO
223
224
0
    if ( aEncryptionData.getLength() == 2 &&
225
0
         aEncryptionData[0].Name == "GpgInfos" &&
226
0
         aEncryptionData[1].Name == "EncryptionKey" )
227
0
    {
228
0
        xEncrSet->setGpgProperties(
229
0
            aEncryptionData[0].Value.get< uno::Sequence< uno::Sequence< beans::NamedValue > > >() );
230
0
        xEncrSet->setEncryptionData(
231
0
            aEncryptionData[1].Value.get< uno::Sequence< beans::NamedValue > >() );
232
0
    }
233
0
    else
234
0
        xEncrSet->setEncryptionData( aEncryptionData );
235
0
}
236
237
238
sal_Int32 OStorageHelper::GetXStorageFormat(
239
            const uno::Reference< embed::XStorage >& xStorage )
240
125k
{
241
125k
    uno::Reference< beans::XPropertySet > xStorProps( xStorage, uno::UNO_QUERY_THROW );
242
243
125k
    OUString aMediaType;
244
125k
    xStorProps->getPropertyValue(u"MediaType"_ustr) >>= aMediaType;
245
246
125k
    sal_Int32 nResult = 0;
247
248
    // TODO/LATER: the filter configuration could be used to detect it later, or better a special service
249
125k
    if (
250
125k
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_WRITER       ) ||
251
125k
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_WRITER_WEB   ) ||
252
125k
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_WRITER_GLOBAL) ||
253
125k
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_DRAW         ) ||
254
125k
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_IMPRESS      ) ||
255
125k
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_CALC         ) ||
256
125k
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_CHART        ) ||
257
125k
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_MATH         )
258
125k
       )
259
0
    {
260
0
        nResult = SOFFICE_FILEFORMAT_60;
261
0
    }
262
125k
    else if (
263
125k
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_TEXT        ) ||
264
95.7k
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_TEXT_WEB    ) ||
265
95.7k
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_TEXT_GLOBAL ) ||
266
95.7k
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_DRAWING     ) ||
267
95.7k
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION) ||
268
73.2k
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_SPREADSHEET ) ||
269
27.4k
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_CHART       ) ||
270
27.4k
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_FORMULA     ) ||
271
0
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_DATABASE    ) ||
272
0
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_REPORT      ) ||
273
0
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_REPORT_CHART) ||
274
0
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_TEXT_TEMPLATE        ) ||
275
0
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_TEXT_GLOBAL_TEMPLATE ) ||
276
0
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_DRAWING_TEMPLATE     ) ||
277
0
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_TEMPLATE) ||
278
0
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_SPREADSHEET_TEMPLATE ) ||
279
0
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_CHART_TEMPLATE       ) ||
280
0
        aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_FORMULA_TEMPLATE     )
281
125k
       )
282
125k
    {
283
125k
        nResult = SOFFICE_FILEFORMAT_8;
284
125k
    }
285
0
    else
286
0
    {
287
        // the mediatype is not known
288
0
        throw beans::IllegalTypeException("unknown media type '" + aMediaType + "'");
289
0
    }
290
291
125k
    return nResult;
292
125k
}
293
294
295
uno::Reference< embed::XStorage > OStorageHelper::GetStorageOfFormatFromURL(
296
            const OUString& aFormat,
297
            const OUString& aURL,
298
            sal_Int32 nStorageMode,
299
            const uno::Reference< uno::XComponentContext >& rxContext )
300
0
{
301
0
    uno::Sequence< beans::PropertyValue > aProps{ comphelper::makePropertyValue(u"StorageFormat"_ustr,
302
0
                                                                                aFormat) };
303
304
0
    uno::Sequence< uno::Any > aArgs{ uno::Any(aURL), uno::Any(nStorageMode), uno::Any(aProps) };
305
0
    uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ),
306
0
                                                    uno::UNO_QUERY_THROW );
307
0
    return xTempStorage;
308
0
}
309
310
311
uno::Reference< embed::XStorage > OStorageHelper::GetStorageOfFormatFromInputStream(
312
            const OUString& aFormat,
313
            const uno::Reference < io::XInputStream >& xStream,
314
            const uno::Reference< uno::XComponentContext >& rxContext,
315
            bool bRepairStorage )
316
59.8k
{
317
59.8k
    uno::Sequence< beans::PropertyValue > aProps( bRepairStorage ? 2 : 1 );
318
59.8k
    auto pProps = aProps.getArray();
319
59.8k
    pProps[0].Name = "StorageFormat";
320
59.8k
    pProps[0].Value <<= aFormat;
321
59.8k
    if ( bRepairStorage )
322
0
    {
323
0
        pProps[1].Name = "RepairPackage";
324
0
        pProps[1].Value <<= bRepairStorage;
325
0
    }
326
327
59.8k
    uno::Sequence< uno::Any > aArgs{ uno::Any(xStream), uno::Any(embed::ElementModes::READ), uno::Any(aProps) };
328
59.8k
    uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ),
329
59.8k
                                                    uno::UNO_QUERY_THROW );
330
59.8k
    return xTempStorage;
331
59.8k
}
332
333
334
uno::Reference< embed::XStorage > OStorageHelper::GetStorageOfFormatFromStream(
335
            const OUString& aFormat,
336
            const uno::Reference < io::XStream >& xStream,
337
            sal_Int32 nStorageMode,
338
            const uno::Reference< uno::XComponentContext >& rxContext,
339
            bool bRepairStorage )
340
0
{
341
0
    uno::Sequence< beans::PropertyValue > aProps( bRepairStorage ? 2 : 1 );
342
0
    auto pProps = aProps.getArray();
343
0
    pProps[0].Name = "StorageFormat";
344
0
    pProps[0].Value <<= aFormat;
345
0
    if ( bRepairStorage )
346
0
    {
347
0
        pProps[1].Name = "RepairPackage";
348
0
        pProps[1].Value <<= bRepairStorage;
349
0
    }
350
351
0
    uno::Sequence< uno::Any > aArgs{ uno::Any(xStream), uno::Any(nStorageMode), uno::Any(aProps) };
352
0
    uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ),
353
0
                                                    uno::UNO_QUERY_THROW );
354
0
    return xTempStorage;
355
0
}
356
357
358
uno::Sequence< beans::NamedValue > OStorageHelper::CreatePackageEncryptionData( std::u16string_view aPassword )
359
45
{
360
    // TODO/LATER: Should not the method be part of DocPasswordHelper?
361
45
    uno::Sequence< beans::NamedValue > aEncryptionData;
362
45
    if ( !aPassword.empty() )
363
45
    {
364
45
        sal_Int32 nSha1Ind = 0;
365
        // generate SHA256 start key
366
45
        try
367
45
        {
368
45
            OString aUTF8Password( OUStringToOString( aPassword, RTL_TEXTENCODING_UTF8 ) );
369
45
            std::vector<unsigned char> const hash(comphelper::Hash::calculateHash(
370
45
                aUTF8Password.getStr(), aUTF8Password.getLength(),
371
45
                comphelper::HashType::SHA256));
372
45
            uno::Sequence<sal_Int8> aDigest(reinterpret_cast<const sal_Int8*>(hash.data()), hash.size());
373
374
45
            ++nSha1Ind;
375
45
            aEncryptionData = { { PACKAGE_ENCRYPTIONDATA_SHA256UTF8, uno::Any(aDigest) } };
376
45
        }
377
45
        catch ( uno::Exception& )
378
45
        {
379
0
            TOOLS_WARN_EXCEPTION("comphelper", "Can not create SHA256 digest!" );
380
0
            throw; // tdf#159519 DO NOT RETURN SUCCESS
381
0
        }
382
383
        // MS_1252 encoding was used for SO60 document format password encoding,
384
        // this encoding supports only a minor subset of nonascii characters,
385
        // but for compatibility reasons it has to be used for old document formats
386
45
        aEncryptionData.realloc( nSha1Ind + 3 );
387
45
        auto pEncryptionData = aEncryptionData.getArray();
388
        // these are StarOffice not-quite-SHA1
389
45
        pEncryptionData[nSha1Ind].Name = PACKAGE_ENCRYPTIONDATA_SHA1UTF8;
390
45
        pEncryptionData[nSha1Ind + 1].Name = PACKAGE_ENCRYPTIONDATA_SHA1MS1252;
391
392
45
        rtl_TextEncoding const pEncoding[2] = { RTL_TEXTENCODING_UTF8, RTL_TEXTENCODING_MS_1252 };
393
394
135
        for ( sal_Int32 nInd = 0; nInd < 2; nInd++ )
395
90
        {
396
90
            OString aByteStrPass = OUStringToOString( aPassword, pEncoding[nInd] );
397
398
90
            sal_uInt8 pBuffer[RTL_DIGEST_LENGTH_SHA1];
399
90
            rtlDigestError nError = rtl_digest_SHA1( aByteStrPass.getStr(),
400
90
                                                    aByteStrPass.getLength(),
401
90
                                                    pBuffer,
402
90
                                                    RTL_DIGEST_LENGTH_SHA1 );
403
404
90
            if ( nError != rtl_Digest_E_None )
405
0
            {
406
0
                aEncryptionData.realloc( nSha1Ind );
407
0
                return aEncryptionData;
408
0
            }
409
410
90
            pEncryptionData[nSha1Ind+nInd].Value <<= uno::Sequence< sal_Int8 >( reinterpret_cast<sal_Int8*>(pBuffer), RTL_DIGEST_LENGTH_SHA1 );
411
90
        }
412
413
        // actual SHA1
414
45
        pEncryptionData[nSha1Ind + 2].Name = PACKAGE_ENCRYPTIONDATA_SHA1CORRECT;
415
45
        OString aByteStrPass = OUStringToOString(aPassword, RTL_TEXTENCODING_UTF8);
416
45
        std::vector<unsigned char> const sha1(::comphelper::Hash::calculateHash(
417
45
                aByteStrPass.getStr(), aByteStrPass.getLength(),
418
45
                ::comphelper::HashType::SHA1));
419
45
        pEncryptionData[nSha1Ind + 2].Value <<= uno::Sequence<sal_Int8>(
420
45
                reinterpret_cast<sal_Int8 const*>(sha1.data()), sha1.size());
421
45
    }
422
423
45
    return aEncryptionData;
424
45
}
425
426
uno::Sequence<beans::NamedValue>
427
OStorageHelper::CreateGpgPackageEncryptionData(const css::uno::Reference<css::awt::XWindow>&
428
#if HAVE_FEATURE_GPGME
429
                                                   xParentWindow
430
#endif
431
)
432
0
{
433
#if HAVE_FEATURE_GPGME
434
    // generate session key
435
    // --------------------
436
437
    // get 32 random chars out of it
438
    uno::Sequence < sal_Int8 > aVector(32);
439
    if (rtl_random_getBytes(nullptr, aVector.getArray(), aVector.getLength()) != rtl_Random_E_None)
440
    {
441
        throw uno::RuntimeException(u"rtl_random_getBytes failed"_ustr);
442
    }
443
444
    std::vector< uno::Sequence< beans::NamedValue > > aGpgEncryptions;
445
446
    uno::Reference< security::XDocumentDigitalSignatures > xSigner(
447
        // here none of the version-dependent methods are called
448
        security::DocumentDigitalSignatures::createDefault(
449
            comphelper::getProcessComponentContext()));
450
451
    xSigner->setParentWindow(xParentWindow);
452
453
    // fire up certificate chooser dialog - user can multi-select!
454
    const uno::Sequence< uno::Reference< security::XCertificate > > xSignCertificates=
455
        xSigner->chooseEncryptionCertificate(css::security::CertificateKind_OPENPGP);
456
457
    if (!xSignCertificates.hasElements())
458
        return uno::Sequence< beans::NamedValue >(); // user cancelled
459
460
    // generate one encrypted key entry for each recipient
461
    // ---------------------------------------------------
462
463
    std::unique_ptr<GpgME::Context> ctx;
464
    GpgME::Error err = GpgME::checkEngine(GpgME::OpenPGP);
465
    if (err)
466
        throw uno::RuntimeException(u"The GpgME library failed to initialize for the OpenPGP protocol."_ustr);
467
468
    bool bResetContext = true;
469
    for (const auto & cert : xSignCertificates)
470
    {
471
        if (bResetContext)
472
        {
473
            bResetContext = false;
474
            ctx.reset( GpgME::Context::createForProtocol(GpgME::OpenPGP) );
475
            if (ctx == nullptr)
476
                throw uno::RuntimeException(u"The GpgME library failed to initialize for the OpenPGP protocol."_ustr);
477
            ctx->setArmor(false);
478
        }
479
480
        OString aKeyID;
481
        if (cert.is())
482
        {
483
            aKeyID
484
                = OUStringToOString(comphelper::xmlsec::GetHexString(cert->getSHA1Thumbprint(), ""),
485
                                    RTL_TEXTENCODING_UTF8);
486
        }
487
488
        std::vector<GpgME::Key> keys{ ctx->key(aKeyID.getStr(), err, false) };
489
490
        // ctx is setup now, let's encrypt the lot!
491
        GpgME::Data plain(
492
            reinterpret_cast<const char*>(aVector.getConstArray()),
493
            size_t(aVector.getLength()), false);
494
        GpgME::Data cipher;
495
496
        GpgME::EncryptionResult crypt_res = ctx->encrypt(
497
            keys, plain,
498
            cipher, GpgME::Context::NoCompress);
499
500
        // tdf#160184 ask user if they want to trust an untrusted certificate
501
        // gpgme contexts uses the "auto" trust model by default which only
502
        // allows encrypting with keys that have their trust level set to
503
        // "Ultimate". The gpg command, however, gives the user the option
504
        // to encrypt with a certificate that has a lower trust level so
505
        // emulate that behavior by asking the user if they want to trust
506
        // the certificate for just this operation only.
507
        if (crypt_res.error().code() == GPG_ERR_UNUSABLE_PUBKEY)
508
        {
509
            if (xSigner->trustUntrustedCertificate(cert))
510
            {
511
                // Reset the trust model back to "auto" before processing
512
                // the next certificate
513
                bResetContext = true;
514
515
                ctx->setFlag("trust-model", "tofu+pgp");
516
                ctx->setFlag("tofu-default-policy", "unknown");
517
                crypt_res = ctx->encrypt(
518
                    keys, plain,
519
                    cipher, GpgME::Context::NoCompress);
520
            }
521
        }
522
523
        off_t result = cipher.seek(0,SEEK_SET);
524
        (void) result;
525
        assert(result == 0);
526
        int len=0, curr=0; char buf;
527
        while( (curr=cipher.read(&buf, 1)) )
528
            len += curr;
529
530
        if(crypt_res.error() || !len)
531
            throw lang::IllegalArgumentException(
532
                u"Not a suitable key, or failed to encrypt."_ustr,
533
                css::uno::Reference<css::uno::XInterface>(), -1);
534
535
        uno::Sequence < sal_Int8 > aCipherValue(len);
536
        result = cipher.seek(0,SEEK_SET);
537
        assert(result == 0);
538
        if( cipher.read(aCipherValue.getArray(), len) != len )
539
            throw uno::RuntimeException(u"The GpgME library failed to read the encrypted value."_ustr);
540
541
        SAL_INFO("comphelper.crypto", "Generated gpg crypto of length: " << len);
542
543
        uno::Sequence<sal_Int8> aKeyIdSequence
544
            = comphelper::arrayToSequence<sal_Int8>(aKeyID.getStr(), aKeyID.getLength() + 1);
545
        uno::Sequence<beans::NamedValue> aGpgEncryptionEntry{
546
            { u"KeyId"_ustr, uno::Any(aKeyIdSequence) },
547
            { u"KeyPacket"_ustr, uno::Any(aKeyIdSequence) },
548
            { u"CipherValue"_ustr, uno::Any(aCipherValue) }
549
        };
550
551
        aGpgEncryptions.push_back(aGpgEncryptionEntry);
552
    }
553
554
    uno::Sequence<beans::NamedValue> aEncryptionData
555
        = { { PACKAGE_ENCRYPTIONDATA_SHA256UTF8, uno::Any(aVector) } };
556
557
    uno::Sequence<beans::NamedValue> aContainer
558
        = { { u"GpgInfos"_ustr, uno::Any(comphelper::containerToSequence(aGpgEncryptions)) },
559
            { u"EncryptionKey"_ustr, uno::Any(aEncryptionData) } };
560
561
    return aContainer;
562
#else
563
0
    return uno::Sequence< beans::NamedValue >();
564
0
#endif
565
0
}
566
567
bool OStorageHelper::IsValidZipEntryFileName( std::u16string_view aName, bool bSlashAllowed )
568
1.42M
{
569
1.42M
    long nDots{0};
570
23.1M
    for ( size_t i = 0; i < aName.size(); i++ )
571
21.7M
    {
572
21.7M
        switch ( aName[i] )
573
21.7M
        {
574
1.32M
            case '.':
575
1.32M
                if (nDots != -1)
576
64.1k
                {
577
64.1k
                    ++nDots;
578
64.1k
                }
579
1.32M
                break;
580
6
            case '\\':
581
11
            case '?':
582
17
            case '<':
583
22
            case '>':
584
26
            case '\"':
585
31
            case '|':
586
36
            case ':':
587
36
                return false;
588
719k
            case '/':
589
719k
                if (!bSlashAllowed || nDots == 1 || nDots == 2 || i == 0)
590
5
                    return false;
591
719k
                nDots = 0;
592
719k
                break;
593
19.6M
            default:
594
19.6M
                nDots = -1;
595
19.6M
                if ( aName[i] < 32  || (aName[i] >= 0xD800 && aName[i] <= 0xDFFF) )
596
35
                    return false;
597
21.7M
        }
598
21.7M
    }
599
1.42M
    return nDots != 1 && nDots != 2;
600
1.42M
}
601
602
603
bool OStorageHelper::PathHasSegment( std::u16string_view aPath, std::u16string_view aSegment )
604
0
{
605
0
    bool bResult = false;
606
0
    const size_t nPathLen = aPath.size();
607
0
    const size_t nSegLen = aSegment.size();
608
609
0
    if ( !aSegment.empty() && nPathLen >= nSegLen )
610
0
    {
611
0
        OUString aEndSegment = OUString::Concat("/") + aSegment;
612
0
        OUString aInternalSegment = aEndSegment + "/";
613
614
0
        if ( aPath.find( aInternalSegment ) != std::u16string_view::npos )
615
0
            bResult = true;
616
617
0
        if ( !bResult && o3tl::starts_with(aPath, aSegment ) )
618
0
        {
619
0
            if ( nPathLen == nSegLen || aPath[nSegLen] == '/' )
620
0
                bResult = true;
621
0
        }
622
623
0
        if ( !bResult && nPathLen > nSegLen && aPath.substr( nPathLen - nSegLen - 1, nSegLen + 1 ) == aEndSegment )
624
0
            bResult = true;
625
0
    }
626
627
0
    return bResult;
628
0
}
629
630
class LifecycleProxy::Impl
631
    : public std::vector< uno::Reference< embed::XStorage > > {};
632
LifecycleProxy::LifecycleProxy()
633
24.3k
    : m_xBadness( new Impl ) { }
634
24.3k
LifecycleProxy::~LifecycleProxy() { }
635
636
void LifecycleProxy::commitStorages()
637
0
{
638
0
    std::for_each(m_xBadness->rbegin(), m_xBadness->rend(), // reverse order (outwards)
639
0
        [](Impl::reference rxItem) {
640
0
            uno::Reference<embed::XTransactedObject> const xTransaction(rxItem, uno::UNO_QUERY);
641
0
            if (xTransaction.is())
642
0
            {
643
0
                xTransaction->commit();
644
0
            }
645
0
        });
646
0
}
647
648
static void splitPath( std::vector<OUString> &rElems, std::u16string_view rPath )
649
0
{
650
0
    for (sal_Int32 i = 0; i >= 0;)
651
0
        rElems.push_back( OUString(o3tl::getToken(rPath, 0, '/', i )) );
652
0
}
653
654
static uno::Reference< embed::XStorage > LookupStorageAtPath(
655
        const uno::Reference< embed::XStorage > &xParentStorage,
656
        std::vector<OUString> &rElems, sal_uInt32 nOpenMode,
657
        LifecycleProxy const &rNastiness )
658
0
{
659
0
    uno::Reference< embed::XStorage > xStorage( xParentStorage );
660
0
    rNastiness.m_xBadness->push_back( xStorage );
661
0
    for( size_t i = 0; i < rElems.size() && xStorage.is(); i++ )
662
0
    {
663
0
        xStorage = xStorage->openStorageElement( rElems[i], nOpenMode );
664
0
        rNastiness.m_xBadness->push_back( xStorage );
665
0
    }
666
0
    return xStorage;
667
0
}
668
669
uno::Reference< embed::XStorage > OStorageHelper::GetStorageAtPath(
670
        const uno::Reference< embed::XStorage > &xStorage,
671
        std::u16string_view rPath, sal_uInt32 nOpenMode,
672
        LifecycleProxy const &rNastiness )
673
0
{
674
0
    std::vector<OUString> aElems;
675
0
    splitPath( aElems, rPath );
676
0
    return LookupStorageAtPath( xStorage, aElems, nOpenMode, rNastiness );
677
0
}
678
679
uno::Reference< io::XStream > OStorageHelper::GetStreamAtPath(
680
        const uno::Reference< embed::XStorage > &xParentStorage,
681
        std::u16string_view rPath, sal_uInt32 nOpenMode,
682
        LifecycleProxy const &rNastiness )
683
0
{
684
0
    std::vector<OUString> aElems;
685
0
    splitPath( aElems, rPath );
686
0
    OUString aName( aElems.back() );
687
0
    aElems.pop_back();
688
0
    sal_uInt32 nStorageMode = nOpenMode & ~embed::ElementModes::TRUNCATE;
689
0
    uno::Reference< embed::XStorage > xStorage(
690
0
        LookupStorageAtPath( xParentStorage, aElems, nStorageMode, rNastiness ),
691
0
        uno::UNO_SET_THROW );
692
0
    return xStorage->openStreamElement( aName, nOpenMode );
693
0
}
694
695
uno::Reference< io::XStream > OStorageHelper::GetStreamAtPackageURL(
696
        uno::Reference< embed::XStorage > const& xParentStorage,
697
        const OUString& rURL, sal_uInt32 const nOpenMode,
698
        LifecycleProxy const & rNastiness)
699
0
{
700
0
    std::u16string_view path;
701
0
    if (rURL.startsWithIgnoreAsciiCase("vnd.sun.star.Package:", &path))
702
0
    {
703
0
        return GetStreamAtPath(xParentStorage, path, nOpenMode, rNastiness);
704
0
    }
705
0
    return nullptr;
706
0
}
707
708
OUString OStorageHelper::GetODFVersionFromStorage(const uno::Reference<embed::XStorage>& xStorage)
709
0
{
710
0
    OUString aODFVersion;
711
0
    try
712
0
    {
713
0
        uno::Reference<beans::XPropertySet> xPropSet(xStorage, uno::UNO_QUERY);
714
0
        if (xPropSet)
715
0
            xPropSet->getPropertyValue(u"Version"_ustr) >>= aODFVersion;
716
0
    }
717
0
    catch (uno::Exception&)
718
0
    {
719
0
    }
720
0
    return aODFVersion;
721
0
}
722
}
723
724
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */