1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp>
21 #include <com/sun/star/xml/crypto/DigestID.hpp>
22 #include <com/sun/star/xml/crypto/CipherID.hpp>
23 #include <com/sun/star/beans/PropertyValue.hpp>
24 #include <com/sun/star/beans/NamedValue.hpp>
25 #include <com/sun/star/uno/RuntimeException.hpp>
27 #include "ManifestDefines.hxx"
28 #include "ManifestExport.hxx"
30 #include <osl/diagnose.h>
31 #include <rtl/ustrbuf.hxx>
32 #include <rtl/ref.hxx>
33 #include <sal/log.hxx>
34 #include <comphelper/base64.hxx>
35 #include <comphelper/documentconstants.hxx>
36 #include <comphelper/attributelist.hxx>
38 using namespace ::com::sun::star
;
40 #if OSL_DEBUG_LEVEL > 0
41 #define THROW_WHERE SAL_WHERE
43 #define THROW_WHERE ""
46 ManifestExport::ManifestExport( uno::Reference
< xml::sax::XDocumentHandler
> const & xHandler
, const uno::Sequence
< uno::Sequence
< beans::PropertyValue
> >& rManList
)
48 static const OUStringLiteral
sKeyInfo ( u
"KeyInfo" );
49 static const OUStringLiteral
sPgpKeyIDProperty ( u
"KeyId" );
50 static const OUStringLiteral
sPgpKeyPacketProperty ( u
"KeyPacket" );
51 static const OUStringLiteral
sCipherValueProperty ( u
"CipherValue" );
52 static const OUStringLiteral
sFullPathProperty ( u
"FullPath" );
53 static const OUStringLiteral
sVersionProperty ( u
"Version" );
54 static const OUStringLiteral
sMediaTypeProperty ( u
"MediaType" );
55 static const OUStringLiteral
sIterationCountProperty ( u
"IterationCount" );
56 static const OUStringLiteral
sDerivedKeySizeProperty ( u
"DerivedKeySize" );
57 static const OUStringLiteral
sSaltProperty ( u
"Salt" );
58 static const OUStringLiteral
sInitialisationVectorProperty( u
"InitialisationVector" );
59 static const OUStringLiteral
sSizeProperty ( u
"Size" );
60 static const OUStringLiteral
sDigestProperty ( u
"Digest" );
61 static const OUStringLiteral
sEncryptionAlgProperty ( u
"EncryptionAlgorithm" );
62 static const OUStringLiteral
sStartKeyAlgProperty ( u
"StartKeyAlgorithm" );
63 static const OUStringLiteral
sDigestAlgProperty ( u
"DigestAlgorithm" );
65 static const OUStringLiteral
sWhiteSpace ( u
" " );
67 const OUString
sSHA256_URL_ODF12 ( SHA256_URL_ODF12
);
68 const OUString
sSHA1_Name ( SHA1_NAME
);
70 const OUString
sSHA1_1k_Name ( SHA1_1K_NAME
);
71 const OUString
sSHA256_1k_URL ( SHA256_1K_URL
);
73 const OUString
sBlowfish_Name ( BLOWFISH_NAME
);
74 const OUString
sAES256_URL ( AES256_URL
);
76 const OUString
sPBKDF2_Name ( PBKDF2_NAME
);
77 const OUString
sPGP_Name ( PGP_NAME
);
79 rtl::Reference
<::comphelper::AttributeList
> pRootAttrList
= new ::comphelper::AttributeList
;
81 // find the mediatype of the document if any
82 OUString aDocMediaType
;
84 const uno::Sequence
<beans::PropertyValue
>* pRootFolderPropSeq
= nullptr;
85 for (const uno::Sequence
< beans::PropertyValue
>& rSequence
: rManList
)
91 for (const beans::PropertyValue
& rValue
: rSequence
)
93 if (rValue
.Name
== sMediaTypeProperty
)
95 rValue
.Value
>>= aMediaType
;
97 else if (rValue
.Name
== sFullPathProperty
)
99 rValue
.Value
>>= aPath
;
101 else if (rValue
.Name
== sVersionProperty
)
103 rValue
.Value
>>= aVersion
;
106 if ( !aPath
.isEmpty() && !aMediaType
.isEmpty() && !aVersion
.isEmpty() )
112 aDocMediaType
= aMediaType
;
113 aDocVersion
= aVersion
;
114 pRootFolderPropSeq
= &rSequence
;
119 bool bProvideDTD
= false;
120 bool bAcceptNonemptyVersion
= false;
121 bool bStoreStartKeyGeneration
= false;
122 if ( !aDocMediaType
.isEmpty() )
124 if ( aDocMediaType
== MIMETYPE_OASIS_OPENDOCUMENT_TEXT_ASCII
125 || aDocMediaType
== MIMETYPE_OASIS_OPENDOCUMENT_TEXT_WEB_ASCII
126 || aDocMediaType
== MIMETYPE_OASIS_OPENDOCUMENT_TEXT_GLOBAL_ASCII
127 || aDocMediaType
== MIMETYPE_OASIS_OPENDOCUMENT_DRAWING_ASCII
128 || aDocMediaType
== MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_ASCII
129 || aDocMediaType
== MIMETYPE_OASIS_OPENDOCUMENT_SPREADSHEET_ASCII
130 || aDocMediaType
== MIMETYPE_OASIS_OPENDOCUMENT_CHART_ASCII
131 || aDocMediaType
== MIMETYPE_OASIS_OPENDOCUMENT_DATABASE_ASCII
132 || aDocMediaType
== MIMETYPE_OASIS_OPENDOCUMENT_FORMULA_ASCII
133 || aDocMediaType
== MIMETYPE_OASIS_OPENDOCUMENT_TEXT_TEMPLATE_ASCII
134 || aDocMediaType
== MIMETYPE_OASIS_OPENDOCUMENT_TEXT_GLOBAL_TEMPLATE_ASCII
135 || aDocMediaType
== MIMETYPE_OASIS_OPENDOCUMENT_DRAWING_TEMPLATE_ASCII
136 || aDocMediaType
== MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_TEMPLATE_ASCII
137 || aDocMediaType
== MIMETYPE_OASIS_OPENDOCUMENT_SPREADSHEET_TEMPLATE_ASCII
138 || aDocMediaType
== MIMETYPE_OASIS_OPENDOCUMENT_CHART_TEMPLATE_ASCII
139 || aDocMediaType
== MIMETYPE_OASIS_OPENDOCUMENT_FORMULA_TEMPLATE_ASCII
)
143 pRootAttrList
->AddAttribute ( ATTRIBUTE_XMLNS
,
144 MANIFEST_OASIS_NAMESPACE
);
145 bAcceptNonemptyVersion
= true;
146 if ( aDocVersion
.compareTo( ODFVER_012_TEXT
) >= 0 )
148 // this is ODF12 or later generation, let encrypted
149 // streams contain start-key-generation entry
150 bStoreStartKeyGeneration
= true;
151 pRootAttrList
->AddAttribute ( ATTRIBUTE_VERSION
, aDocVersion
);
152 // plus gpg4libre extensions - loext NS for that
153 pRootAttrList
->AddAttribute ( ATTRIBUTE_XMLNS_LOEXT
,
154 MANIFEST_LOEXT_NAMESPACE
);
159 // even if it is no SO6 format the namespace must be specified
160 // thus SO6 format is used as default one
161 pRootAttrList
->AddAttribute ( ATTRIBUTE_XMLNS
,
162 MANIFEST_NAMESPACE
);
168 xHandler
->startDocument();
169 uno::Reference
< xml::sax::XExtendedDocumentHandler
> xExtHandler ( xHandler
, uno::UNO_QUERY
);
170 if ( xExtHandler
.is() && bProvideDTD
)
172 xExtHandler
->unknown ( MANIFEST_DOCTYPE
);
173 xHandler
->ignorableWhitespace ( sWhiteSpace
);
175 xHandler
->startElement( ELEMENT_MANIFEST
, pRootAttrList
);
177 const uno::Any
*pKeyInfoProperty
= nullptr;
178 if ( pRootFolderPropSeq
)
180 // do we have package-wide encryption info?
181 for (const beans::PropertyValue
& rValue
: *pRootFolderPropSeq
)
183 if (rValue
.Name
== sKeyInfo
)
184 pKeyInfoProperty
= &rValue
.Value
;
187 if ( pKeyInfoProperty
)
189 // yeah, so that goes directly below the manifest:manifest
191 OUStringBuffer aBuffer
;
193 xHandler
->ignorableWhitespace ( sWhiteSpace
);
195 // ==== manifest:keyinfo & children
196 bool const isODF13(aDocVersion
.compareTo(ODFVER_013_TEXT
) >= 0);
199 xHandler
->startElement(ELEMENT_MANIFEST_KEYINFO
, nullptr);
201 xHandler
->ignorableWhitespace ( sWhiteSpace
);
203 uno::Sequence
< uno::Sequence
< beans::NamedValue
> > aKeyInfoSequence
;
204 *pKeyInfoProperty
>>= aKeyInfoSequence
;
205 for (const uno::Sequence
<beans::NamedValue
>& rKeyInfoSequence
: std::as_const(aKeyInfoSequence
))
207 uno::Sequence
< sal_Int8
> aPgpKeyID
;
208 uno::Sequence
< sal_Int8
> aPgpKeyPacket
;
209 uno::Sequence
< sal_Int8
> aCipherValue
;
210 for (const beans::NamedValue
& rNValue
: rKeyInfoSequence
)
212 if (rNValue
.Name
== sPgpKeyIDProperty
)
213 rNValue
.Value
>>= aPgpKeyID
;
214 else if (rNValue
.Name
== sPgpKeyPacketProperty
)
215 rNValue
.Value
>>= aPgpKeyPacket
;
216 else if (rNValue
.Name
== sCipherValueProperty
)
217 rNValue
.Value
>>= aCipherValue
;
220 if (aPgpKeyID
.hasElements() && aCipherValue
.hasElements() )
222 // ==== manifest:encrypted-key & children - one for each recipient
223 xHandler
->startElement(isODF13
? OUString(ELEMENT_ENCRYPTEDKEY13
) : OUString(ELEMENT_ENCRYPTEDKEY
), nullptr);
224 xHandler
->ignorableWhitespace ( sWhiteSpace
);
226 rtl::Reference
<::comphelper::AttributeList
> pNewAttrList
= new ::comphelper::AttributeList
;
227 // TODO: the algorithm should rather be configurable
228 pNewAttrList
->AddAttribute(
229 isODF13
? OUString(ATTRIBUTE_ALGORITHM13
) : OUString(ATTRIBUTE_ALGORITHM
),
230 "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p" );
231 xHandler
->startElement(isODF13
? OUString(ELEMENT_ENCRYPTIONMETHOD13
) : OUString(ELEMENT_ENCRYPTIONMETHOD
), pNewAttrList
);
232 xHandler
->endElement(isODF13
? OUString(ELEMENT_ENCRYPTIONMETHOD13
) : OUString(ELEMENT_ENCRYPTIONMETHOD
));
233 xHandler
->ignorableWhitespace ( sWhiteSpace
);
235 // note: the mismatch here corresponds to ODF 1.3 cs01 schema
236 xHandler
->startElement(isODF13
? OUString(ELEMENT_MANIFEST13_KEYINFO
) : OUString(ELEMENT_MANIFEST_KEYINFO
), nullptr);
237 xHandler
->ignorableWhitespace ( sWhiteSpace
);
239 xHandler
->startElement(isODF13
? OUString(ELEMENT_PGPDATA13
) : OUString(ELEMENT_PGPDATA
), nullptr);
240 xHandler
->ignorableWhitespace ( sWhiteSpace
);
242 xHandler
->startElement(isODF13
? OUString(ELEMENT_PGPKEYID13
) : OUString(ELEMENT_PGPKEYID
), nullptr);
243 ::comphelper::Base64::encode(aBuffer
, aPgpKeyID
);
244 xHandler
->characters( aBuffer
.makeStringAndClear() );
245 xHandler
->endElement(isODF13
? OUString(ELEMENT_PGPKEYID13
) : OUString(ELEMENT_PGPKEYID
));
246 xHandler
->ignorableWhitespace ( sWhiteSpace
);
248 // key packet is optional
249 if (aPgpKeyPacket
.hasElements())
251 xHandler
->startElement(isODF13
? OUString(ELEMENT_PGPKEYPACKET13
) : OUString(ELEMENT_PGPKEYPACKET
), nullptr);
252 ::comphelper::Base64::encode(aBuffer
, aPgpKeyPacket
);
253 xHandler
->characters( aBuffer
.makeStringAndClear() );
254 xHandler
->endElement(isODF13
? OUString(ELEMENT_PGPKEYPACKET13
) : OUString(ELEMENT_PGPKEYPACKET
));
255 xHandler
->ignorableWhitespace ( sWhiteSpace
);
258 xHandler
->endElement(isODF13
? OUString(ELEMENT_PGPDATA13
) : OUString(ELEMENT_PGPDATA
));
259 xHandler
->ignorableWhitespace ( sWhiteSpace
);
261 xHandler
->endElement(isODF13
? OUString(ELEMENT_MANIFEST13_KEYINFO
) : OUString(ELEMENT_MANIFEST_KEYINFO
));
262 xHandler
->ignorableWhitespace ( sWhiteSpace
);
264 xHandler
->startElement(isODF13
? OUString(ELEMENT_CIPHERDATA13
) : OUString(ELEMENT_CIPHERDATA
), nullptr);
265 xHandler
->ignorableWhitespace ( sWhiteSpace
);
267 xHandler
->startElement(isODF13
? OUString(ELEMENT_CIPHERVALUE13
) : OUString(ELEMENT_CIPHERVALUE
), nullptr);
268 ::comphelper::Base64::encode(aBuffer
, aCipherValue
);
269 xHandler
->characters( aBuffer
.makeStringAndClear() );
270 xHandler
->endElement(isODF13
? OUString(ELEMENT_CIPHERVALUE13
) : OUString(ELEMENT_CIPHERVALUE
));
271 xHandler
->ignorableWhitespace ( sWhiteSpace
);
273 xHandler
->endElement(isODF13
? OUString(ELEMENT_CIPHERDATA13
) : OUString(ELEMENT_CIPHERDATA
));
274 xHandler
->ignorableWhitespace ( sWhiteSpace
);
276 xHandler
->endElement(isODF13
? OUString(ELEMENT_ENCRYPTEDKEY13
) : OUString(ELEMENT_ENCRYPTEDKEY
));
277 xHandler
->ignorableWhitespace ( sWhiteSpace
);
283 xHandler
->endElement(ELEMENT_MANIFEST_KEYINFO
);
285 xHandler
->ignorableWhitespace ( sWhiteSpace
);
289 // now write individual file entries
290 for (const uno::Sequence
<beans::PropertyValue
>& rSequence
: rManList
)
292 rtl::Reference
<::comphelper::AttributeList
> pAttrList
= new ::comphelper::AttributeList
;
294 const uno::Any
*pVector
= nullptr, *pSalt
= nullptr, *pIterationCount
= nullptr, *pDigest
= nullptr, *pDigestAlg
= nullptr, *pEncryptAlg
= nullptr, *pStartKeyAlg
= nullptr, *pDerivedKeySize
= nullptr;
295 for (const beans::PropertyValue
& rValue
: rSequence
)
297 if (rValue
.Name
== sMediaTypeProperty
)
299 rValue
.Value
>>= aString
;
300 pAttrList
->AddAttribute ( ATTRIBUTE_MEDIA_TYPE
, aString
);
302 else if (rValue
.Name
== sVersionProperty
)
304 rValue
.Value
>>= aString
;
305 // the version is stored only if it is not empty
306 if ( bAcceptNonemptyVersion
&& !aString
.isEmpty() )
307 pAttrList
->AddAttribute ( ATTRIBUTE_VERSION
, aString
);
309 else if (rValue
.Name
== sFullPathProperty
)
311 rValue
.Value
>>= aString
;
312 pAttrList
->AddAttribute ( ATTRIBUTE_FULL_PATH
, aString
);
314 else if (rValue
.Name
== sSizeProperty
)
317 rValue
.Value
>>= nSize
;
318 pAttrList
->AddAttribute ( ATTRIBUTE_SIZE
, OUString::number( nSize
) );
320 else if (rValue
.Name
== sInitialisationVectorProperty
)
321 pVector
= &rValue
.Value
;
322 else if (rValue
.Name
== sSaltProperty
)
323 pSalt
= &rValue
.Value
;
324 else if (rValue
.Name
== sIterationCountProperty
)
325 pIterationCount
= &rValue
.Value
;
326 else if (rValue
.Name
== sDigestProperty
)
327 pDigest
= &rValue
.Value
;
328 else if (rValue
.Name
== sDigestAlgProperty
)
329 pDigestAlg
= &rValue
.Value
;
330 else if (rValue
.Name
== sEncryptionAlgProperty
)
331 pEncryptAlg
= &rValue
.Value
;
332 else if (rValue
.Name
== sStartKeyAlgProperty
)
333 pStartKeyAlg
= &rValue
.Value
;
334 else if (rValue
.Name
== sDerivedKeySizeProperty
)
335 pDerivedKeySize
= &rValue
.Value
;
338 xHandler
->ignorableWhitespace ( sWhiteSpace
);
339 xHandler
->startElement( ELEMENT_FILE_ENTRY
, pAttrList
);
340 if ( pVector
&& pSalt
&& pIterationCount
&& pDigest
&& pDigestAlg
&& pEncryptAlg
&& pStartKeyAlg
&& pDerivedKeySize
)
342 // ==== Encryption Data
343 rtl::Reference
<::comphelper::AttributeList
> pNewAttrList
= new ::comphelper::AttributeList
;
344 OUStringBuffer aBuffer
;
345 uno::Sequence
< sal_Int8
> aSequence
;
347 xHandler
->ignorableWhitespace ( sWhiteSpace
);
350 OUString sChecksumType
;
351 sal_Int32 nDigestAlgID
= 0;
352 *pDigestAlg
>>= nDigestAlgID
;
353 if ( nDigestAlgID
== xml::crypto::DigestID::SHA256_1K
)
354 sChecksumType
= sSHA256_1k_URL
;
355 else if ( nDigestAlgID
== xml::crypto::DigestID::SHA1_1K
)
356 sChecksumType
= sSHA1_1k_Name
;
358 throw uno::RuntimeException( THROW_WHERE
"Unexpected digest algorithm is provided!" );
360 pNewAttrList
->AddAttribute ( ATTRIBUTE_CHECKSUM_TYPE
, sChecksumType
);
361 *pDigest
>>= aSequence
;
362 ::comphelper::Base64::encode(aBuffer
, aSequence
);
363 pNewAttrList
->AddAttribute ( ATTRIBUTE_CHECKSUM
, aBuffer
.makeStringAndClear() );
365 xHandler
->startElement( ELEMENT_ENCRYPTION_DATA
, pNewAttrList
);
368 pNewAttrList
= new ::comphelper::AttributeList
;
370 sal_Int32 nEncAlgID
= 0;
371 sal_Int32 nDerivedKeySize
= 0;
372 *pEncryptAlg
>>= nEncAlgID
;
373 *pDerivedKeySize
>>= nDerivedKeySize
;
375 OUString sEncAlgName
;
376 if ( nEncAlgID
== xml::crypto::CipherID::AES_CBC_W3C_PADDING
)
378 OSL_ENSURE( nDerivedKeySize
, "Unexpected key size is provided!" );
379 if ( nDerivedKeySize
!= 32 )
380 throw uno::RuntimeException( THROW_WHERE
"Unexpected key size is provided!" );
382 sEncAlgName
= sAES256_URL
;
384 else if ( nEncAlgID
== xml::crypto::CipherID::BLOWFISH_CFB_8
)
386 sEncAlgName
= sBlowfish_Name
;
389 throw uno::RuntimeException( THROW_WHERE
"Unexpected encryption algorithm is provided!" );
391 pNewAttrList
->AddAttribute ( ATTRIBUTE_ALGORITHM_NAME
, sEncAlgName
);
393 *pVector
>>= aSequence
;
394 ::comphelper::Base64::encode(aBuffer
, aSequence
);
395 pNewAttrList
->AddAttribute ( ATTRIBUTE_INITIALISATION_VECTOR
, aBuffer
.makeStringAndClear() );
397 xHandler
->ignorableWhitespace ( sWhiteSpace
);
398 xHandler
->startElement( ELEMENT_ALGORITHM
, pNewAttrList
);
399 xHandler
->ignorableWhitespace ( sWhiteSpace
);
400 xHandler
->endElement( ELEMENT_ALGORITHM
);
402 if ( bStoreStartKeyGeneration
)
404 // ==== Start Key Generation
405 pNewAttrList
= new ::comphelper::AttributeList
;
407 OUString sStartKeyAlg
;
408 OUString sStartKeySize
;
409 sal_Int32 nStartKeyAlgID
= 0;
410 *pStartKeyAlg
>>= nStartKeyAlgID
;
411 if ( nStartKeyAlgID
== xml::crypto::DigestID::SHA256
)
413 sStartKeyAlg
= sSHA256_URL_ODF12
; // TODO use SHA256_URL
414 aBuffer
.append( sal_Int32(32) );
415 sStartKeySize
= aBuffer
.makeStringAndClear();
417 else if ( nStartKeyAlgID
== xml::crypto::DigestID::SHA1
)
419 sStartKeyAlg
= sSHA1_Name
;
420 aBuffer
.append( sal_Int32(20) );
421 sStartKeySize
= aBuffer
.makeStringAndClear();
424 throw uno::RuntimeException( THROW_WHERE
"Unexpected start key algorithm is provided!" );
426 pNewAttrList
->AddAttribute ( ATTRIBUTE_START_KEY_GENERATION_NAME
, sStartKeyAlg
);
427 pNewAttrList
->AddAttribute ( ATTRIBUTE_KEY_SIZE
, sStartKeySize
);
429 xHandler
->ignorableWhitespace ( sWhiteSpace
);
430 xHandler
->startElement( ELEMENT_START_KEY_GENERATION
, pNewAttrList
);
431 xHandler
->ignorableWhitespace ( sWhiteSpace
);
432 xHandler
->endElement( ELEMENT_START_KEY_GENERATION
);
435 // ==== Key Derivation
436 pNewAttrList
= new ::comphelper::AttributeList
;
438 if (pKeyInfoProperty
)
440 pNewAttrList
->AddAttribute(ATTRIBUTE_KEY_DERIVATION_NAME
,
442 // no start-key-generation needed, our session key has
444 bStoreStartKeyGeneration
= false;
448 pNewAttrList
->AddAttribute(ATTRIBUTE_KEY_DERIVATION_NAME
,
451 if (bStoreStartKeyGeneration
)
453 aBuffer
.append(nDerivedKeySize
);
454 pNewAttrList
->AddAttribute ( ATTRIBUTE_KEY_SIZE
, aBuffer
.makeStringAndClear() );
457 sal_Int32 nCount
= 0;
458 *pIterationCount
>>= nCount
;
459 aBuffer
.append(nCount
);
460 pNewAttrList
->AddAttribute ( ATTRIBUTE_ITERATION_COUNT
, aBuffer
.makeStringAndClear() );
462 *pSalt
>>= aSequence
;
463 ::comphelper::Base64::encode(aBuffer
, aSequence
);
464 pNewAttrList
->AddAttribute ( ATTRIBUTE_SALT
, aBuffer
.makeStringAndClear() );
467 xHandler
->ignorableWhitespace(sWhiteSpace
);
468 xHandler
->startElement(ELEMENT_KEY_DERIVATION
, pNewAttrList
);
469 xHandler
->ignorableWhitespace(sWhiteSpace
);
470 xHandler
->endElement(ELEMENT_KEY_DERIVATION
);
472 xHandler
->ignorableWhitespace ( sWhiteSpace
);
473 xHandler
->endElement( ELEMENT_ENCRYPTION_DATA
);
475 xHandler
->ignorableWhitespace ( sWhiteSpace
);
476 xHandler
->endElement( ELEMENT_FILE_ENTRY
);
478 xHandler
->ignorableWhitespace ( sWhiteSpace
);
479 xHandler
->endElement( ELEMENT_MANIFEST
);
480 xHandler
->endDocument();
483 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */