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>
21 #include <xicontent.hxx>
22 #include <sfx2/objsh.hxx>
23 #include <sfx2/docfile.hxx>
24 #include <tools/urlobj.hxx>
25 #include <sfx2/linkmgr.hxx>
26 #include <svl/itemset.hxx>
27 #include <scitems.hxx>
28 #include <editeng/eeitem.hxx>
29 #include <svl/intitem.hxx>
30 #include <svl/stritem.hxx>
31 #include <editeng/flditem.hxx>
32 #include <editeng/editobj.hxx>
33 #include <unotools/charclass.hxx>
34 #include <unotools/configmgr.hxx>
35 #include <stringutil.hxx>
36 #include <cellform.hxx>
37 #include <cellvalue.hxx>
38 #include <document.hxx>
39 #include <editutil.hxx>
40 #include <validat.hxx>
41 #include <patattr.hxx>
42 #include <docpool.hxx>
43 #include <rangenam.hxx>
44 #include <arealink.hxx>
45 #include <stlsheet.hxx>
46 #include <xlcontent.hxx>
47 #include <xlformula.hxx>
48 #include <xltracer.hxx>
49 #include <xistream.hxx>
50 #include <xihelper.hxx>
51 #include <xistyle.hxx>
52 #include <xiescher.hxx>
55 #include <excform.hxx>
56 #include <tabprotection.hxx>
57 #include <documentimport.hxx>
61 #include <oox/helper/helper.hxx>
62 #include <sal/log.hxx>
64 using ::com::sun::star::uno::Sequence
;
65 using ::std::unique_ptr
;
67 // Shared string table ========================================================
69 XclImpSst::XclImpSst( const XclImpRoot
& rRoot
) :
74 void XclImpSst::ReadSst( XclImpStream
& rStrm
)
77 sal_uInt32 nStrCount
= rStrm
.ReaduInt32();
78 auto nBytesAvailable
= rStrm
.GetRecLeft();
79 if (nStrCount
> nBytesAvailable
)
81 SAL_WARN("sc.filter", "xls claimed to have " << nStrCount
<< " strings, but only " << nBytesAvailable
<< " bytes available, truncating");
82 nStrCount
= nBytesAvailable
;
85 maStrings
.reserve(nStrCount
);
86 while( (nStrCount
> 0) && rStrm
.IsValid() )
89 aString
.Read( rStrm
);
90 maStrings
.push_back( aString
);
95 const XclImpString
* XclImpSst::GetString( sal_uInt32 nSstIndex
) const
97 return (nSstIndex
< maStrings
.size()) ? &maStrings
[ nSstIndex
] : nullptr;
100 // Hyperlinks =================================================================
104 /** Reads character array and stores it into rString.
105 @param nChars Number of following characters (not byte count!).
106 @param b16Bit true = 16-bit characters, false = 8-bit characters. */
107 void lclAppendString32( OUString
& rString
, XclImpStream
& rStrm
, sal_uInt32 nChars
, bool b16Bit
)
109 sal_uInt16 nReadChars
= ulimit_cast
< sal_uInt16
>( nChars
);
110 rString
+= rStrm
.ReadRawUniString( nReadChars
, b16Bit
);
111 // ignore remaining chars
112 std::size_t nIgnore
= nChars
- nReadChars
;
115 rStrm
.Ignore( nIgnore
);
118 /** Reads 32-bit string length and the character array and stores it into rString.
119 @param b16Bit true = 16-bit characters, false = 8-bit characters. */
120 void lclAppendString32( OUString
& rString
, XclImpStream
& rStrm
, bool b16Bit
)
122 lclAppendString32( rString
, rStrm
, rStrm
.ReaduInt32(), b16Bit
);
125 /** Reads 32-bit string length and ignores following 16-bit character array. */
126 void lclIgnoreString32( XclImpStream
& rStrm
)
128 sal_uInt32 nChars
= rStrm
.ReaduInt32();
130 rStrm
.Ignore( nChars
);
133 /** Converts a path to an absolute path.
134 @param rPath The source path. The resulting path is returned here.
135 @param nLevel Number of parent directories to add in front of the path. */
136 void lclGetAbsPath( OUString
& rPath
, sal_uInt16 nLevel
, const SfxObjectShell
* pDocShell
)
138 OUStringBuffer aTmpStr
;
141 aTmpStr
.append( "../" );
144 aTmpStr
.append( rPath
);
148 bool bWasAbs
= false;
149 rPath
= pDocShell
->GetMedium()->GetURLObject().smartRel2Abs( aTmpStr
.makeStringAndClear(), bWasAbs
).GetMainURL( INetURLObject::DecodeMechanism::NONE
);
150 // full path as stored in SvxURLField must be encoded
153 rPath
= aTmpStr
.makeStringAndClear();
156 /** Inserts the URL into a text cell. Does not modify value or formula cells. */
157 void lclInsertUrl( XclImpRoot
& rRoot
, const OUString
& rUrl
, SCCOL nScCol
, SCROW nScRow
, SCTAB nScTab
)
159 ScDocumentImport
& rDoc
= rRoot
.GetDocImport();
160 ScAddress
aScPos( nScCol
, nScRow
, nScTab
);
161 ScRefCellValue
aCell(rDoc
.getDoc(), aScPos
);
162 switch( aCell
.meType
)
164 // #i54261# hyperlinks in string cells
165 case CELLTYPE_STRING
:
168 sal_uInt32 nNumFmt
= rDoc
.getDoc().GetNumberFormat(rDoc
.getDoc().GetNonThreadedContext(), aScPos
);
169 SvNumberFormatter
* pFormatter
= rDoc
.getDoc().GetFormatTable();
171 OUString aDisplText
= ScCellFormat::GetString(aCell
, nNumFmt
, &pColor
, *pFormatter
, rDoc
.getDoc());
172 if (aDisplText
.isEmpty())
175 ScEditEngineDefaulter
& rEE
= rRoot
.GetEditEngine();
176 SvxURLField
aUrlField( rUrl
, aDisplText
, SvxURLFormat::AppDefault
);
178 if( aCell
.meType
== CELLTYPE_EDIT
)
180 const EditTextObject
* pEditObj
= aCell
.mpEditText
;
181 rEE
.SetTextCurrentDefaults( *pEditObj
);
182 rEE
.QuickInsertField( SvxFieldItem( aUrlField
, EE_FEATURE_FIELD
), ESelection( 0, 0, EE_PARA_ALL
, 0 ) );
186 rEE
.SetTextCurrentDefaults( OUString() );
187 rEE
.QuickInsertField( SvxFieldItem( aUrlField
, EE_FEATURE_FIELD
), ESelection() );
188 if( const ScPatternAttr
* pPattern
= rDoc
.getDoc().GetPattern( aScPos
.Col(), aScPos
.Row(), nScTab
) )
190 SfxItemSet
aItemSet( rEE
.GetEmptyItemSet() );
191 pPattern
->FillEditItemSet( &aItemSet
);
192 rEE
.QuickSetAttribs( aItemSet
, ESelection( 0, 0, EE_PARA_ALL
, 0 ) );
196 // The cell will own the text object instance.
197 rDoc
.setEditCell(aScPos
, rEE
.CreateTextObject());
202 // Handle other cell types e.g. formulas ( and ? ) that have associated
204 // Ideally all hyperlinks should be treated as below. For the moment,
205 // given the current absence of ods support lets just handle what we
206 // previously didn't handle the new way.
207 // Unfortunately we won't be able to preserve such hyperlinks when
208 // saving to ods. Note: when we are able to save such hyperlinks to ods
209 // we should handle *all* imported hyperlinks as below ( e.g. as cell
210 // attribute ) for better interoperability.
212 SfxStringItem
aItem( ATTR_HYPERLINK
, rUrl
);
213 rDoc
.getDoc().ApplyAttr(nScCol
, nScRow
, nScTab
, aItem
);
221 void XclImpHyperlink::ReadHlink( XclImpStream
& rStrm
)
223 XclRange
aXclRange( ScAddress::UNINITIALIZED
);
225 // #i80006# Excel silently ignores invalid hi-byte of column index (TODO: everywhere?)
226 aXclRange
.maFirst
.mnCol
&= 0xFF;
227 aXclRange
.maLast
.mnCol
&= 0xFF;
228 OUString aString
= ReadEmbeddedData( rStrm
);
229 if ( !aString
.isEmpty() )
230 rStrm
.GetRoot().GetXFRangeBuffer().SetHyperlink( aXclRange
, aString
);
233 OUString
XclImpHyperlink::ReadEmbeddedData( XclImpStream
& rStrm
)
235 const XclImpRoot
& rRoot
= rStrm
.GetRoot();
236 SfxObjectShell
* pDocShell
= rRoot
.GetDocShell();
238 OSL_ENSURE_BIFF( rRoot
.GetBiff() == EXC_BIFF8
);
243 sal_uInt32 nFlags
= rStrm
.ReaduInt32();
245 OSL_ENSURE( aGuid
== XclTools::maGuidStdLink
, "XclImpHyperlink::ReadEmbeddedData - unknown header GUID" );
247 ::std::unique_ptr
< OUString
> xLongName
; // link / file name
248 ::std::unique_ptr
< OUString
> xShortName
; // 8.3-representation of file name
249 ::std::unique_ptr
< OUString
> xTextMark
; // text mark
251 // description (ignore)
252 if( ::get_flag( nFlags
, EXC_HLINK_DESCR
) )
253 lclIgnoreString32( rStrm
);
254 // target frame (ignore) !! DESCR/FRAME - is this the right order? (never seen them together)
255 if( ::get_flag( nFlags
, EXC_HLINK_FRAME
) )
256 lclIgnoreString32( rStrm
);
258 // URL fields are zero-terminated - do not let the stream replace them
259 // in the lclAppendString32() with the '?' character.
260 rStrm
.SetNulSubstChar( '\0' );
263 if( ::get_flag( nFlags
, EXC_HLINK_UNC
) )
265 xLongName
.reset( new OUString
);
266 lclAppendString32( *xLongName
, rStrm
, true );
267 lclGetAbsPath( *xLongName
, 0, pDocShell
);
270 else if( ::get_flag( nFlags
, EXC_HLINK_BODY
) )
274 if( aGuid
== XclTools::maGuidFileMoniker
)
276 sal_uInt16 nLevel
= rStrm
.ReaduInt16(); // counter for level to climb down in path
277 xShortName
.reset( new OUString
);
278 lclAppendString32( *xShortName
, rStrm
, false );
281 sal_uInt32 nStrLen
= rStrm
.ReaduInt32();
284 nStrLen
= rStrm
.ReaduInt32();
285 nStrLen
/= 2; // it's byte count here...
287 xLongName
.reset( new OUString
);
288 lclAppendString32( *xLongName
, rStrm
, nStrLen
, true );
289 lclGetAbsPath( *xLongName
, nLevel
, pDocShell
);
292 lclGetAbsPath( *xShortName
, nLevel
, pDocShell
);
294 else if( aGuid
== XclTools::maGuidUrlMoniker
)
296 sal_uInt32 nStrLen
= rStrm
.ReaduInt32();
297 nStrLen
/= 2; // it's byte count here...
298 xLongName
.reset( new OUString
);
299 lclAppendString32( *xLongName
, rStrm
, nStrLen
, true );
300 if( !::get_flag( nFlags
, EXC_HLINK_ABS
) )
301 lclGetAbsPath( *xLongName
, 0, pDocShell
);
305 OSL_FAIL( "XclImpHyperlink::ReadEmbeddedData - unknown content GUID" );
310 if( ::get_flag( nFlags
, EXC_HLINK_MARK
) )
312 xTextMark
.reset( new OUString
);
313 lclAppendString32( *xTextMark
, rStrm
, true );
316 rStrm
.SetNulSubstChar(); // back to default
318 OSL_ENSURE( rStrm
.GetRecLeft() == 0, "XclImpHyperlink::ReadEmbeddedData - record size mismatch" );
320 if (!xLongName
&& xShortName
)
321 xLongName
= std::move(xShortName
);
322 else if (!xLongName
&& xTextMark
)
323 xLongName
.reset( new OUString
);
329 if( xLongName
->isEmpty() )
331 sal_Int32 nSepPos
= xTextMark
->lastIndexOf( '!' );
334 // Do not attempt to blindly convert '#SheetName!A1' to
335 // '#SheetName.A1', it can be #SheetName!R1C1 as well.
336 // Hyperlink handler has to handle all, but prefer
337 // '#SheetName.A1' if possible.
338 if (nSepPos
< xTextMark
->getLength() - 1)
340 ScDocument
& rDoc
= rRoot
.GetDoc();
342 if ((aRange
.ParseAny( xTextMark
->copy( nSepPos
+ 1 ), rDoc
, formula::FormulaGrammar::CONV_XL_R1C1
)
343 & ScRefFlags::VALID
) == ScRefFlags::ZERO
)
344 xTextMark
.reset( new OUString( xTextMark
->replaceAt( nSepPos
, 1, rtl::OUStringChar( '.' ))));
348 xLongName
.reset( new OUString( *xLongName
+ "#" + *xTextMark
) );
350 return( *xLongName
);
352 return( OUString() );
355 void XclImpHyperlink::ConvertToValidTabName(OUString
& rUrl
)
357 sal_Int32 n
= rUrl
.getLength();
359 // Needs at least 4 characters.
363 // the 1st character must be '#'.
366 OUStringBuffer
aNewUrl("#");
367 OUStringBuffer aTabName
;
369 bool bInQuote
= false;
370 bool bQuoteTabName
= false;
371 for( sal_Int32 i
= 1; i
< n
; ++i
)
373 sal_Unicode c
= rUrl
[i
];
376 if (bInQuote
&& i
+1 < n
&& rUrl
[i
+1] == '\'')
378 // Two consecutive single quotes ('') signify a single literal
379 // quite. When this occurs, the whole table name needs to be
381 bQuoteTabName
= true;
382 aTabName
.append(c
).append(c
);
387 bInQuote
= !bInQuote
;
388 if (!bInQuote
&& !aTabName
.isEmpty())
392 aNewUrl
.append(aTabName
);
404 // It should be outside the quotes!
407 // All is good. Pass the new URL.
408 rUrl
= aNewUrl
.makeStringAndClear();
411 void XclImpHyperlink::InsertUrl( XclImpRoot
& rRoot
, const XclRange
& rXclRange
, const OUString
& rUrl
)
414 ConvertToValidTabName(aUrl
);
416 SCTAB nScTab
= rRoot
.GetCurrScTab();
417 ScRange
aScRange( ScAddress::UNINITIALIZED
);
418 if( rRoot
.GetAddressConverter().ConvertRange( aScRange
, rXclRange
, nScTab
, nScTab
, true ) )
420 SCCOL nScCol1
, nScCol2
;
421 SCROW nScRow1
, nScRow2
;
422 aScRange
.GetVars( nScCol1
, nScRow1
, nScTab
, nScCol2
, nScRow2
, nScTab
);
424 if (utl::ConfigManager::IsFuzzing())
426 SCROW nRows
= nScRow2
- nScRow1
;
429 SAL_WARN("sc.filter", "for fuzzing performance, clamped hyperlink apply range end row from " << nScRow2
<< " to " << nScRow1
+ 1024);
430 nScRow2
= nScRow1
+ 1024;
434 for( SCCOL nScCol
= nScCol1
; nScCol
<= nScCol2
; ++nScCol
)
435 for( SCROW nScRow
= nScRow1
; nScRow
<= nScRow2
; ++nScRow
)
436 lclInsertUrl( rRoot
, aUrl
, nScCol
, nScRow
, nScTab
);
440 // Label ranges ===============================================================
442 void XclImpLabelranges::ReadLabelranges( XclImpStream
& rStrm
)
444 const XclImpRoot
& rRoot
= rStrm
.GetRoot();
445 OSL_ENSURE_BIFF( rRoot
.GetBiff() == EXC_BIFF8
);
447 ScDocument
& rDoc
= rRoot
.GetDoc();
448 SCTAB nScTab
= rRoot
.GetCurrScTab();
449 XclImpAddressConverter
& rAddrConv
= rRoot
.GetAddressConverter();
450 ScRangePairListRef xLabelRangesRef
;
452 XclRangeList aRowXclRanges
, aColXclRanges
;
453 rStrm
>> aRowXclRanges
>> aColXclRanges
;
456 ScRangeList aRowScRanges
;
457 rAddrConv
.ConvertRangeList( aRowScRanges
, aRowXclRanges
, nScTab
, false );
458 xLabelRangesRef
= rDoc
.GetRowNameRangesRef();
459 for ( size_t i
= 0, nRanges
= aRowScRanges
.size(); i
< nRanges
; ++i
)
461 const ScRange
& rScRange
= aRowScRanges
[ i
];
462 ScRange
aDataRange( rScRange
);
463 if( aDataRange
.aEnd
.Col() < rDoc
.MaxCol() )
465 aDataRange
.aStart
.SetCol( aDataRange
.aEnd
.Col() + 1 );
466 aDataRange
.aEnd
.SetCol( rDoc
.MaxCol() );
468 else if( aDataRange
.aStart
.Col() > 0 )
470 aDataRange
.aEnd
.SetCol( aDataRange
.aStart
.Col() - 1 );
471 aDataRange
.aStart
.SetCol( 0 );
473 xLabelRangesRef
->Append( ScRangePair( rScRange
, aDataRange
) );
476 // column label ranges
477 ScRangeList aColScRanges
;
478 rAddrConv
.ConvertRangeList( aColScRanges
, aColXclRanges
, nScTab
, false );
479 xLabelRangesRef
= rDoc
.GetColNameRangesRef();
481 for ( size_t i
= 0, nRanges
= aColScRanges
.size(); i
< nRanges
; ++i
)
483 const ScRange
& rScRange
= aColScRanges
[ i
];
484 ScRange
aDataRange( rScRange
);
485 if( aDataRange
.aEnd
.Row() < rDoc
.MaxRow() )
487 aDataRange
.aStart
.SetRow( aDataRange
.aEnd
.Row() + 1 );
488 aDataRange
.aEnd
.SetRow( rDoc
.MaxRow() );
490 else if( aDataRange
.aStart
.Row() > 0 )
492 aDataRange
.aEnd
.SetRow( aDataRange
.aStart
.Row() - 1 );
493 aDataRange
.aStart
.SetRow( 0 );
495 xLabelRangesRef
->Append( ScRangePair( rScRange
, aDataRange
) );
499 // Conditional formatting =====================================================
501 XclImpCondFormat::XclImpCondFormat( const XclImpRoot
& rRoot
, sal_uInt32 nFormatIndex
) :
503 mnFormatIndex( nFormatIndex
),
509 XclImpCondFormat::~XclImpCondFormat()
513 void XclImpCondFormat::ReadCondfmt( XclImpStream
& rStrm
)
515 OSL_ENSURE( !mnCondCount
, "XclImpCondFormat::ReadCondfmt - already initialized" );
516 XclRangeList aXclRanges
;
517 mnCondCount
= rStrm
.ReaduInt16();
520 GetAddressConverter().ConvertRangeList( maRanges
, aXclRanges
, GetCurrScTab(), true );
523 void XclImpCondFormat::ReadCF( XclImpStream
& rStrm
)
525 if( mnCondIndex
>= mnCondCount
)
527 OSL_FAIL( "XclImpCondFormat::ReadCF - CF without leading CONDFMT" );
531 // entire conditional format outside of valid range?
532 if( maRanges
.empty() )
535 sal_uInt8 nType
= rStrm
.ReaduInt8();
536 sal_uInt8 nOperator
= rStrm
.ReaduInt8();
537 sal_uInt16 nFmlaSize1
= rStrm
.ReaduInt16();
538 sal_uInt16 nFmlaSize2
= rStrm
.ReaduInt16();
539 sal_uInt32 nFlags
= rStrm
.ReaduInt32();
540 rStrm
.Ignore( 2 ); //nFlagsExtended
542 // *** mode and comparison operator ***
544 ScConditionMode eMode
= ScConditionMode::NONE
;
547 case EXC_CF_TYPE_CELL
:
551 case EXC_CF_CMP_BETWEEN
: eMode
= ScConditionMode::Between
; break;
552 case EXC_CF_CMP_NOT_BETWEEN
: eMode
= ScConditionMode::NotBetween
; break;
553 case EXC_CF_CMP_EQUAL
: eMode
= ScConditionMode::Equal
; break;
554 case EXC_CF_CMP_NOT_EQUAL
: eMode
= ScConditionMode::NotEqual
; break;
555 case EXC_CF_CMP_GREATER
: eMode
= ScConditionMode::Greater
; break;
556 case EXC_CF_CMP_LESS
: eMode
= ScConditionMode::Less
; break;
557 case EXC_CF_CMP_GREATER_EQUAL
: eMode
= ScConditionMode::EqGreater
; break;
558 case EXC_CF_CMP_LESS_EQUAL
: eMode
= ScConditionMode::EqLess
; break;
561 "sc.filter", "unknown CF comparison " << nOperator
);
566 case EXC_CF_TYPE_FMLA
:
567 eMode
= ScConditionMode::Direct
;
571 SAL_INFO("sc.filter", "unknown CF mode " << nType
);
575 // *** create style sheet ***
577 OUString
aStyleName( XclTools::GetCondFormatStyleName( GetCurrScTab(), mnFormatIndex
, mnCondIndex
) );
578 SfxItemSet
& rStyleItemSet
= ScfTools::MakeCellStyleSheet( GetStyleSheetPool(), aStyleName
, true ).GetItemSet();
580 const XclImpPalette
& rPalette
= GetPalette();
584 if( get_flag( nFlags
, EXC_CF_BLOCK_NUMFMT
) )
586 XclImpNumFmtBuffer
& rNumFmtBuffer
= GetRoot().GetNumFmtBuffer();
587 bool bIFmt
= get_flag( nFlags
, EXC_CF_IFMT_USER
);
588 sal_uInt16 nFormat
= rNumFmtBuffer
.ReadCFFormat( rStrm
, bIFmt
);
589 rNumFmtBuffer
.FillToItemSet( rStyleItemSet
, nFormat
);
592 // *** font block ***
594 if( ::get_flag( nFlags
, EXC_CF_BLOCK_FONT
) )
596 XclImpFont
aFont( GetRoot() );
597 aFont
.ReadCFFontBlock( rStrm
);
598 aFont
.FillToItemSet( rStyleItemSet
, XclFontItemType::Cell
);
602 if( get_flag( nFlags
, EXC_CF_BLOCK_ALIGNMENT
) )
604 XclImpCellAlign aAlign
;
605 sal_uInt16
nAlign(0);
606 sal_uInt16
nAlignMisc(0);
607 nAlign
= rStrm
.ReaduInt16();
608 nAlignMisc
= rStrm
.ReaduInt16();
609 aAlign
.FillFromCF( nAlign
, nAlignMisc
);
610 aAlign
.FillToItemSet( rStyleItemSet
, nullptr );
614 // *** border block ***
616 if( ::get_flag( nFlags
, EXC_CF_BLOCK_BORDER
) )
618 sal_uInt16
nLineStyle(0);
619 sal_uInt32
nLineColor(0);
620 nLineStyle
= rStrm
.ReaduInt16();
621 nLineColor
= rStrm
.ReaduInt32();
624 XclImpCellBorder aBorder
;
625 aBorder
.FillFromCF8( nLineStyle
, nLineColor
, nFlags
);
626 aBorder
.FillToItemSet( rStyleItemSet
, rPalette
);
629 // *** pattern block ***
631 if( ::get_flag( nFlags
, EXC_CF_BLOCK_AREA
) )
633 sal_uInt16
nPattern(0), nColor(0);
634 nPattern
= rStrm
.ReaduInt16();
635 nColor
= rStrm
.ReaduInt16();
637 XclImpCellArea aArea
;
638 aArea
.FillFromCF8( nPattern
, nColor
, nFlags
);
639 aArea
.FillToItemSet( rStyleItemSet
, rPalette
);
642 if( get_flag( nFlags
, EXC_CF_BLOCK_PROTECTION
) )
644 sal_uInt16 nCellProt
;
645 nCellProt
= rStrm
.ReaduInt16();
646 XclImpCellProt aCellProt
;
647 aCellProt
.FillFromXF3(nCellProt
);
648 aCellProt
.FillToItemSet( rStyleItemSet
);
653 const ScAddress
& rPos
= maRanges
.front().aStart
; // assured above that maRanges is not empty
654 ExcelToSc
& rFmlaConv
= GetOldFmlaConverter();
656 ::std::unique_ptr
< ScTokenArray
> xTokArr1
;
659 std::unique_ptr
<ScTokenArray
> pTokArr
;
660 rFmlaConv
.Reset( rPos
);
661 rFmlaConv
.Convert( pTokArr
, rStrm
, nFmlaSize1
, false, FT_CondFormat
);
662 // formula converter owns pTokArr -> create a copy of the token array
665 xTokArr1
= std::move( pTokArr
);
666 GetDoc().CheckLinkFormulaNeedingCheck( *xTokArr1
);
670 ::std::unique_ptr
< ScTokenArray
> xTokArr2
;
673 std::unique_ptr
<ScTokenArray
> pTokArr
;
674 rFmlaConv
.Reset( rPos
);
675 rFmlaConv
.Convert( pTokArr
, rStrm
, nFmlaSize2
, false, FT_CondFormat
);
676 // formula converter owns pTokArr -> create a copy of the token array
679 xTokArr2
= std::move( pTokArr
);
680 GetDoc().CheckLinkFormulaNeedingCheck( *xTokArr2
);
684 // *** create the Calc conditional formatting ***
686 const ScAddress
aPos(rPos
); //in case maRanges.Join invalidates it
690 mxScCondFmt
.reset( new ScConditionalFormat( 0/*nKey*/, &GetDoc() ) );
691 if(maRanges
.size() > 1)
692 maRanges
.Join(maRanges
[0], true);
693 mxScCondFmt
->SetRange(maRanges
);
696 ScCondFormatEntry
* pEntry
= new ScCondFormatEntry(eMode
, xTokArr1
.get(), xTokArr2
.get(), GetDoc(), aPos
, aStyleName
);
697 mxScCondFmt
->AddEntry( pEntry
);
701 void XclImpCondFormat::Apply()
705 ScDocument
& rDoc
= GetDoc();
707 SCTAB nTab
= maRanges
.front().aStart
.Tab();
708 sal_uLong nKey
= rDoc
.AddCondFormat( mxScCondFmt
->Clone(), nTab
);
710 rDoc
.AddCondFormatData( maRanges
, nTab
, nKey
);
714 XclImpCondFormatManager::XclImpCondFormatManager( const XclImpRoot
& rRoot
) :
719 void XclImpCondFormatManager::ReadCondfmt( XclImpStream
& rStrm
)
721 XclImpCondFormat
* pFmt
= new XclImpCondFormat( GetRoot(), maCondFmtList
.size() );
722 pFmt
->ReadCondfmt( rStrm
);
723 maCondFmtList
.push_back( std::unique_ptr
<XclImpCondFormat
>(pFmt
) );
726 void XclImpCondFormatManager::ReadCF( XclImpStream
& rStrm
)
728 OSL_ENSURE( !maCondFmtList
.empty(), "XclImpCondFormatManager::ReadCF - CF without leading CONDFMT" );
729 if( !maCondFmtList
.empty() )
730 maCondFmtList
.back()->ReadCF( rStrm
);
733 void XclImpCondFormatManager::Apply()
735 for( auto& rxFmt
: maCondFmtList
)
737 maCondFmtList
.clear();
740 // Data Validation ============================================================
742 XclImpValidationManager::DVItem::DVItem( const ScRangeList
& rRanges
, const ScValidationData
& rValidData
) :
743 maRanges(rRanges
), maValidData(rValidData
) {}
745 XclImpValidationManager::XclImpValidationManager( const XclImpRoot
& rRoot
) :
750 void XclImpValidationManager::ReadDval( XclImpStream
& rStrm
)
752 const XclImpRoot
& rRoot
= rStrm
.GetRoot();
753 OSL_ENSURE_BIFF( rRoot
.GetBiff() == EXC_BIFF8
);
755 sal_uInt32
nObjId(0);
757 nObjId
= rStrm
.ReaduInt32();
758 if( nObjId
!= EXC_DVAL_NOOBJ
)
760 OSL_ENSURE( nObjId
<= 0xFFFF, "XclImpValidation::ReadDval - invalid object ID" );
761 rRoot
.GetCurrSheetDrawing().SetSkipObj( static_cast< sal_uInt16
>( nObjId
) );
765 void XclImpValidationManager::ReadDV( XclImpStream
& rStrm
)
767 const XclImpRoot
& rRoot
= rStrm
.GetRoot();
768 OSL_ENSURE_BIFF( rRoot
.GetBiff() == EXC_BIFF8
);
770 ScDocument
& rDoc
= rRoot
.GetDoc();
771 SCTAB nScTab
= rRoot
.GetCurrScTab();
772 ExcelToSc
& rFmlaConv
= rRoot
.GetOldFmlaConverter();
775 sal_uInt32 nFlags
= rStrm
.ReaduInt32();
778 /* Empty strings are single NUL characters in Excel (string length is 1).
779 -> Do not let the stream replace them with '?' characters. */
780 rStrm
.SetNulSubstChar( '\0' );
781 OUString
aPromptTitle( rStrm
.ReadUniString() );
782 OUString
aErrorTitle( rStrm
.ReadUniString() );
783 OUString
aPromptMessage( rStrm
.ReadUniString() );
784 OUString
aErrorMessage( rStrm
.ReadUniString() );
785 rStrm
.SetNulSubstChar(); // back to default
788 if ( rStrm
.GetRecLeft() <= 8 )
789 // Not enough bytes left in the record. Bail out.
793 // string list is single tStr token with NUL separators -> replace them with LF
794 rStrm
.SetNulSubstChar( '\n' );
795 ::std::unique_ptr
< ScTokenArray
> xTokArr1
;
797 // We can't import the formula directly because we need the range
798 sal_uInt16 nLenFormula1
= rStrm
.ReaduInt16();
800 XclImpStreamPos aPosFormula1
;
801 rStrm
.StorePosition(aPosFormula1
);
802 rStrm
.Ignore(nLenFormula1
);
805 ::std::unique_ptr
< ScTokenArray
> xTokArr2
;
807 sal_uInt16 nLenFormula2
= rStrm
.ReaduInt16();
809 XclImpStreamPos aPosFormula2
;
810 rStrm
.StorePosition(aPosFormula2
);
811 rStrm
.Ignore(nLenFormula2
);
813 // read all cell ranges
814 XclRangeList aXclRanges
;
817 // convert to Calc range list
818 ScRangeList aScRanges
;
819 rRoot
.GetAddressConverter().ConvertRangeList( aScRanges
, aXclRanges
, nScTab
, true );
821 // only continue if there are valid ranges
822 if ( aScRanges
.empty() )
825 ScRange aCombinedRange
= aScRanges
.Combine();
827 XclImpStreamPos aCurrentPos
;
828 rStrm
.StorePosition(aCurrentPos
);
829 rStrm
.RestorePosition(aPosFormula1
);
830 if( nLenFormula1
> 0 )
832 std::unique_ptr
<ScTokenArray
> pTokArr
;
833 rFmlaConv
.Reset(aCombinedRange
.aStart
);
834 rFmlaConv
.Convert( pTokArr
, rStrm
, nLenFormula1
, false, FT_CondFormat
);
835 // formula converter owns pTokArr -> create a copy of the token array
837 xTokArr1
= std::move( pTokArr
);
839 rStrm
.SetNulSubstChar(); // back to default
840 if (nLenFormula2
> 0)
842 rStrm
.RestorePosition(aPosFormula2
);
843 std::unique_ptr
<ScTokenArray
> pTokArr
;
844 rFmlaConv
.Reset(aCombinedRange
.aStart
);
845 rFmlaConv
.Convert( pTokArr
, rStrm
, nLenFormula2
, false, FT_CondFormat
);
846 // formula converter owns pTokArr -> create a copy of the token array
848 xTokArr2
= std::move( pTokArr
);
851 rStrm
.RestorePosition(aCurrentPos
);
853 bool bIsValid
= true; // valid settings in flags field
855 ScValidationMode eValMode
= SC_VALID_ANY
;
856 switch( nFlags
& EXC_DV_MODE_MASK
)
858 case EXC_DV_MODE_ANY
: eValMode
= SC_VALID_ANY
; break;
859 case EXC_DV_MODE_WHOLE
: eValMode
= SC_VALID_WHOLE
; break;
860 case EXC_DV_MODE_DECIMAL
: eValMode
= SC_VALID_DECIMAL
; break;
861 case EXC_DV_MODE_LIST
: eValMode
= SC_VALID_LIST
; break;
862 case EXC_DV_MODE_DATE
: eValMode
= SC_VALID_DATE
; break;
863 case EXC_DV_MODE_TIME
: eValMode
= SC_VALID_TIME
; break;
864 case EXC_DV_MODE_TEXTLEN
: eValMode
= SC_VALID_TEXTLEN
; break;
865 case EXC_DV_MODE_CUSTOM
: eValMode
= SC_VALID_CUSTOM
; break;
866 default: bIsValid
= false;
868 rRoot
.GetTracer().TraceDVType(eValMode
== SC_VALID_CUSTOM
);
870 ScConditionMode eCondMode
= ScConditionMode::Between
;
871 switch( nFlags
& EXC_DV_COND_MASK
)
873 case EXC_DV_COND_BETWEEN
: eCondMode
= ScConditionMode::Between
; break;
874 case EXC_DV_COND_NOTBETWEEN
:eCondMode
= ScConditionMode::NotBetween
; break;
875 case EXC_DV_COND_EQUAL
: eCondMode
= ScConditionMode::Equal
; break;
876 case EXC_DV_COND_NOTEQUAL
: eCondMode
= ScConditionMode::NotEqual
; break;
877 case EXC_DV_COND_GREATER
: eCondMode
= ScConditionMode::Greater
; break;
878 case EXC_DV_COND_LESS
: eCondMode
= ScConditionMode::Less
; break;
879 case EXC_DV_COND_EQGREATER
: eCondMode
= ScConditionMode::EqGreater
; break;
880 case EXC_DV_COND_EQLESS
: eCondMode
= ScConditionMode::EqLess
; break;
881 default: bIsValid
= false;
885 // No valid validation found. Bail out.
888 // The default value for comparison is _BETWEEN. However, custom
889 // rules are a formula, and thus the comparator should be ignored
890 // and only a true or false from the formula is evaluated. In Calc,
891 // formulas use comparison SC_COND_DIRECT.
892 if( eValMode
== SC_VALID_CUSTOM
)
894 eCondMode
= ScConditionMode::Direct
;
897 // first range for base address for relative references
898 const ScRange
& rScRange
= aScRanges
.front(); // aScRanges is not empty
900 // process string list of a list validity (convert to list of string tokens)
901 if( xTokArr1
&& (eValMode
== SC_VALID_LIST
) && ::get_flag( nFlags
, EXC_DV_STRINGLIST
) )
902 XclTokenArrayHelper::ConvertStringToList(*xTokArr1
, rDoc
.GetSharedStringPool(), '\n');
905 std::make_unique
<DVItem
>(aScRanges
, ScValidationData(eValMode
, eCondMode
, xTokArr1
.get(), xTokArr2
.get(), rDoc
, rScRange
.aStart
)));
906 DVItem
& rItem
= *maDVItems
.back();
908 rItem
.maValidData
.SetIgnoreBlank( ::get_flag( nFlags
, EXC_DV_IGNOREBLANK
) );
909 rItem
.maValidData
.SetListType( ::get_flagvalue( nFlags
, EXC_DV_SUPPRESSDROPDOWN
, css::sheet::TableValidationVisibility::INVISIBLE
, css::sheet::TableValidationVisibility::UNSORTED
) );
911 // *** prompt box ***
912 if( !aPromptTitle
.isEmpty() || !aPromptMessage
.isEmpty() )
914 // set any text stored in the record
915 rItem
.maValidData
.SetInput( aPromptTitle
, aPromptMessage
);
916 if( !::get_flag( nFlags
, EXC_DV_SHOWPROMPT
) )
917 rItem
.maValidData
.ResetInput();
921 ScValidErrorStyle eErrStyle
= SC_VALERR_STOP
;
922 switch( nFlags
& EXC_DV_ERROR_MASK
)
924 case EXC_DV_ERROR_WARNING
: eErrStyle
= SC_VALERR_WARNING
; break;
925 case EXC_DV_ERROR_INFO
: eErrStyle
= SC_VALERR_INFO
; break;
927 // set texts and error style
928 rItem
.maValidData
.SetError( aErrorTitle
, aErrorMessage
, eErrStyle
);
929 if( !::get_flag( nFlags
, EXC_DV_SHOWERROR
) )
930 rItem
.maValidData
.ResetError();
933 void XclImpValidationManager::Apply()
935 ScDocument
& rDoc
= GetRoot().GetDoc();
936 for (const auto& rxDVItem
: maDVItems
)
938 DVItem
& rItem
= *rxDVItem
;
940 sal_uLong nHandle
= rDoc
.AddValidationEntry( rItem
.maValidData
);
941 ScPatternAttr
aPattern( rDoc
.GetPool() );
942 aPattern
.GetItemSet().Put( SfxUInt32Item( ATTR_VALIDDATA
, nHandle
) );
945 for ( size_t i
= 0, nRanges
= rItem
.maRanges
.size(); i
< nRanges
; ++i
)
947 const ScRange
& rScRange
= rItem
.maRanges
[ i
];
948 rDoc
.ApplyPatternAreaTab( rScRange
.aStart
.Col(), rScRange
.aStart
.Row(),
949 rScRange
.aEnd
.Col(), rScRange
.aEnd
.Row(), rScRange
.aStart
.Tab(), aPattern
);
955 // Web queries ================================================================
957 XclImpWebQuery::XclImpWebQuery( const ScRange
& rDestRange
) :
958 maDestRange( rDestRange
),
959 meMode( xlWQUnknown
),
964 void XclImpWebQuery::ReadParamqry( XclImpStream
& rStrm
)
966 sal_uInt16 nFlags
= rStrm
.ReaduInt16();
967 sal_uInt16 nType
= ::extract_value
< sal_uInt16
>( nFlags
, 0, 3 );
968 if( !((nType
== EXC_PQRYTYPE_WEBQUERY
) && ::get_flag( nFlags
, EXC_PQRY_WEBQUERY
)) )
971 if( ::get_flag( nFlags
, EXC_PQRY_TABLES
) )
973 meMode
= xlWQAllTables
;
974 maTables
= ScfTools::GetHTMLTablesName();
978 meMode
= xlWQDocument
;
979 maTables
= ScfTools::GetHTMLDocName();
983 void XclImpWebQuery::ReadWqstring( XclImpStream
& rStrm
)
985 maURL
= rStrm
.ReadUniString();
988 void XclImpWebQuery::ReadWqsettings( XclImpStream
& rStrm
)
991 sal_uInt16 nFlags
= rStrm
.ReaduInt16();
993 mnRefresh
= rStrm
.ReaduInt16();
995 if( ::get_flag( nFlags
, EXC_WQSETT_SPECTABLES
) && (meMode
== xlWQAllTables
) )
996 meMode
= xlWQSpecTables
;
999 void XclImpWebQuery::ReadWqtables( XclImpStream
& rStrm
)
1001 if( meMode
!= xlWQSpecTables
)
1005 OUString
aTables( rStrm
.ReadUniString() );
1007 const sal_Unicode cSep
= ';';
1008 static const OUStringLiteral
aQuotedPairs( u
"\"\"" );
1010 for ( sal_Int32 nStringIx
{aTables
.isEmpty() ? -1 : 0}; nStringIx
>=0; )
1012 OUString
aToken( ScStringUtil::GetQuotedToken( aTables
, 0, aQuotedPairs
, ',', nStringIx
) );
1013 sal_Int32 nTabNum
= CharClass::isAsciiNumeric( aToken
) ? aToken
.toInt32() : 0;
1015 maTables
= ScGlobal::addToken( maTables
, ScfTools::GetNameFromHTMLIndex( static_cast< sal_uInt32
>( nTabNum
) ), cSep
);
1018 ScGlobal::EraseQuotes( aToken
, '"', false );
1019 if( !aToken
.isEmpty() )
1020 maTables
= ScGlobal::addToken( maTables
, ScfTools::GetNameFromHTMLName( aToken
), cSep
);
1025 void XclImpWebQuery::Apply( ScDocument
& rDoc
, const OUString
& rFilterName
)
1027 if( !maURL
.isEmpty() && (meMode
!= xlWQUnknown
) && rDoc
.GetDocumentShell() )
1029 ScAreaLink
* pLink
= new ScAreaLink( rDoc
.GetDocumentShell(),
1030 maURL
, rFilterName
, OUString(), maTables
, maDestRange
, mnRefresh
* 60UL );
1031 rDoc
.GetLinkManager()->InsertFileLink( *pLink
, sfx2::SvBaseLinkObjectType::ClientFile
,
1032 maURL
, &rFilterName
, &maTables
);
1036 XclImpWebQueryBuffer::XclImpWebQueryBuffer( const XclImpRoot
& rRoot
) :
1041 void XclImpWebQueryBuffer::ReadQsi( XclImpStream
& rStrm
)
1043 if( GetBiff() == EXC_BIFF8
)
1046 OUString
aXclName( rStrm
.ReadUniString() );
1048 // #i64794# Excel replaces spaces with underscores
1049 aXclName
= aXclName
.replaceAll( " ", "_" );
1051 // find the defined name used in Calc
1052 if( const XclImpName
* pName
= GetNameManager().FindName( aXclName
, GetCurrScTab() ) )
1054 if( const ScRangeData
* pRangeData
= pName
->GetScRangeData() )
1057 if( pRangeData
->IsReference( aRange
) )
1058 maWQList
.emplace_back( aRange
);
1068 void XclImpWebQueryBuffer::ReadParamqry( XclImpStream
& rStrm
)
1070 if (!maWQList
.empty())
1071 maWQList
.back().ReadParamqry( rStrm
);
1074 void XclImpWebQueryBuffer::ReadWqstring( XclImpStream
& rStrm
)
1076 if (!maWQList
.empty())
1077 maWQList
.back().ReadWqstring( rStrm
);
1080 void XclImpWebQueryBuffer::ReadWqsettings( XclImpStream
& rStrm
)
1082 if (!maWQList
.empty())
1083 maWQList
.back().ReadWqsettings( rStrm
);
1086 void XclImpWebQueryBuffer::ReadWqtables( XclImpStream
& rStrm
)
1088 if (!maWQList
.empty())
1089 maWQList
.back().ReadWqtables( rStrm
);
1092 void XclImpWebQueryBuffer::Apply()
1094 ScDocument
& rDoc
= GetDoc();
1095 for( auto& rQuery
: maWQList
)
1096 rQuery
.Apply( rDoc
, EXC_WEBQRY_FILTER
);
1099 // Decryption =================================================================
1103 XclImpDecrypterRef
lclReadFilepass5( XclImpStream
& rStrm
)
1105 XclImpDecrypterRef xDecr
;
1106 OSL_ENSURE( rStrm
.GetRecLeft() == 4, "lclReadFilepass5 - wrong record size" );
1107 if( rStrm
.GetRecLeft() == 4 )
1109 sal_uInt16
nKey(0), nHash(0);
1110 nKey
= rStrm
.ReaduInt16();
1111 nHash
= rStrm
.ReaduInt16();
1112 xDecr
= std::make_shared
<XclImpBiff5Decrypter
>( nKey
, nHash
);
1117 XclImpDecrypterRef
lclReadFilepass8_Standard( XclImpStream
& rStrm
)
1119 XclImpDecrypterRef xDecr
;
1120 OSL_ENSURE( rStrm
.GetRecLeft() == 48, "lclReadFilepass8 - wrong record size" );
1121 if( rStrm
.GetRecLeft() == 48 )
1123 std::vector
<sal_uInt8
> aSalt(16);
1124 std::vector
<sal_uInt8
> aVerifier(16);
1125 std::vector
<sal_uInt8
> aVerifierHash(16);
1126 rStrm
.Read(aSalt
.data(), 16);
1127 rStrm
.Read(aVerifier
.data(), 16);
1128 rStrm
.Read(aVerifierHash
.data(), 16);
1129 xDecr
= std::make_shared
<XclImpBiff8StdDecrypter
>(std::move(aSalt
), std::move(aVerifier
), std::move(aVerifierHash
));
1134 XclImpDecrypterRef
lclReadFilepass8_Strong(XclImpStream
& rStream
)
1136 //It is possible there are other variants in existence but these
1137 //are the defaults I get with Excel 2013
1138 XclImpDecrypterRef xDecr
;
1140 msfilter::RC4EncryptionInfo info
;
1142 info
.header
.flags
= rStream
.ReaduInt32();
1143 if (oox::getFlag( info
.header
.flags
, msfilter::ENCRYPTINFO_EXTERNAL
))
1146 sal_uInt32 nHeaderSize
= rStream
.ReaduInt32();
1147 sal_uInt32 actualHeaderSize
= sizeof(info
.header
);
1149 if( nHeaderSize
< actualHeaderSize
)
1152 info
.header
.flags
= rStream
.ReaduInt32();
1153 info
.header
.sizeExtra
= rStream
.ReaduInt32();
1154 info
.header
.algId
= rStream
.ReaduInt32();
1155 info
.header
.algIdHash
= rStream
.ReaduInt32();
1156 info
.header
.keyBits
= rStream
.ReaduInt32();
1157 info
.header
.providedType
= rStream
.ReaduInt32();
1158 info
.header
.reserved1
= rStream
.ReaduInt32();
1159 info
.header
.reserved2
= rStream
.ReaduInt32();
1161 rStream
.Ignore(nHeaderSize
- actualHeaderSize
);
1163 info
.verifier
.saltSize
= rStream
.ReaduInt32();
1164 if (info
.verifier
.saltSize
!= msfilter::SALT_LENGTH
)
1166 rStream
.Read(&info
.verifier
.salt
, sizeof(info
.verifier
.salt
));
1167 rStream
.Read(&info
.verifier
.encryptedVerifier
, sizeof(info
.verifier
.encryptedVerifier
));
1169 info
.verifier
.encryptedVerifierHashSize
= rStream
.ReaduInt32();
1170 if (info
.verifier
.encryptedVerifierHashSize
!= RTL_DIGEST_LENGTH_SHA1
)
1172 rStream
.Read(&info
.verifier
.encryptedVerifierHash
, info
.verifier
.encryptedVerifierHashSize
);
1174 // check flags and algorithm IDs, required are AES128 and SHA-1
1175 if (!oox::getFlag(info
.header
.flags
, msfilter::ENCRYPTINFO_CRYPTOAPI
))
1178 if (oox::getFlag(info
.header
.flags
, msfilter::ENCRYPTINFO_AES
))
1181 if (info
.header
.algId
!= msfilter::ENCRYPT_ALGO_RC4
)
1184 // hash algorithm ID 0 defaults to SHA-1 too
1185 if (info
.header
.algIdHash
!= 0 && info
.header
.algIdHash
!= msfilter::ENCRYPT_HASH_SHA1
)
1188 xDecr
= std::make_shared
<XclImpBiff8CryptoAPIDecrypter
>(
1189 std::vector
<sal_uInt8
>(info
.verifier
.salt
,
1190 info
.verifier
.salt
+ SAL_N_ELEMENTS(info
.verifier
.salt
)),
1191 std::vector
<sal_uInt8
>(info
.verifier
.encryptedVerifier
,
1192 info
.verifier
.encryptedVerifier
+ SAL_N_ELEMENTS(info
.verifier
.encryptedVerifier
)),
1193 std::vector
<sal_uInt8
>(info
.verifier
.encryptedVerifierHash
,
1194 info
.verifier
.encryptedVerifierHash
+ SAL_N_ELEMENTS(info
.verifier
.encryptedVerifierHash
)));
1199 XclImpDecrypterRef
lclReadFilepass8( XclImpStream
& rStrm
)
1201 XclImpDecrypterRef xDecr
;
1203 sal_uInt16 nMode
= rStrm
.ReaduInt16();
1206 case EXC_FILEPASS_BIFF5
:
1207 xDecr
= lclReadFilepass5( rStrm
);
1210 case EXC_FILEPASS_BIFF8
:
1212 sal_uInt32 nVersion
= rStrm
.ReaduInt32();
1213 if (nVersion
== msfilter::VERSION_INFO_1997_FORMAT
)
1215 //A Version structure where Version.vMajor MUST be 0x0001,
1216 //and Version.vMinor MUST be 0x0001.
1217 xDecr
= lclReadFilepass8_Standard(rStrm
);
1219 else if (nVersion
== msfilter::VERSION_INFO_2007_FORMAT
||
1220 nVersion
== msfilter::VERSION_INFO_2007_FORMAT_SP2
)
1222 //Version.vMajor MUST be 0x0002, 0x0003 or 0x0004 and
1223 //Version.vMinor MUST be 0x0002.
1224 xDecr
= lclReadFilepass8_Strong(rStrm
);
1227 OSL_FAIL("lclReadFilepass8 - unknown BIFF8 encryption sub mode");
1232 OSL_FAIL( "lclReadFilepass8 - unknown encryption mode" );
1240 const ErrCode
& XclImpDecryptHelper::ReadFilepass( XclImpStream
& rStrm
)
1242 XclImpDecrypterRef xDecr
;
1243 rStrm
.DisableDecryption();
1245 // read the FILEPASS record and create a new decrypter object
1246 switch( rStrm
.GetRoot().GetBiff() )
1251 case EXC_BIFF5
: xDecr
= lclReadFilepass5( rStrm
); break;
1252 case EXC_BIFF8
: xDecr
= lclReadFilepass8( rStrm
); break;
1253 default: DBG_ERROR_BIFF();
1256 // set decrypter at import stream
1257 rStrm
.SetDecrypter( xDecr
);
1259 // request and verify a password (decrypter implements IDocPasswordVerifier)
1261 rStrm
.GetRoot().RequestEncryptionData( *xDecr
);
1263 // return error code (success, wrong password, etc.)
1264 return xDecr
? xDecr
->GetError() : EXC_ENCR_ERROR_UNSUPP_CRYPT
;
1267 // Document protection ========================================================
1269 XclImpDocProtectBuffer::XclImpDocProtectBuffer( const XclImpRoot
& rRoot
) :
1270 XclImpRoot( rRoot
),
1272 mbDocProtect(false),
1277 void XclImpDocProtectBuffer::ReadDocProtect( XclImpStream
& rStrm
)
1279 mbDocProtect
= rStrm
.ReaduInt16() != 0;
1282 void XclImpDocProtectBuffer::ReadWinProtect( XclImpStream
& rStrm
)
1284 mbWinProtect
= rStrm
.ReaduInt16() != 0;
1287 void XclImpDocProtectBuffer::ReadPasswordHash( XclImpStream
& rStrm
)
1289 rStrm
.EnableDecryption();
1290 mnPassHash
= rStrm
.ReaduInt16();
1293 void XclImpDocProtectBuffer::Apply() const
1295 if (!mbDocProtect
&& !mbWinProtect
)
1296 // Excel requires either the structure or windows protection is set.
1297 // If neither is set then the document is not protected at all.
1300 unique_ptr
<ScDocProtection
> pProtect(new ScDocProtection
);
1301 pProtect
->setProtected(true);
1305 // 16-bit password hash.
1306 Sequence
<sal_Int8
> aPass
{sal_Int8(mnPassHash
>> 8), sal_Int8(mnPassHash
& 0xFF)};
1307 pProtect
->setPasswordHash(aPass
, PASSHASH_XL
);
1310 // document protection options
1311 pProtect
->setOption(ScDocProtection::STRUCTURE
, mbDocProtect
);
1312 pProtect
->setOption(ScDocProtection::WINDOWS
, mbWinProtect
);
1314 GetDoc().SetDocProtection(pProtect
.get());
1317 // Sheet Protection ===========================================================
1319 XclImpSheetProtectBuffer::Sheet::Sheet() :
1321 mnPasswordHash(0x0000),
1326 XclImpSheetProtectBuffer::Sheet::Sheet(const Sheet
& r
) :
1327 mbProtected(r
.mbProtected
),
1328 mnPasswordHash(r
.mnPasswordHash
),
1329 mnOptions(r
.mnOptions
)
1333 XclImpSheetProtectBuffer::XclImpSheetProtectBuffer( const XclImpRoot
& rRoot
) :
1338 void XclImpSheetProtectBuffer::ReadProtect( XclImpStream
& rStrm
, SCTAB nTab
)
1340 if ( rStrm
.ReaduInt16() )
1342 Sheet
* pSheet
= GetSheetItem(nTab
);
1344 pSheet
->mbProtected
= true;
1348 void XclImpSheetProtectBuffer::ReadOptions( XclImpStream
& rStrm
, SCTAB nTab
)
1350 // The flag size specifies the size of bytes that follows that stores
1351 // feature data. If -1 it depends on the feature type imported earlier.
1352 // For enhanced protection data, the size is always 4. For the most xls
1353 // documents out there this value is almost always -1.
1354 sal_Int32 nFlagSize
= rStrm
.ReadInt32();
1355 if (nFlagSize
!= -1)
1358 // There are actually 4 bytes to read, but the upper 2 bytes currently
1359 // don't store any bits.
1360 sal_uInt16 nOptions
= rStrm
.ReaduInt16();
1362 Sheet
* pSheet
= GetSheetItem(nTab
);
1364 pSheet
->mnOptions
= nOptions
;
1367 void XclImpSheetProtectBuffer::AppendEnhancedProtection( const ScEnhancedProtection
& rProt
, SCTAB nTab
)
1369 Sheet
* pSheet
= GetSheetItem(nTab
);
1371 pSheet
->maEnhancedProtections
.push_back( rProt
);
1374 void XclImpSheetProtectBuffer::ReadPasswordHash( XclImpStream
& rStrm
, SCTAB nTab
)
1376 sal_uInt16 nHash
= rStrm
.ReaduInt16();
1377 Sheet
* pSheet
= GetSheetItem(nTab
);
1379 pSheet
->mnPasswordHash
= nHash
;
1382 void XclImpSheetProtectBuffer::Apply() const
1384 for (const auto& [rTab
, rSheet
] : maProtectedSheets
)
1386 if (!rSheet
.mbProtected
)
1387 // This sheet is (for whatever reason) not protected.
1390 unique_ptr
<ScTableProtection
> pProtect(new ScTableProtection
);
1391 pProtect
->setProtected(true);
1393 // 16-bit hash password
1394 const sal_uInt16 nHash
= rSheet
.mnPasswordHash
;
1397 Sequence
<sal_Int8
> aPass
{sal_Int8(nHash
>> 8), sal_Int8(nHash
& 0xFF)};
1398 pProtect
->setPasswordHash(aPass
, PASSHASH_XL
);
1401 // sheet protection options
1402 const sal_uInt16 nOptions
= rSheet
.mnOptions
;
1403 pProtect
->setOption( ScTableProtection::OBJECTS
, (nOptions
& 0x0001) );
1404 pProtect
->setOption( ScTableProtection::SCENARIOS
, (nOptions
& 0x0002) );
1405 pProtect
->setOption( ScTableProtection::FORMAT_CELLS
, (nOptions
& 0x0004) );
1406 pProtect
->setOption( ScTableProtection::FORMAT_COLUMNS
, (nOptions
& 0x0008) );
1407 pProtect
->setOption( ScTableProtection::FORMAT_ROWS
, (nOptions
& 0x0010) );
1408 pProtect
->setOption( ScTableProtection::INSERT_COLUMNS
, (nOptions
& 0x0020) );
1409 pProtect
->setOption( ScTableProtection::INSERT_ROWS
, (nOptions
& 0x0040) );
1410 pProtect
->setOption( ScTableProtection::INSERT_HYPERLINKS
, (nOptions
& 0x0080) );
1411 pProtect
->setOption( ScTableProtection::DELETE_COLUMNS
, (nOptions
& 0x0100) );
1412 pProtect
->setOption( ScTableProtection::DELETE_ROWS
, (nOptions
& 0x0200) );
1413 pProtect
->setOption( ScTableProtection::SELECT_LOCKED_CELLS
, (nOptions
& 0x0400) );
1414 pProtect
->setOption( ScTableProtection::SORT
, (nOptions
& 0x0800) );
1415 pProtect
->setOption( ScTableProtection::AUTOFILTER
, (nOptions
& 0x1000) );
1416 pProtect
->setOption( ScTableProtection::PIVOT_TABLES
, (nOptions
& 0x2000) );
1417 pProtect
->setOption( ScTableProtection::SELECT_UNLOCKED_CELLS
, (nOptions
& 0x4000) );
1419 // Enhanced protection containing editable ranges and permissions.
1420 pProtect
->setEnhancedProtection( std::vector(rSheet
.maEnhancedProtections
) );
1422 // all done. now commit.
1423 GetDoc().SetTabProtection(rTab
, pProtect
.get());
1427 XclImpSheetProtectBuffer::Sheet
* XclImpSheetProtectBuffer::GetSheetItem( SCTAB nTab
)
1429 ProtectedSheetMap::iterator itr
= maProtectedSheets
.find(nTab
);
1430 if (itr
== maProtectedSheets
.end())
1433 if ( !maProtectedSheets
.emplace( nTab
, Sheet() ).second
)
1436 itr
= maProtectedSheets
.find(nTab
);
1439 return &itr
->second
;
1442 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */