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/sheet/TableValidationVisibility.hpp>
22 #include <xicontent.hxx>
23 #include <sfx2/objsh.hxx>
24 #include <sfx2/docfile.hxx>
25 #include <tools/urlobj.hxx>
26 #include <sfx2/linkmgr.hxx>
27 #include <svl/itemset.hxx>
28 #include <scitems.hxx>
29 #include <editeng/eeitem.hxx>
30 #include <svl/intitem.hxx>
31 #include <svl/stritem.hxx>
32 #include <editeng/flditem.hxx>
33 #include <editeng/editobj.hxx>
34 #include <unotools/charclass.hxx>
35 #include <comphelper/configuration.hxx>
36 #include <stringutil.hxx>
37 #include <cellform.hxx>
38 #include <cellvalue.hxx>
39 #include <document.hxx>
40 #include <editutil.hxx>
41 #include <validat.hxx>
42 #include <patattr.hxx>
43 #include <docpool.hxx>
45 #include <rangenam.hxx>
46 #include <arealink.hxx>
47 #include <stlsheet.hxx>
48 #include <xlcontent.hxx>
49 #include <xlformula.hxx>
50 #include <xltracer.hxx>
51 #include <xistream.hxx>
52 #include <xihelper.hxx>
53 #include <xistyle.hxx>
54 #include <xiescher.hxx>
57 #include <excform.hxx>
58 #include <tabprotection.hxx>
59 #include <documentimport.hxx>
62 #include <oox/helper/helper.hxx>
63 #include <sal/log.hxx>
65 using ::com::sun::star::uno::Sequence
;
66 using ::std::unique_ptr
;
68 // Shared string table ========================================================
70 XclImpSst::XclImpSst( const XclImpRoot
& rRoot
) :
75 void XclImpSst::ReadSst( XclImpStream
& rStrm
)
78 sal_uInt32 nStrCount
= rStrm
.ReaduInt32();
79 auto nBytesAvailable
= rStrm
.GetRecLeft();
80 if (nStrCount
> nBytesAvailable
)
82 SAL_WARN("sc.filter", "xls claimed to have " << nStrCount
<< " strings, but only " << nBytesAvailable
<< " bytes available, truncating");
83 nStrCount
= nBytesAvailable
;
86 maStrings
.reserve(nStrCount
);
87 while( (nStrCount
> 0) && rStrm
.IsValid() )
90 aString
.Read( rStrm
);
91 maStrings
.push_back( aString
);
96 const XclImpString
* XclImpSst::GetString( sal_uInt32 nSstIndex
) const
98 return (nSstIndex
< maStrings
.size()) ? &maStrings
[ nSstIndex
] : nullptr;
101 // Hyperlinks =================================================================
105 /** Reads character array and stores it into rString.
106 @param nChars Number of following characters (not byte count!).
107 @param b16Bit true = 16-bit characters, false = 8-bit characters. */
108 void lclAppendString32( OUString
& rString
, XclImpStream
& rStrm
, sal_uInt32 nChars
, bool b16Bit
)
110 sal_uInt16 nReadChars
= ulimit_cast
< sal_uInt16
>( nChars
);
111 rString
+= rStrm
.ReadRawUniString( nReadChars
, b16Bit
);
112 // ignore remaining chars
113 std::size_t nIgnore
= nChars
- nReadChars
;
116 rStrm
.Ignore( nIgnore
);
119 /** Reads 32-bit string length and the character array and stores it into rString.
120 @param b16Bit true = 16-bit characters, false = 8-bit characters. */
121 void lclAppendString32( OUString
& rString
, XclImpStream
& rStrm
, bool b16Bit
)
123 lclAppendString32( rString
, rStrm
, rStrm
.ReaduInt32(), b16Bit
);
126 /** Reads 32-bit string length and ignores following 16-bit character array. */
127 void lclIgnoreString32( XclImpStream
& rStrm
)
129 sal_uInt32 nChars
= rStrm
.ReaduInt32();
131 rStrm
.Ignore( nChars
);
134 /** Converts a path to an absolute path.
135 @param rPath The source path. The resulting path is returned here.
136 @param nLevel Number of parent directories to add in front of the path. */
137 void lclGetAbsPath( OUString
& rPath
, sal_uInt16 nLevel
, const SfxObjectShell
* pDocShell
)
139 OUStringBuffer aTmpStr
;
142 aTmpStr
.append( "../" );
145 aTmpStr
.append( rPath
);
149 bool bWasAbs
= false;
150 rPath
= pDocShell
->GetMedium()->GetURLObject().smartRel2Abs( aTmpStr
.makeStringAndClear(), bWasAbs
).GetMainURL( INetURLObject::DecodeMechanism::NONE
);
151 // full path as stored in SvxURLField must be encoded
154 rPath
= aTmpStr
.makeStringAndClear();
157 /** Inserts the URL into a text cell. Does not modify value or formula cells. */
158 void lclInsertUrl( XclImpRoot
& rRoot
, const OUString
& rUrl
, SCCOL nScCol
, SCROW nScRow
, SCTAB nScTab
)
160 ScDocumentImport
& rDoc
= rRoot
.GetDocImport();
161 ScAddress
aScPos( nScCol
, nScRow
, nScTab
);
162 ScRefCellValue
aCell(rDoc
.getDoc(), aScPos
);
163 switch( aCell
.getType() )
165 // #i54261# hyperlinks in string cells
166 case CELLTYPE_STRING
:
169 ScInterpreterContext
& rContext
= rDoc
.getDoc().GetNonThreadedContext();
170 sal_uInt32 nNumFmt
= rDoc
.getDoc().GetNumberFormat(rContext
, aScPos
);
172 OUString aDisplText
= ScCellFormat::GetString(aCell
, nNumFmt
, &pColor
, &rContext
, rDoc
.getDoc());
173 if (aDisplText
.isEmpty())
176 ScEditEngineDefaulter
& rEE
= rRoot
.GetEditEngine();
177 SvxURLField
aUrlField( rUrl
, aDisplText
, SvxURLFormat::AppDefault
);
179 if( aCell
.getType() == CELLTYPE_EDIT
)
181 const EditTextObject
* pEditObj
= aCell
.getEditText();
182 rEE
.SetTextCurrentDefaults( *pEditObj
);
183 rEE
.QuickInsertField(SvxFieldItem(aUrlField
, EE_FEATURE_FIELD
), ESelection::All());
187 rEE
.SetTextCurrentDefaults( OUString() );
188 rEE
.QuickInsertField( SvxFieldItem( aUrlField
, EE_FEATURE_FIELD
), ESelection() );
189 if( const ScPatternAttr
* pPattern
= rDoc
.getDoc().GetPattern( aScPos
.Col(), aScPos
.Row(), nScTab
) )
191 SfxItemSet
aItemSet( rEE
.GetEmptyItemSet() );
192 pPattern
->FillEditItemSet( &aItemSet
);
193 rEE
.QuickSetAttribs(aItemSet
, ESelection::All());
197 // The cell will own the text object instance.
198 rDoc
.setEditCell(aScPos
, rEE
.CreateTextObject());
203 // Handle other cell types e.g. formulas ( and ? ) that have associated
205 // Ideally all hyperlinks should be treated as below. For the moment,
206 // given the current absence of ods support let's just handle what we
207 // previously didn't handle the new way.
208 // Unfortunately we won't be able to preserve such hyperlinks when
209 // saving to ods. Note: when we are able to save such hyperlinks to ods
210 // we should handle *all* imported hyperlinks as below ( e.g. as cell
211 // attribute ) for better interoperability.
213 SfxStringItem
aItem( ATTR_HYPERLINK
, rUrl
);
214 rDoc
.getDoc().ApplyAttr(nScCol
, nScRow
, nScTab
, aItem
);
222 void XclImpHyperlink::ReadHlink( XclImpStream
& rStrm
)
224 XclRange
aXclRange( ScAddress::UNINITIALIZED
);
226 // #i80006# Excel silently ignores invalid hi-byte of column index (TODO: everywhere?)
227 aXclRange
.maFirst
.mnCol
&= 0xFF;
228 aXclRange
.maLast
.mnCol
&= 0xFF;
229 OUString aString
= ReadEmbeddedData( rStrm
);
230 if ( !aString
.isEmpty() )
231 rStrm
.GetRoot().GetXFRangeBuffer().SetHyperlink( aXclRange
, aString
);
234 OUString
XclImpHyperlink::ReadEmbeddedData( XclImpStream
& rStrm
)
236 const XclImpRoot
& rRoot
= rStrm
.GetRoot();
237 SfxObjectShell
* pDocShell
= rRoot
.GetDocShell();
239 OSL_ENSURE_BIFF( rRoot
.GetBiff() == EXC_BIFF8
);
244 sal_uInt32 nFlags
= rStrm
.ReaduInt32();
246 OSL_ENSURE( aGuid
== XclTools::maGuidStdLink
, "XclImpHyperlink::ReadEmbeddedData - unknown header GUID" );
248 ::std::unique_ptr
< OUString
> xLongName
; // link / file name
249 ::std::unique_ptr
< OUString
> xShortName
; // 8.3-representation of file name
250 ::std::unique_ptr
< OUString
> xTextMark
; // text mark
252 // description (ignore)
253 if( ::get_flag( nFlags
, EXC_HLINK_DESCR
) )
254 lclIgnoreString32( rStrm
);
255 // target frame (ignore) !! DESCR/FRAME - is this the right order? (never seen them together)
256 if( ::get_flag( nFlags
, EXC_HLINK_FRAME
) )
257 lclIgnoreString32( rStrm
);
259 // URL fields are zero-terminated - do not let the stream replace them
260 // in the lclAppendString32() with the '?' character.
261 rStrm
.SetNulSubstChar( '\0' );
264 if( ::get_flag( nFlags
, EXC_HLINK_UNC
) )
266 xLongName
.reset( new OUString
);
267 lclAppendString32( *xLongName
, rStrm
, true );
268 lclGetAbsPath( *xLongName
, 0, pDocShell
);
271 else if( ::get_flag( nFlags
, EXC_HLINK_BODY
) )
275 if( aGuid
== XclTools::maGuidFileMoniker
)
277 sal_uInt16 nLevel
= rStrm
.ReaduInt16(); // counter for level to climb down in path
278 xShortName
.reset( new OUString
);
279 lclAppendString32( *xShortName
, rStrm
, false );
282 sal_uInt32 nStrLen
= rStrm
.ReaduInt32();
285 nStrLen
= rStrm
.ReaduInt32();
286 nStrLen
/= 2; // it's byte count here...
288 xLongName
.reset( new OUString
);
289 lclAppendString32( *xLongName
, rStrm
, nStrLen
, true );
290 lclGetAbsPath( *xLongName
, nLevel
, pDocShell
);
293 lclGetAbsPath( *xShortName
, nLevel
, pDocShell
);
295 else if( aGuid
== XclTools::maGuidUrlMoniker
)
297 sal_uInt32 nStrLen
= rStrm
.ReaduInt32();
298 nStrLen
/= 2; // it's byte count here...
299 xLongName
.reset( new OUString
);
300 lclAppendString32( *xLongName
, rStrm
, nStrLen
, true );
301 if( !::get_flag( nFlags
, EXC_HLINK_ABS
) )
302 lclGetAbsPath( *xLongName
, 0, pDocShell
);
306 OSL_FAIL( "XclImpHyperlink::ReadEmbeddedData - unknown content GUID" );
311 if( ::get_flag( nFlags
, EXC_HLINK_MARK
) )
313 xTextMark
.reset( new OUString
);
314 lclAppendString32( *xTextMark
, rStrm
, true );
317 rStrm
.SetNulSubstChar(); // back to default
319 OSL_ENSURE( rStrm
.GetRecLeft() == 0, "XclImpHyperlink::ReadEmbeddedData - record size mismatch" );
321 if (!xLongName
&& xShortName
)
322 xLongName
= std::move(xShortName
);
323 else if (!xLongName
&& xTextMark
)
324 xLongName
.reset( new OUString
);
330 if( xLongName
->isEmpty() )
332 sal_Int32 nSepPos
= xTextMark
->lastIndexOf( '!' );
335 // Do not attempt to blindly convert '#SheetName!A1' to
336 // '#SheetName.A1', it can be #SheetName!R1C1 as well.
337 // Hyperlink handler has to handle all, but prefer
338 // '#SheetName.A1' if possible.
339 if (nSepPos
< xTextMark
->getLength() - 1)
341 ScDocument
& rDoc
= rRoot
.GetDoc();
343 if ((aRange
.ParseAny( xTextMark
->copy( nSepPos
+ 1 ), rDoc
, formula::FormulaGrammar::CONV_XL_R1C1
)
344 & ScRefFlags::VALID
) == ScRefFlags::ZERO
)
345 xTextMark
.reset( new OUString( xTextMark
->replaceAt( nSepPos
, 1, rtl::OUStringChar( '.' ))));
349 xLongName
.reset( new OUString( *xLongName
+ "#" + *xTextMark
) );
351 return( *xLongName
);
353 return( OUString() );
356 void XclImpHyperlink::ConvertToValidTabName(OUString
& rUrl
)
358 sal_Int32 n
= rUrl
.getLength();
360 // Needs at least 4 characters.
364 // the 1st character must be '#'.
367 OUStringBuffer
aNewUrl("#");
368 OUStringBuffer aTabName
;
370 bool bInQuote
= false;
371 bool bQuoteTabName
= false;
372 for( sal_Int32 i
= 1; i
< n
; ++i
)
374 sal_Unicode c
= rUrl
[i
];
377 if (bInQuote
&& i
+1 < n
&& rUrl
[i
+1] == '\'')
379 // Two consecutive single quotes ('') signify a single literal
380 // quite. When this occurs, the whole table name needs to be
382 bQuoteTabName
= true;
383 aTabName
.append(OUStringChar(c
) + OUStringChar(c
));
388 bInQuote
= !bInQuote
;
389 if (!bInQuote
&& !aTabName
.isEmpty())
393 aNewUrl
.append(aTabName
);
405 // It should be outside the quotes!
408 // All is good. Pass the new URL.
409 rUrl
= aNewUrl
.makeStringAndClear();
412 void XclImpHyperlink::InsertUrl( XclImpRoot
& rRoot
, const XclRange
& rXclRange
, const OUString
& rUrl
)
415 ConvertToValidTabName(aUrl
);
417 SCTAB nScTab
= rRoot
.GetCurrScTab();
418 ScRange
aScRange( ScAddress::UNINITIALIZED
);
419 if( rRoot
.GetAddressConverter().ConvertRange( aScRange
, rXclRange
, nScTab
, nScTab
, true ) )
421 SCCOL nScCol1
, nScCol2
;
422 SCROW nScRow1
, nScRow2
;
423 aScRange
.GetVars( nScCol1
, nScRow1
, nScTab
, nScCol2
, nScRow2
, nScTab
);
425 if (comphelper::IsFuzzing())
427 SCROW nRows
= nScRow2
- nScRow1
;
430 SAL_WARN("sc.filter", "for fuzzing performance, clamped hyperlink apply range end row from " << nScRow2
<< " to " << nScRow1
+ 1024);
431 nScRow2
= nScRow1
+ 1024;
435 for( SCCOL nScCol
= nScCol1
; nScCol
<= nScCol2
; ++nScCol
)
436 for( SCROW nScRow
= nScRow1
; nScRow
<= nScRow2
; ++nScRow
)
437 lclInsertUrl( rRoot
, aUrl
, nScCol
, nScRow
, nScTab
);
441 // Label ranges ===============================================================
443 void XclImpLabelranges::ReadLabelranges( XclImpStream
& rStrm
)
445 const XclImpRoot
& rRoot
= rStrm
.GetRoot();
446 OSL_ENSURE_BIFF( rRoot
.GetBiff() == EXC_BIFF8
);
448 ScDocument
& rDoc
= rRoot
.GetDoc();
449 SCTAB nScTab
= rRoot
.GetCurrScTab();
450 XclImpAddressConverter
& rAddrConv
= rRoot
.GetAddressConverter();
451 ScRangePairListRef xLabelRangesRef
;
453 XclRangeList aRowXclRanges
, aColXclRanges
;
454 rStrm
>> aRowXclRanges
>> aColXclRanges
;
457 ScRangeList aRowScRanges
;
458 rAddrConv
.ConvertRangeList( aRowScRanges
, aRowXclRanges
, nScTab
, false );
459 xLabelRangesRef
= rDoc
.GetRowNameRangesRef();
460 for ( size_t i
= 0, nRanges
= aRowScRanges
.size(); i
< nRanges
; ++i
)
462 const ScRange
& rScRange
= aRowScRanges
[ i
];
463 ScRange
aDataRange( rScRange
);
464 if( aDataRange
.aEnd
.Col() < rDoc
.MaxCol() )
466 aDataRange
.aStart
.SetCol( aDataRange
.aEnd
.Col() + 1 );
467 aDataRange
.aEnd
.SetCol( rDoc
.MaxCol() );
469 else if( aDataRange
.aStart
.Col() > 0 )
471 aDataRange
.aEnd
.SetCol( aDataRange
.aStart
.Col() - 1 );
472 aDataRange
.aStart
.SetCol( 0 );
474 xLabelRangesRef
->Append( ScRangePair( rScRange
, aDataRange
) );
477 // column label ranges
478 ScRangeList aColScRanges
;
479 rAddrConv
.ConvertRangeList( aColScRanges
, aColXclRanges
, nScTab
, false );
480 xLabelRangesRef
= rDoc
.GetColNameRangesRef();
482 for ( size_t i
= 0, nRanges
= aColScRanges
.size(); i
< nRanges
; ++i
)
484 const ScRange
& rScRange
= aColScRanges
[ i
];
485 ScRange
aDataRange( rScRange
);
486 if( aDataRange
.aEnd
.Row() < rDoc
.MaxRow() )
488 aDataRange
.aStart
.SetRow( aDataRange
.aEnd
.Row() + 1 );
489 aDataRange
.aEnd
.SetRow( rDoc
.MaxRow() );
491 else if( aDataRange
.aStart
.Row() > 0 )
493 aDataRange
.aEnd
.SetRow( aDataRange
.aStart
.Row() - 1 );
494 aDataRange
.aStart
.SetRow( 0 );
496 xLabelRangesRef
->Append( ScRangePair( rScRange
, aDataRange
) );
500 // Conditional formatting =====================================================
502 XclImpCondFormat::XclImpCondFormat( const XclImpRoot
& rRoot
, sal_uInt32 nFormatIndex
) :
504 mnFormatIndex( nFormatIndex
),
510 XclImpCondFormat::~XclImpCondFormat()
514 void XclImpCondFormat::ReadCondfmt( XclImpStream
& rStrm
)
516 OSL_ENSURE( !mnCondCount
, "XclImpCondFormat::ReadCondfmt - already initialized" );
517 XclRangeList aXclRanges
;
518 mnCondCount
= rStrm
.ReaduInt16();
521 GetAddressConverter().ConvertRangeList( maRanges
, aXclRanges
, GetCurrScTab(), true );
524 void XclImpCondFormat::ReadCF( XclImpStream
& rStrm
)
526 if( mnCondIndex
>= mnCondCount
)
528 OSL_FAIL( "XclImpCondFormat::ReadCF - CF without leading CONDFMT" );
532 // entire conditional format outside of valid range?
533 if( maRanges
.empty() )
536 sal_uInt8 nType
= rStrm
.ReaduInt8();
537 sal_uInt8 nOperator
= rStrm
.ReaduInt8();
538 sal_uInt16 nFmlaSize1
= rStrm
.ReaduInt16();
539 sal_uInt16 nFmlaSize2
= rStrm
.ReaduInt16();
540 sal_uInt32 nFlags
= rStrm
.ReaduInt32();
541 rStrm
.Ignore( 2 ); //nFlagsExtended
543 // *** mode and comparison operator ***
545 ScConditionMode eMode
= ScConditionMode::NONE
;
548 case EXC_CF_TYPE_CELL
:
552 case EXC_CF_CMP_BETWEEN
: eMode
= ScConditionMode::Between
; break;
553 case EXC_CF_CMP_NOT_BETWEEN
: eMode
= ScConditionMode::NotBetween
; break;
554 case EXC_CF_CMP_EQUAL
: eMode
= ScConditionMode::Equal
; break;
555 case EXC_CF_CMP_NOT_EQUAL
: eMode
= ScConditionMode::NotEqual
; break;
556 case EXC_CF_CMP_GREATER
: eMode
= ScConditionMode::Greater
; break;
557 case EXC_CF_CMP_LESS
: eMode
= ScConditionMode::Less
; break;
558 case EXC_CF_CMP_GREATER_EQUAL
: eMode
= ScConditionMode::EqGreater
; break;
559 case EXC_CF_CMP_LESS_EQUAL
: eMode
= ScConditionMode::EqLess
; break;
562 "sc.filter", "unknown CF comparison " << nOperator
);
567 case EXC_CF_TYPE_FMLA
:
568 eMode
= ScConditionMode::Direct
;
572 SAL_INFO("sc.filter", "unknown CF mode " << nType
);
576 // *** create style sheet ***
578 OUString
aStyleName( XclTools::GetCondFormatStyleName( GetCurrScTab(), mnFormatIndex
, mnCondIndex
) );
579 SfxItemSet
& rStyleItemSet
= ScfTools::MakeCellStyleSheet( GetStyleSheetPool(), aStyleName
, true ).GetItemSet();
581 const XclImpPalette
& rPalette
= GetPalette();
585 if( get_flag( nFlags
, EXC_CF_BLOCK_NUMFMT
) )
587 XclImpNumFmtBuffer
& rNumFmtBuffer
= GetRoot().GetNumFmtBuffer();
588 bool bIFmt
= get_flag( nFlags
, EXC_CF_IFMT_USER
);
589 sal_uInt16 nFormat
= rNumFmtBuffer
.ReadCFFormat( rStrm
, bIFmt
);
590 rNumFmtBuffer
.FillToItemSet( rStyleItemSet
, nFormat
);
593 // *** font block ***
595 if( ::get_flag( nFlags
, EXC_CF_BLOCK_FONT
) )
597 XclImpFont
aFont( GetRoot() );
598 aFont
.ReadCFFontBlock( rStrm
);
599 aFont
.FillToItemSet( rStyleItemSet
, XclFontItemType::Cell
);
603 if( get_flag( nFlags
, EXC_CF_BLOCK_ALIGNMENT
) )
605 XclImpCellAlign aAlign
;
606 sal_uInt16
nAlign(0);
607 sal_uInt16
nAlignMisc(0);
608 nAlign
= rStrm
.ReaduInt16();
609 nAlignMisc
= rStrm
.ReaduInt16();
610 aAlign
.FillFromCF( nAlign
, nAlignMisc
);
611 aAlign
.FillToItemSet( rStyleItemSet
, nullptr );
615 // *** border block ***
617 if( ::get_flag( nFlags
, EXC_CF_BLOCK_BORDER
) )
619 sal_uInt16
nLineStyle(0);
620 sal_uInt32
nLineColor(0);
621 nLineStyle
= rStrm
.ReaduInt16();
622 nLineColor
= rStrm
.ReaduInt32();
625 XclImpCellBorder aBorder
;
626 aBorder
.FillFromCF8( nLineStyle
, nLineColor
, nFlags
);
627 aBorder
.FillToItemSet( rStyleItemSet
, rPalette
);
630 // *** pattern block ***
632 if( ::get_flag( nFlags
, EXC_CF_BLOCK_AREA
) )
634 sal_uInt16
nPattern(0), nColor(0);
635 nPattern
= rStrm
.ReaduInt16();
636 nColor
= rStrm
.ReaduInt16();
638 XclImpCellArea aArea
;
639 aArea
.FillFromCF8( nPattern
, nColor
, nFlags
);
640 aArea
.FillToItemSet( rStyleItemSet
, rPalette
);
643 if( get_flag( nFlags
, EXC_CF_BLOCK_PROTECTION
) )
645 sal_uInt16 nCellProt
;
646 nCellProt
= rStrm
.ReaduInt16();
647 XclImpCellProt aCellProt
;
648 aCellProt
.FillFromXF3(nCellProt
);
649 aCellProt
.FillToItemSet( rStyleItemSet
);
654 const ScAddress
& rPos
= maRanges
.front().aStart
; // assured above that maRanges is not empty
655 ExcelToSc
& rFmlaConv
= GetOldFmlaConverter();
657 ::std::unique_ptr
< ScTokenArray
> xTokArr1
;
660 std::unique_ptr
<ScTokenArray
> pTokArr
;
661 rFmlaConv
.Reset( rPos
);
662 rFmlaConv
.Convert( pTokArr
, rStrm
, nFmlaSize1
, false, FT_CondFormat
);
663 // formula converter owns pTokArr -> create a copy of the token array
666 xTokArr1
= std::move( pTokArr
);
667 GetDoc().CheckLinkFormulaNeedingCheck( *xTokArr1
);
671 ::std::unique_ptr
< ScTokenArray
> xTokArr2
;
674 std::unique_ptr
<ScTokenArray
> pTokArr
;
675 rFmlaConv
.Reset( rPos
);
676 rFmlaConv
.Convert( pTokArr
, rStrm
, nFmlaSize2
, false, FT_CondFormat
);
677 // formula converter owns pTokArr -> create a copy of the token array
680 xTokArr2
= std::move( pTokArr
);
681 GetDoc().CheckLinkFormulaNeedingCheck( *xTokArr2
);
685 // *** create the Calc conditional formatting ***
687 const ScAddress
aPos(rPos
); //in case maRanges.Join invalidates it
691 mxScCondFmt
.reset( new ScConditionalFormat( 0/*nKey*/, &GetDoc() ) );
692 if(maRanges
.size() > 1)
693 maRanges
.Join(maRanges
[0], true);
694 mxScCondFmt
->SetRange(maRanges
);
697 ScCondFormatEntry
* pEntry
= new ScCondFormatEntry(eMode
, xTokArr1
.get(), xTokArr2
.get(), GetDoc(), aPos
, aStyleName
);
698 mxScCondFmt
->AddEntry( pEntry
);
702 void XclImpCondFormat::Apply()
706 ScDocument
& rDoc
= GetDoc();
708 SCTAB nTab
= maRanges
.front().aStart
.Tab();
709 sal_uInt32 nKey
= rDoc
.AddCondFormat( mxScCondFmt
->Clone(), nTab
);
711 rDoc
.AddCondFormatData( maRanges
, nTab
, nKey
);
715 XclImpCondFormatManager::XclImpCondFormatManager( const XclImpRoot
& rRoot
) :
720 void XclImpCondFormatManager::ReadCondfmt( XclImpStream
& rStrm
)
722 XclImpCondFormat
* pFmt
= new XclImpCondFormat( GetRoot(), maCondFmtList
.size() );
723 pFmt
->ReadCondfmt( rStrm
);
724 maCondFmtList
.push_back( std::unique_ptr
<XclImpCondFormat
>(pFmt
) );
727 void XclImpCondFormatManager::ReadCF( XclImpStream
& rStrm
)
729 OSL_ENSURE( !maCondFmtList
.empty(), "XclImpCondFormatManager::ReadCF - CF without leading CONDFMT" );
730 if( !maCondFmtList
.empty() )
731 maCondFmtList
.back()->ReadCF( rStrm
);
734 void XclImpCondFormatManager::Apply()
736 for( auto& rxFmt
: maCondFmtList
)
738 maCondFmtList
.clear();
741 // Data Validation ============================================================
743 XclImpValidationManager::DVItem::DVItem( ScRangeList aRanges
, const ScValidationData
& rValidData
) :
744 maRanges(std::move(aRanges
)), maValidData(rValidData
) {}
746 XclImpValidationManager::XclImpValidationManager( const XclImpRoot
& rRoot
) :
751 void XclImpValidationManager::ReadDval( XclImpStream
& rStrm
)
753 const XclImpRoot
& rRoot
= rStrm
.GetRoot();
754 OSL_ENSURE_BIFF( rRoot
.GetBiff() == EXC_BIFF8
);
756 sal_uInt32
nObjId(0);
758 nObjId
= rStrm
.ReaduInt32();
759 if( nObjId
!= EXC_DVAL_NOOBJ
)
761 OSL_ENSURE( nObjId
<= 0xFFFF, "XclImpValidation::ReadDval - invalid object ID" );
762 rRoot
.GetCurrSheetDrawing().SetSkipObj( static_cast< sal_uInt16
>( nObjId
) );
766 void XclImpValidationManager::ReadDV( XclImpStream
& rStrm
)
768 const XclImpRoot
& rRoot
= rStrm
.GetRoot();
769 OSL_ENSURE_BIFF( rRoot
.GetBiff() == EXC_BIFF8
);
771 ScDocument
& rDoc
= rRoot
.GetDoc();
772 SCTAB nScTab
= rRoot
.GetCurrScTab();
773 ExcelToSc
& rFmlaConv
= rRoot
.GetOldFmlaConverter();
776 sal_uInt32 nFlags
= rStrm
.ReaduInt32();
779 /* Empty strings are single NUL characters in Excel (string length is 1).
780 -> Do not let the stream replace them with '?' characters. */
781 rStrm
.SetNulSubstChar( '\0' );
782 OUString
aPromptTitle( rStrm
.ReadUniString() );
783 OUString
aErrorTitle( rStrm
.ReadUniString() );
784 OUString
aPromptMessage( rStrm
.ReadUniString() );
785 OUString
aErrorMessage( rStrm
.ReadUniString() );
786 rStrm
.SetNulSubstChar(); // back to default
789 if ( rStrm
.GetRecLeft() <= 8 )
790 // Not enough bytes left in the record. Bail out.
794 // string list is single tStr token with NUL separators -> replace them with LF
795 rStrm
.SetNulSubstChar( '\n' );
796 ::std::unique_ptr
< ScTokenArray
> xTokArr1
;
798 // We can't import the formula directly because we need the range
799 sal_uInt16 nLenFormula1
= rStrm
.ReaduInt16();
801 XclImpStreamPos aPosFormula1
;
802 rStrm
.StorePosition(aPosFormula1
);
803 rStrm
.Ignore(nLenFormula1
);
806 ::std::unique_ptr
< ScTokenArray
> xTokArr2
;
808 sal_uInt16 nLenFormula2
= rStrm
.ReaduInt16();
810 XclImpStreamPos aPosFormula2
;
811 rStrm
.StorePosition(aPosFormula2
);
812 rStrm
.Ignore(nLenFormula2
);
814 // read all cell ranges
815 XclRangeList aXclRanges
;
818 // convert to Calc range list
819 ScRangeList aScRanges
;
820 rRoot
.GetAddressConverter().ConvertRangeList( aScRanges
, aXclRanges
, nScTab
, true );
822 // only continue if there are valid ranges
823 if ( aScRanges
.empty() )
826 ScRange aCombinedRange
= aScRanges
.Combine();
828 XclImpStreamPos aCurrentPos
;
829 rStrm
.StorePosition(aCurrentPos
);
830 rStrm
.RestorePosition(aPosFormula1
);
831 if( nLenFormula1
> 0 )
833 std::unique_ptr
<ScTokenArray
> pTokArr
;
834 rFmlaConv
.Reset(aCombinedRange
.aStart
);
835 rFmlaConv
.Convert( pTokArr
, rStrm
, nLenFormula1
, false, FT_CondFormat
);
836 // formula converter owns pTokArr -> create a copy of the token array
838 xTokArr1
= std::move( pTokArr
);
840 rStrm
.SetNulSubstChar(); // back to default
841 if (nLenFormula2
> 0)
843 rStrm
.RestorePosition(aPosFormula2
);
844 std::unique_ptr
<ScTokenArray
> pTokArr
;
845 rFmlaConv
.Reset(aCombinedRange
.aStart
);
846 rFmlaConv
.Convert( pTokArr
, rStrm
, nLenFormula2
, false, FT_CondFormat
);
847 // formula converter owns pTokArr -> create a copy of the token array
849 xTokArr2
= std::move( pTokArr
);
852 rStrm
.RestorePosition(aCurrentPos
);
854 bool bIsValid
= true; // valid settings in flags field
856 ScValidationMode eValMode
= SC_VALID_ANY
;
857 switch( nFlags
& EXC_DV_MODE_MASK
)
859 case EXC_DV_MODE_ANY
: eValMode
= SC_VALID_ANY
; break;
860 case EXC_DV_MODE_WHOLE
: eValMode
= SC_VALID_WHOLE
; break;
861 case EXC_DV_MODE_DECIMAL
: eValMode
= SC_VALID_DECIMAL
; break;
862 case EXC_DV_MODE_LIST
: eValMode
= SC_VALID_LIST
; break;
863 case EXC_DV_MODE_DATE
: eValMode
= SC_VALID_DATE
; break;
864 case EXC_DV_MODE_TIME
: eValMode
= SC_VALID_TIME
; break;
865 case EXC_DV_MODE_TEXTLEN
: eValMode
= SC_VALID_TEXTLEN
; break;
866 case EXC_DV_MODE_CUSTOM
: eValMode
= SC_VALID_CUSTOM
; break;
867 default: bIsValid
= false;
869 rRoot
.GetTracer().TraceDVType(eValMode
== SC_VALID_CUSTOM
);
871 ScConditionMode eCondMode
= ScConditionMode::Between
;
872 switch( nFlags
& EXC_DV_COND_MASK
)
874 case EXC_DV_COND_BETWEEN
: eCondMode
= ScConditionMode::Between
; break;
875 case EXC_DV_COND_NOTBETWEEN
:eCondMode
= ScConditionMode::NotBetween
; break;
876 case EXC_DV_COND_EQUAL
: eCondMode
= ScConditionMode::Equal
; break;
877 case EXC_DV_COND_NOTEQUAL
: eCondMode
= ScConditionMode::NotEqual
; break;
878 case EXC_DV_COND_GREATER
: eCondMode
= ScConditionMode::Greater
; break;
879 case EXC_DV_COND_LESS
: eCondMode
= ScConditionMode::Less
; break;
880 case EXC_DV_COND_EQGREATER
: eCondMode
= ScConditionMode::EqGreater
; break;
881 case EXC_DV_COND_EQLESS
: eCondMode
= ScConditionMode::EqLess
; break;
882 default: bIsValid
= false;
886 // No valid validation found. Bail out.
889 // The default value for comparison is _BETWEEN. However, custom
890 // rules are a formula, and thus the comparator should be ignored
891 // and only a true or false from the formula is evaluated. In Calc,
892 // formulas use comparison SC_COND_DIRECT.
893 if( eValMode
== SC_VALID_CUSTOM
)
895 eCondMode
= ScConditionMode::Direct
;
898 // first range for base address for relative references
899 const ScRange
& rScRange
= aScRanges
.front(); // aScRanges is not empty
901 // process string list of a list validity (convert to list of string tokens)
902 if( xTokArr1
&& (eValMode
== SC_VALID_LIST
) && ::get_flag( nFlags
, EXC_DV_STRINGLIST
) )
903 XclTokenArrayHelper::ConvertStringToList(*xTokArr1
, rDoc
.GetSharedStringPool(), '\n');
906 std::make_unique
<DVItem
>(aScRanges
, ScValidationData(eValMode
, eCondMode
, xTokArr1
.get(), xTokArr2
.get(), rDoc
, rScRange
.aStart
)));
907 DVItem
& rItem
= *maDVItems
.back();
909 rItem
.maValidData
.SetIgnoreBlank( ::get_flag( nFlags
, EXC_DV_IGNOREBLANK
) );
910 rItem
.maValidData
.SetListType( ::get_flagvalue( nFlags
, EXC_DV_SUPPRESSDROPDOWN
, css::sheet::TableValidationVisibility::INVISIBLE
, css::sheet::TableValidationVisibility::UNSORTED
) );
912 // *** prompt box ***
913 if( !aPromptTitle
.isEmpty() || !aPromptMessage
.isEmpty() )
915 // set any text stored in the record
916 rItem
.maValidData
.SetInput( aPromptTitle
, aPromptMessage
);
917 if( !::get_flag( nFlags
, EXC_DV_SHOWPROMPT
) )
918 rItem
.maValidData
.ResetInput();
922 ScValidErrorStyle eErrStyle
= SC_VALERR_STOP
;
923 switch( nFlags
& EXC_DV_ERROR_MASK
)
925 case EXC_DV_ERROR_WARNING
: eErrStyle
= SC_VALERR_WARNING
; break;
926 case EXC_DV_ERROR_INFO
: eErrStyle
= SC_VALERR_INFO
; break;
928 // set texts and error style
929 rItem
.maValidData
.SetError( aErrorTitle
, aErrorMessage
, eErrStyle
);
930 if( !::get_flag( nFlags
, EXC_DV_SHOWERROR
) )
931 rItem
.maValidData
.ResetError();
934 void XclImpValidationManager::Apply()
936 const bool bFuzzing
= comphelper::IsFuzzing();
937 size_t nPatterns
= 0;
939 ScDocument
& rDoc
= GetRoot().GetDoc();
940 for (const auto& rxDVItem
: maDVItems
)
942 DVItem
& rItem
= *rxDVItem
;
944 sal_uInt32 nHandle
= rDoc
.AddValidationEntry( rItem
.maValidData
);
945 ScPatternAttr
aPattern(rDoc
.getCellAttributeHelper());
946 aPattern
.GetItemSet().Put( SfxUInt32Item( ATTR_VALIDDATA
, nHandle
) );
949 for ( size_t i
= 0, nRanges
= rItem
.maRanges
.size(); i
< nRanges
; ++i
, ++nPatterns
)
951 const ScRange
& rScRange
= rItem
.maRanges
[ i
];
952 rDoc
.ApplyPatternAreaTab( rScRange
.aStart
.Col(), rScRange
.aStart
.Row(),
953 rScRange
.aEnd
.Col(), rScRange
.aEnd
.Row(), rScRange
.aStart
.Tab(), aPattern
);
954 if (bFuzzing
&& nPatterns
>= 128)
956 SAL_WARN("sc.filter", "for fuzzing performance, abandoned pattern after " << nPatterns
<< " insertions");
964 // Web queries ================================================================
966 XclImpWebQuery::XclImpWebQuery( const ScRange
& rDestRange
) :
967 maDestRange( rDestRange
),
968 meMode( xlWQUnknown
),
973 void XclImpWebQuery::ReadParamqry( XclImpStream
& rStrm
)
975 sal_uInt16 nFlags
= rStrm
.ReaduInt16();
976 sal_uInt16 nType
= ::extract_value
< sal_uInt16
>( nFlags
, 0, 3 );
977 if( !((nType
== EXC_PQRYTYPE_WEBQUERY
) && ::get_flag( nFlags
, EXC_PQRY_WEBQUERY
)) )
980 if( ::get_flag( nFlags
, EXC_PQRY_TABLES
) )
982 meMode
= xlWQAllTables
;
983 maTables
= ScfTools::GetHTMLTablesName();
987 meMode
= xlWQDocument
;
988 maTables
= ScfTools::GetHTMLDocName();
992 void XclImpWebQuery::ReadWqstring( XclImpStream
& rStrm
)
994 maURL
= rStrm
.ReadUniString();
997 void XclImpWebQuery::ReadWqsettings( XclImpStream
& rStrm
)
1000 sal_uInt16 nFlags
= rStrm
.ReaduInt16();
1002 mnRefresh
= rStrm
.ReaduInt16();
1004 if( ::get_flag( nFlags
, EXC_WQSETT_SPECTABLES
) && (meMode
== xlWQAllTables
) )
1005 meMode
= xlWQSpecTables
;
1008 void XclImpWebQuery::ReadWqtables( XclImpStream
& rStrm
)
1010 if( meMode
!= xlWQSpecTables
)
1014 OUString
aTables( rStrm
.ReadUniString() );
1016 const sal_Unicode cSep
= ';';
1017 static constexpr OUStringLiteral
aQuotedPairs( u
"\"\"" );
1019 for ( sal_Int32 nStringIx
{aTables
.isEmpty() ? -1 : 0}; nStringIx
>=0; )
1021 OUString
aToken( ScStringUtil::GetQuotedToken( aTables
, 0, aQuotedPairs
, ',', nStringIx
) );
1022 sal_Int32 nTabNum
= CharClass::isAsciiNumeric( aToken
) ? aToken
.toInt32() : 0;
1024 maTables
= ScGlobal::addToken( maTables
, ScfTools::GetNameFromHTMLIndex( static_cast< sal_uInt32
>( nTabNum
) ), cSep
);
1027 ScGlobal::EraseQuotes( aToken
, '"', false );
1028 if( !aToken
.isEmpty() )
1029 maTables
= ScGlobal::addToken( maTables
, ScfTools::GetNameFromHTMLName( aToken
), cSep
);
1034 void XclImpWebQuery::Apply( ScDocument
& rDoc
, const OUString
& rFilterName
)
1036 if( !maURL
.isEmpty() && (meMode
!= xlWQUnknown
) && rDoc
.GetDocumentShell() )
1038 ScAreaLink
* pLink
= new ScAreaLink( rDoc
.GetDocumentShell(),
1039 maURL
, rFilterName
, OUString(), maTables
, maDestRange
, mnRefresh
* 60UL );
1040 rDoc
.GetLinkManager()->InsertFileLink( *pLink
, sfx2::SvBaseLinkObjectType::ClientFile
,
1041 maURL
, &rFilterName
, &maTables
);
1045 XclImpWebQueryBuffer::XclImpWebQueryBuffer( const XclImpRoot
& rRoot
) :
1050 void XclImpWebQueryBuffer::ReadQsi( XclImpStream
& rStrm
)
1052 if( GetBiff() == EXC_BIFF8
)
1055 OUString
aXclName( rStrm
.ReadUniString() );
1057 // #i64794# Excel replaces spaces with underscores
1058 aXclName
= aXclName
.replaceAll( " ", "_" );
1060 // find the defined name used in Calc
1061 if( const XclImpName
* pName
= GetNameManager().FindName( aXclName
, GetCurrScTab() ) )
1063 if( const ScRangeData
* pRangeData
= pName
->GetScRangeData() )
1066 if( pRangeData
->IsReference( aRange
) )
1067 maWQList
.emplace_back( aRange
);
1077 void XclImpWebQueryBuffer::ReadParamqry( XclImpStream
& rStrm
)
1079 if (!maWQList
.empty())
1080 maWQList
.back().ReadParamqry( rStrm
);
1083 void XclImpWebQueryBuffer::ReadWqstring( XclImpStream
& rStrm
)
1085 if (!maWQList
.empty())
1086 maWQList
.back().ReadWqstring( rStrm
);
1089 void XclImpWebQueryBuffer::ReadWqsettings( XclImpStream
& rStrm
)
1091 if (!maWQList
.empty())
1092 maWQList
.back().ReadWqsettings( rStrm
);
1095 void XclImpWebQueryBuffer::ReadWqtables( XclImpStream
& rStrm
)
1097 if (!maWQList
.empty())
1098 maWQList
.back().ReadWqtables( rStrm
);
1101 void XclImpWebQueryBuffer::Apply()
1103 ScDocument
& rDoc
= GetDoc();
1104 for( auto& rQuery
: maWQList
)
1105 rQuery
.Apply( rDoc
, EXC_WEBQRY_FILTER
);
1108 // Decryption =================================================================
1112 XclImpDecrypterRef
lclReadFilepass5( XclImpStream
& rStrm
)
1114 XclImpDecrypterRef xDecr
;
1115 OSL_ENSURE( rStrm
.GetRecLeft() == 4, "lclReadFilepass5 - wrong record size" );
1116 if( rStrm
.GetRecLeft() == 4 )
1118 sal_uInt16
nKey(0), nHash(0);
1119 nKey
= rStrm
.ReaduInt16();
1120 nHash
= rStrm
.ReaduInt16();
1121 xDecr
= std::make_shared
<XclImpBiff5Decrypter
>( nKey
, nHash
);
1126 XclImpDecrypterRef
lclReadFilepass8_Standard( XclImpStream
& rStrm
)
1128 XclImpDecrypterRef xDecr
;
1129 OSL_ENSURE( rStrm
.GetRecLeft() == 48, "lclReadFilepass8 - wrong record size" );
1130 if( rStrm
.GetRecLeft() == 48 )
1132 std::vector
<sal_uInt8
> aSalt(16);
1133 std::vector
<sal_uInt8
> aVerifier(16);
1134 std::vector
<sal_uInt8
> aVerifierHash(16);
1135 rStrm
.Read(aSalt
.data(), 16);
1136 rStrm
.Read(aVerifier
.data(), 16);
1137 rStrm
.Read(aVerifierHash
.data(), 16);
1138 xDecr
= std::make_shared
<XclImpBiff8StdDecrypter
>(std::move(aSalt
), std::move(aVerifier
), std::move(aVerifierHash
));
1143 XclImpDecrypterRef
lclReadFilepass8_Strong(XclImpStream
& rStream
)
1145 //It is possible there are other variants in existence but these
1146 //are the defaults I get with Excel 2013
1147 XclImpDecrypterRef xDecr
;
1149 msfilter::RC4EncryptionInfo info
;
1151 info
.header
.flags
= rStream
.ReaduInt32();
1152 if (oox::getFlag( info
.header
.flags
, msfilter::ENCRYPTINFO_EXTERNAL
))
1155 sal_uInt32 nHeaderSize
= rStream
.ReaduInt32();
1156 sal_uInt32 actualHeaderSize
= sizeof(info
.header
);
1158 if( nHeaderSize
< actualHeaderSize
)
1161 info
.header
.flags
= rStream
.ReaduInt32();
1162 info
.header
.sizeExtra
= rStream
.ReaduInt32();
1163 info
.header
.algId
= rStream
.ReaduInt32();
1164 info
.header
.algIdHash
= rStream
.ReaduInt32();
1165 info
.header
.keyBits
= rStream
.ReaduInt32();
1166 info
.header
.providedType
= rStream
.ReaduInt32();
1167 info
.header
.reserved1
= rStream
.ReaduInt32();
1168 info
.header
.reserved2
= rStream
.ReaduInt32();
1170 rStream
.Ignore(nHeaderSize
- actualHeaderSize
);
1172 info
.verifier
.saltSize
= rStream
.ReaduInt32();
1173 if (info
.verifier
.saltSize
!= msfilter::SALT_LENGTH
)
1175 rStream
.Read(&info
.verifier
.salt
, sizeof(info
.verifier
.salt
));
1176 rStream
.Read(&info
.verifier
.encryptedVerifier
, sizeof(info
.verifier
.encryptedVerifier
));
1178 info
.verifier
.encryptedVerifierHashSize
= rStream
.ReaduInt32();
1179 if (info
.verifier
.encryptedVerifierHashSize
!= RTL_DIGEST_LENGTH_SHA1
)
1181 rStream
.Read(&info
.verifier
.encryptedVerifierHash
, info
.verifier
.encryptedVerifierHashSize
);
1183 // check flags and algorithm IDs, required are AES128 and SHA-1
1184 if (!oox::getFlag(info
.header
.flags
, msfilter::ENCRYPTINFO_CRYPTOAPI
))
1187 if (oox::getFlag(info
.header
.flags
, msfilter::ENCRYPTINFO_AES
))
1190 if (info
.header
.algId
!= msfilter::ENCRYPT_ALGO_RC4
)
1193 // hash algorithm ID 0 defaults to SHA-1 too
1194 if (info
.header
.algIdHash
!= 0 && info
.header
.algIdHash
!= msfilter::ENCRYPT_HASH_SHA1
)
1197 xDecr
= std::make_shared
<XclImpBiff8CryptoAPIDecrypter
>(
1198 std::vector
<sal_uInt8
>(info
.verifier
.salt
,
1199 info
.verifier
.salt
+ SAL_N_ELEMENTS(info
.verifier
.salt
)),
1200 std::vector
<sal_uInt8
>(info
.verifier
.encryptedVerifier
,
1201 info
.verifier
.encryptedVerifier
+ SAL_N_ELEMENTS(info
.verifier
.encryptedVerifier
)),
1202 std::vector
<sal_uInt8
>(info
.verifier
.encryptedVerifierHash
,
1203 info
.verifier
.encryptedVerifierHash
+ SAL_N_ELEMENTS(info
.verifier
.encryptedVerifierHash
)));
1208 XclImpDecrypterRef
lclReadFilepass8( XclImpStream
& rStrm
)
1210 XclImpDecrypterRef xDecr
;
1212 sal_uInt16 nMode
= rStrm
.ReaduInt16();
1215 case EXC_FILEPASS_BIFF5
:
1216 xDecr
= lclReadFilepass5( rStrm
);
1219 case EXC_FILEPASS_BIFF8
:
1221 sal_uInt32 nVersion
= rStrm
.ReaduInt32();
1222 if (nVersion
== msfilter::VERSION_INFO_1997_FORMAT
)
1224 //A Version structure where Version.vMajor MUST be 0x0001,
1225 //and Version.vMinor MUST be 0x0001.
1226 xDecr
= lclReadFilepass8_Standard(rStrm
);
1228 else if (nVersion
== msfilter::VERSION_INFO_2007_FORMAT
||
1229 nVersion
== msfilter::VERSION_INFO_2007_FORMAT_SP2
)
1231 //Version.vMajor MUST be 0x0002, 0x0003 or 0x0004 and
1232 //Version.vMinor MUST be 0x0002.
1233 xDecr
= lclReadFilepass8_Strong(rStrm
);
1236 OSL_FAIL("lclReadFilepass8 - unknown BIFF8 encryption sub mode");
1241 OSL_FAIL( "lclReadFilepass8 - unknown encryption mode" );
1249 const ErrCode
& XclImpDecryptHelper::ReadFilepass( XclImpStream
& rStrm
)
1251 XclImpDecrypterRef xDecr
;
1252 rStrm
.DisableDecryption();
1254 // read the FILEPASS record and create a new decrypter object
1255 switch( rStrm
.GetRoot().GetBiff() )
1260 case EXC_BIFF5
: xDecr
= lclReadFilepass5( rStrm
); break;
1261 case EXC_BIFF8
: xDecr
= lclReadFilepass8( rStrm
); break;
1262 default: DBG_ERROR_BIFF();
1265 // set decrypter at import stream
1266 rStrm
.SetDecrypter( xDecr
);
1268 // request and verify a password (decrypter implements IDocPasswordVerifier)
1270 rStrm
.GetRoot().RequestEncryptionData( *xDecr
);
1272 // return error code (success, wrong password, etc.)
1273 return xDecr
? xDecr
->GetError() : EXC_ENCR_ERROR_UNSUPP_CRYPT
;
1276 // Document protection ========================================================
1278 XclImpDocProtectBuffer::XclImpDocProtectBuffer( const XclImpRoot
& rRoot
) :
1279 XclImpRoot( rRoot
),
1281 mbDocProtect(false),
1286 void XclImpDocProtectBuffer::ReadDocProtect( XclImpStream
& rStrm
)
1288 mbDocProtect
= rStrm
.ReaduInt16() != 0;
1291 void XclImpDocProtectBuffer::ReadWinProtect( XclImpStream
& rStrm
)
1293 mbWinProtect
= rStrm
.ReaduInt16() != 0;
1296 void XclImpDocProtectBuffer::ReadPasswordHash( XclImpStream
& rStrm
)
1298 rStrm
.EnableDecryption();
1299 mnPassHash
= rStrm
.ReaduInt16();
1302 void XclImpDocProtectBuffer::Apply() const
1304 if (!mbDocProtect
&& !mbWinProtect
)
1305 // Excel requires either the structure or windows protection is set.
1306 // If neither is set then the document is not protected at all.
1309 unique_ptr
<ScDocProtection
> pProtect(new ScDocProtection
);
1310 pProtect
->setProtected(true);
1314 // 16-bit password hash.
1315 Sequence
<sal_Int8
> aPass
{sal_Int8(mnPassHash
>> 8), sal_Int8(mnPassHash
& 0xFF)};
1316 pProtect
->setPasswordHash(aPass
, PASSHASH_XL
);
1319 // document protection options
1320 pProtect
->setOption(ScDocProtection::STRUCTURE
, mbDocProtect
);
1321 pProtect
->setOption(ScDocProtection::WINDOWS
, mbWinProtect
);
1323 GetDoc().SetDocProtection(pProtect
.get());
1326 // Sheet Protection ===========================================================
1328 XclImpSheetProtectBuffer::Sheet::Sheet() :
1330 mnPasswordHash(0x0000),
1335 XclImpSheetProtectBuffer::Sheet::Sheet(const Sheet
& r
) :
1336 mbProtected(r
.mbProtected
),
1337 mnPasswordHash(r
.mnPasswordHash
),
1338 mnOptions(r
.mnOptions
)
1342 XclImpSheetProtectBuffer::XclImpSheetProtectBuffer( const XclImpRoot
& rRoot
) :
1347 void XclImpSheetProtectBuffer::ReadProtect( XclImpStream
& rStrm
, SCTAB nTab
)
1349 if ( rStrm
.ReaduInt16() )
1351 Sheet
* pSheet
= GetSheetItem(nTab
);
1353 pSheet
->mbProtected
= true;
1357 void XclImpSheetProtectBuffer::ReadOptions( XclImpStream
& rStrm
, SCTAB nTab
)
1359 // The flag size specifies the size of bytes that follows that stores
1360 // feature data. If -1 it depends on the feature type imported earlier.
1361 // For enhanced protection data, the size is always 4. For the most xls
1362 // documents out there this value is almost always -1.
1363 sal_Int32 nFlagSize
= rStrm
.ReadInt32();
1364 if (nFlagSize
!= -1)
1367 // There are actually 4 bytes to read, but the upper 2 bytes currently
1368 // don't store any bits.
1369 sal_uInt16 nOptions
= rStrm
.ReaduInt16();
1371 Sheet
* pSheet
= GetSheetItem(nTab
);
1373 pSheet
->mnOptions
= nOptions
;
1376 void XclImpSheetProtectBuffer::AppendEnhancedProtection( const ScEnhancedProtection
& rProt
, SCTAB nTab
)
1378 Sheet
* pSheet
= GetSheetItem(nTab
);
1380 pSheet
->maEnhancedProtections
.push_back( rProt
);
1383 void XclImpSheetProtectBuffer::ReadPasswordHash( XclImpStream
& rStrm
, SCTAB nTab
)
1385 sal_uInt16 nHash
= rStrm
.ReaduInt16();
1386 Sheet
* pSheet
= GetSheetItem(nTab
);
1388 pSheet
->mnPasswordHash
= nHash
;
1391 void XclImpSheetProtectBuffer::Apply() const
1393 for (const auto& [rTab
, rSheet
] : maProtectedSheets
)
1395 if (!rSheet
.mbProtected
)
1396 // This sheet is (for whatever reason) not protected.
1399 unique_ptr
<ScTableProtection
> pProtect(new ScTableProtection
);
1400 pProtect
->setProtected(true);
1402 // 16-bit hash password
1403 const sal_uInt16 nHash
= rSheet
.mnPasswordHash
;
1406 Sequence
<sal_Int8
> aPass
{sal_Int8(nHash
>> 8), sal_Int8(nHash
& 0xFF)};
1407 pProtect
->setPasswordHash(aPass
, PASSHASH_XL
);
1410 // sheet protection options
1411 const sal_uInt16 nOptions
= rSheet
.mnOptions
;
1412 pProtect
->setOption( ScTableProtection::OBJECTS
, (nOptions
& 0x0001) );
1413 pProtect
->setOption( ScTableProtection::SCENARIOS
, (nOptions
& 0x0002) );
1414 pProtect
->setOption( ScTableProtection::FORMAT_CELLS
, (nOptions
& 0x0004) );
1415 pProtect
->setOption( ScTableProtection::FORMAT_COLUMNS
, (nOptions
& 0x0008) );
1416 pProtect
->setOption( ScTableProtection::FORMAT_ROWS
, (nOptions
& 0x0010) );
1417 pProtect
->setOption( ScTableProtection::INSERT_COLUMNS
, (nOptions
& 0x0020) );
1418 pProtect
->setOption( ScTableProtection::INSERT_ROWS
, (nOptions
& 0x0040) );
1419 pProtect
->setOption( ScTableProtection::INSERT_HYPERLINKS
, (nOptions
& 0x0080) );
1420 pProtect
->setOption( ScTableProtection::DELETE_COLUMNS
, (nOptions
& 0x0100) );
1421 pProtect
->setOption( ScTableProtection::DELETE_ROWS
, (nOptions
& 0x0200) );
1422 pProtect
->setOption( ScTableProtection::SELECT_LOCKED_CELLS
, (nOptions
& 0x0400) );
1423 pProtect
->setOption( ScTableProtection::SORT
, (nOptions
& 0x0800) );
1424 pProtect
->setOption( ScTableProtection::AUTOFILTER
, (nOptions
& 0x1000) );
1425 pProtect
->setOption( ScTableProtection::PIVOT_TABLES
, (nOptions
& 0x2000) );
1426 pProtect
->setOption( ScTableProtection::SELECT_UNLOCKED_CELLS
, (nOptions
& 0x4000) );
1428 // Enhanced protection containing editable ranges and permissions.
1429 pProtect
->setEnhancedProtection( std::vector(rSheet
.maEnhancedProtections
) );
1431 // all done. now commit.
1432 GetDoc().SetTabProtection(rTab
, pProtect
.get());
1436 XclImpSheetProtectBuffer::Sheet
* XclImpSheetProtectBuffer::GetSheetItem( SCTAB nTab
)
1438 ProtectedSheetMap::iterator itr
= maProtectedSheets
.find(nTab
);
1439 if (itr
== maProtectedSheets
.end())
1442 if ( !maProtectedSheets
.emplace( nTab
, Sheet() ).second
)
1445 itr
= maProtectedSheets
.find(nTab
);
1448 return &itr
->second
;
1451 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */