Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / sc / source / filter / excel / xicontent.cxx
blob298f612f2b1c91ba42fa985a28b40556c7d502d9
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <stringutil.hxx>
35 #include <cellform.hxx>
36 #include <cellvalue.hxx>
37 #include <document.hxx>
38 #include <editutil.hxx>
39 #include <validat.hxx>
40 #include <patattr.hxx>
41 #include <docpool.hxx>
42 #include <rangenam.hxx>
43 #include <arealink.hxx>
44 #include <stlsheet.hxx>
45 #include <xlcontent.hxx>
46 #include <xlformula.hxx>
47 #include <xltracer.hxx>
48 #include <xistream.hxx>
49 #include <xihelper.hxx>
50 #include <xistyle.hxx>
51 #include <xiescher.hxx>
52 #include <xiname.hxx>
54 #include <excform.hxx>
55 #include <tabprotection.hxx>
56 #include <documentimport.hxx>
58 #include <memory>
59 #include <utility>
60 #include <oox/helper/helper.hxx>
61 #include <sal/log.hxx>
63 using ::com::sun::star::uno::Sequence;
64 using ::std::unique_ptr;
66 // Shared string table ========================================================
68 XclImpSst::XclImpSst( const XclImpRoot& rRoot ) :
69 XclImpRoot( rRoot )
73 void XclImpSst::ReadSst( XclImpStream& rStrm )
75 rStrm.Ignore( 4 );
76 sal_uInt32 nStrCount = rStrm.ReaduInt32();
77 auto nBytesAvailable = rStrm.GetRecLeft();
78 if (nStrCount > nBytesAvailable)
80 SAL_WARN("sc.filter", "xls claimed to have " << nStrCount << " strings, but only " << nBytesAvailable << " bytes available, truncating");
81 nStrCount = nBytesAvailable;
83 maStrings.clear();
84 maStrings.reserve(nStrCount);
85 while( (nStrCount > 0) && rStrm.IsValid() )
87 XclImpString aString;
88 aString.Read( rStrm );
89 maStrings.push_back( aString );
90 --nStrCount;
94 const XclImpString* XclImpSst::GetString( sal_uInt32 nSstIndex ) const
96 return (nSstIndex < maStrings.size()) ? &maStrings[ nSstIndex ] : nullptr;
99 // Hyperlinks =================================================================
101 namespace {
103 /** Reads character array and stores it into rString.
104 @param nChars Number of following characters (not byte count!).
105 @param b16Bit true = 16-bit characters, false = 8-bit characters. */
106 void lclAppendString32( OUString& rString, XclImpStream& rStrm, sal_uInt32 nChars, bool b16Bit )
108 sal_uInt16 nReadChars = ulimit_cast< sal_uInt16 >( nChars );
109 rString += rStrm.ReadRawUniString( nReadChars, b16Bit );
110 // ignore remaining chars
111 std::size_t nIgnore = nChars - nReadChars;
112 if( b16Bit )
113 nIgnore *= 2;
114 rStrm.Ignore( nIgnore );
117 /** Reads 32-bit string length and the character array and stores it into rString.
118 @param b16Bit true = 16-bit characters, false = 8-bit characters. */
119 void lclAppendString32( OUString& rString, XclImpStream& rStrm, bool b16Bit )
121 lclAppendString32( rString, rStrm, rStrm.ReaduInt32(), b16Bit );
124 /** Reads 32-bit string length and ignores following 16-bit character array. */
125 void lclIgnoreString32( XclImpStream& rStrm )
127 sal_uInt32 nChars = rStrm.ReaduInt32();
128 nChars *= 2;
129 rStrm.Ignore( nChars );
132 /** Converts a path to an absolute path.
133 @param rPath The source path. The resulting path is returned here.
134 @param nLevel Number of parent directories to add in front of the path. */
135 void lclGetAbsPath( OUString& rPath, sal_uInt16 nLevel, const SfxObjectShell* pDocShell )
137 OUStringBuffer aTmpStr;
138 while( nLevel )
140 aTmpStr.append( "../" );
141 --nLevel;
143 aTmpStr.append( rPath );
145 if( pDocShell )
147 bool bWasAbs = false;
148 rPath = pDocShell->GetMedium()->GetURLObject().smartRel2Abs( aTmpStr.makeStringAndClear(), bWasAbs ).GetMainURL( INetURLObject::DecodeMechanism::NONE );
149 // full path as stored in SvxURLField must be encoded
151 else
152 rPath = aTmpStr.makeStringAndClear();
155 /** Inserts the URL into a text cell. Does not modify value or formula cells. */
156 void lclInsertUrl( XclImpRoot& rRoot, const OUString& rUrl, SCCOL nScCol, SCROW nScRow, SCTAB nScTab )
158 ScDocumentImport& rDoc = rRoot.GetDocImport();
159 ScAddress aScPos( nScCol, nScRow, nScTab );
160 ScRefCellValue aCell(rDoc.getDoc(), aScPos);
161 switch( aCell.meType )
163 // #i54261# hyperlinks in string cells
164 case CELLTYPE_STRING:
165 case CELLTYPE_EDIT:
167 sal_uInt32 nNumFmt = rDoc.getDoc().GetNumberFormat(rDoc.getDoc().GetNonThreadedContext(), aScPos);
168 SvNumberFormatter* pFormatter = rDoc.getDoc().GetFormatTable();
169 Color* pColor;
170 OUString aDisplText;
171 ScCellFormat::GetString(aCell, nNumFmt, aDisplText, &pColor, *pFormatter, &rDoc.getDoc());
172 if (aDisplText.isEmpty())
173 aDisplText = rUrl;
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.SetText( *pEditObj );
182 rEE.QuickInsertField( SvxFieldItem( aUrlField, EE_FEATURE_FIELD ), ESelection( 0, 0, EE_PARA_ALL, 0 ) );
184 else
186 rEE.SetText( EMPTY_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());
199 break;
201 default:
202 // Handle other cell types e.g. formulas ( and ? ) that have associated
203 // hyperlinks.
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);
214 break;
219 } // namespace
221 void XclImpHyperlink::ReadHlink( XclImpStream& rStrm )
223 XclRange aXclRange( ScAddress::UNINITIALIZED );
224 rStrm >> aXclRange;
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 );
240 XclGuid aGuid;
241 rStrm >> aGuid;
242 rStrm.Ignore( 4 );
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' );
262 // UNC path
263 if( ::get_flag( nFlags, EXC_HLINK_UNC ) )
265 xLongName.reset( new OUString );
266 lclAppendString32( *xLongName, rStrm, true );
267 lclGetAbsPath( *xLongName, 0, pDocShell );
269 // file link or URL
270 else if( ::get_flag( nFlags, EXC_HLINK_BODY ) )
272 rStrm >> aGuid;
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 );
279 rStrm.Ignore( 24 );
281 sal_uInt32 nStrLen = rStrm.ReaduInt32();
282 if( nStrLen )
284 nStrLen = rStrm.ReaduInt32();
285 nStrLen /= 2; // it's byte count here...
286 rStrm.Ignore( 2 );
287 xLongName.reset( new OUString );
288 lclAppendString32( *xLongName, rStrm, nStrLen, true );
289 lclGetAbsPath( *xLongName, nLevel, pDocShell );
291 else
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 );
303 else
305 OSL_FAIL( "XclImpHyperlink::ReadEmbeddedData - unknown content GUID" );
309 // text mark
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.get())
321 xLongName = std::move(xShortName);
322 else if (!xLongName && xTextMark.get())
323 xLongName.reset( new OUString );
325 if (xLongName)
327 if (xTextMark)
329 if( xLongName->isEmpty() )
331 sal_Int32 nSepPos = xTextMark->lastIndexOf( '!' );
332 if( nSepPos > 0 )
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();
341 ScRange aRange;
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, OUString( '.' ))));
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();
358 if (n < 4)
359 // Needs at least 4 characters.
360 return;
362 if (rUrl[0] != '#')
363 // the 1st character must be '#'.
364 return;
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];
374 if (c == '\'')
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
380 // quoted.
381 bQuoteTabName = true;
382 aTabName.append(c).append(c);
383 ++i;
384 continue;
387 bInQuote = !bInQuote;
388 if (!bInQuote && !aTabName.isEmpty())
390 if (bQuoteTabName)
391 aNewUrl.append("'");
392 aNewUrl.append(aTabName);
393 if (bQuoteTabName)
394 aNewUrl.append("'");
397 else if (bInQuote)
398 aTabName.append(c);
399 else
400 aNewUrl.append(c);
403 if (bInQuote)
404 // It should be outside the quotes!
405 return;
407 // All is good. Pass the new URL.
408 rUrl = aNewUrl.makeStringAndClear();
411 void XclImpHyperlink::InsertUrl( XclImpRoot& rRoot, const XclRange& rXclRange, const OUString& rUrl )
413 OUString aUrl(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 );
423 for( SCCOL nScCol = nScCol1; nScCol <= nScCol2; ++nScCol )
424 for( SCROW nScRow = nScRow1; nScRow <= nScRow2; ++nScRow )
425 lclInsertUrl( rRoot, aUrl, nScCol, nScRow, nScTab );
429 // Label ranges ===============================================================
431 void XclImpLabelranges::ReadLabelranges( XclImpStream& rStrm )
433 const XclImpRoot& rRoot = rStrm.GetRoot();
434 OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
436 ScDocument& rDoc = rRoot.GetDoc();
437 SCTAB nScTab = rRoot.GetCurrScTab();
438 XclImpAddressConverter& rAddrConv = rRoot.GetAddressConverter();
439 ScRangePairListRef xLabelRangesRef;
441 XclRangeList aRowXclRanges, aColXclRanges;
442 rStrm >> aRowXclRanges >> aColXclRanges;
444 // row label ranges
445 ScRangeList aRowScRanges;
446 rAddrConv.ConvertRangeList( aRowScRanges, aRowXclRanges, nScTab, false );
447 xLabelRangesRef = rDoc.GetRowNameRangesRef();
448 for ( size_t i = 0, nRanges = aRowScRanges.size(); i < nRanges; ++i )
450 const ScRange & rScRange = aRowScRanges[ i ];
451 ScRange aDataRange( rScRange );
452 if( aDataRange.aEnd.Col() < rDoc.MaxCol() )
454 aDataRange.aStart.SetCol( aDataRange.aEnd.Col() + 1 );
455 aDataRange.aEnd.SetCol( rDoc.MaxCol() );
457 else if( aDataRange.aStart.Col() > 0 )
459 aDataRange.aEnd.SetCol( aDataRange.aStart.Col() - 1 );
460 aDataRange.aStart.SetCol( 0 );
462 xLabelRangesRef->Append( ScRangePair( rScRange, aDataRange ) );
465 // column label ranges
466 ScRangeList aColScRanges;
467 rAddrConv.ConvertRangeList( aColScRanges, aColXclRanges, nScTab, false );
468 xLabelRangesRef = rDoc.GetColNameRangesRef();
470 for ( size_t i = 0, nRanges = aColScRanges.size(); i < nRanges; ++i )
472 const ScRange & rScRange = aColScRanges[ i ];
473 ScRange aDataRange( rScRange );
474 if( aDataRange.aEnd.Row() < rDoc.MaxRow() )
476 aDataRange.aStart.SetRow( aDataRange.aEnd.Row() + 1 );
477 aDataRange.aEnd.SetRow( rDoc.MaxRow() );
479 else if( aDataRange.aStart.Row() > 0 )
481 aDataRange.aEnd.SetRow( aDataRange.aStart.Row() - 1 );
482 aDataRange.aStart.SetRow( 0 );
484 xLabelRangesRef->Append( ScRangePair( rScRange, aDataRange ) );
488 // Conditional formatting =====================================================
490 XclImpCondFormat::XclImpCondFormat( const XclImpRoot& rRoot, sal_uInt32 nFormatIndex ) :
491 XclImpRoot( rRoot ),
492 mnFormatIndex( nFormatIndex ),
493 mnCondCount( 0 ),
494 mnCondIndex( 0 )
498 XclImpCondFormat::~XclImpCondFormat()
502 void XclImpCondFormat::ReadCondfmt( XclImpStream& rStrm )
504 OSL_ENSURE( !mnCondCount, "XclImpCondFormat::ReadCondfmt - already initialized" );
505 XclRangeList aXclRanges;
506 mnCondCount = rStrm.ReaduInt16();
507 rStrm.Ignore( 10 );
508 rStrm >> aXclRanges;
509 GetAddressConverter().ConvertRangeList( maRanges, aXclRanges, GetCurrScTab(), true );
512 void XclImpCondFormat::ReadCF( XclImpStream& rStrm )
514 if( mnCondIndex >= mnCondCount )
516 OSL_FAIL( "XclImpCondFormat::ReadCF - CF without leading CONDFMT" );
517 return;
520 // entire conditional format outside of valid range?
521 if( maRanges.empty() )
522 return;
524 sal_uInt8 nType = rStrm.ReaduInt8();
525 sal_uInt8 nOperator = rStrm.ReaduInt8();
526 sal_uInt16 nFmlaSize1 = rStrm.ReaduInt16();
527 sal_uInt16 nFmlaSize2 = rStrm.ReaduInt16();
528 sal_uInt32 nFlags = rStrm.ReaduInt32();
529 rStrm.Ignore( 2 ); //nFlagsExtended
531 // *** mode and comparison operator ***
533 ScConditionMode eMode = ScConditionMode::NONE;
534 switch( nType )
536 case EXC_CF_TYPE_CELL:
538 switch( nOperator )
540 case EXC_CF_CMP_BETWEEN: eMode = ScConditionMode::Between; break;
541 case EXC_CF_CMP_NOT_BETWEEN: eMode = ScConditionMode::NotBetween; break;
542 case EXC_CF_CMP_EQUAL: eMode = ScConditionMode::Equal; break;
543 case EXC_CF_CMP_NOT_EQUAL: eMode = ScConditionMode::NotEqual; break;
544 case EXC_CF_CMP_GREATER: eMode = ScConditionMode::Greater; break;
545 case EXC_CF_CMP_LESS: eMode = ScConditionMode::Less; break;
546 case EXC_CF_CMP_GREATER_EQUAL: eMode = ScConditionMode::EqGreater; break;
547 case EXC_CF_CMP_LESS_EQUAL: eMode = ScConditionMode::EqLess; break;
548 default:
549 SAL_INFO(
550 "sc.filter", "unknown CF comparison " << nOperator);
553 break;
555 case EXC_CF_TYPE_FMLA:
556 eMode = ScConditionMode::Direct;
557 break;
559 default:
560 SAL_INFO("sc.filter", "unknown CF mode " << nType);
561 return;
564 // *** create style sheet ***
566 OUString aStyleName( XclTools::GetCondFormatStyleName( GetCurrScTab(), mnFormatIndex, mnCondIndex ) );
567 SfxItemSet& rStyleItemSet = ScfTools::MakeCellStyleSheet( GetStyleSheetPool(), aStyleName, true ).GetItemSet();
569 const XclImpPalette& rPalette = GetPalette();
571 // number format
573 if( get_flag( nFlags, EXC_CF_BLOCK_NUMFMT ) )
575 XclImpNumFmtBuffer& rNumFmtBuffer = GetRoot().GetNumFmtBuffer();
576 bool bIFmt = get_flag( nFlags, EXC_CF_IFMT_USER );
577 sal_uInt16 nFormat = rNumFmtBuffer.ReadCFFormat( rStrm, bIFmt );
578 rNumFmtBuffer.FillToItemSet( rStyleItemSet, nFormat );
581 // *** font block ***
583 if( ::get_flag( nFlags, EXC_CF_BLOCK_FONT ) )
585 XclImpFont aFont( GetRoot() );
586 aFont.ReadCFFontBlock( rStrm );
587 aFont.FillToItemSet( rStyleItemSet, XclFontItemType::Cell );
590 // alignment
591 if( get_flag( nFlags, EXC_CF_BLOCK_ALIGNMENT ) )
593 XclImpCellAlign aAlign;
594 sal_uInt16 nAlign(0);
595 sal_uInt16 nAlignMisc(0);
596 nAlign = rStrm.ReaduInt16();
597 nAlignMisc = rStrm.ReaduInt16();
598 aAlign.FillFromCF( nAlign, nAlignMisc );
599 aAlign.FillToItemSet( rStyleItemSet, nullptr );
600 rStrm.Ignore(4);
603 // *** border block ***
605 if( ::get_flag( nFlags, EXC_CF_BLOCK_BORDER ) )
607 sal_uInt16 nLineStyle(0);
608 sal_uInt32 nLineColor(0);
609 nLineStyle = rStrm.ReaduInt16();
610 nLineColor = rStrm.ReaduInt32();
611 rStrm.Ignore( 2 );
613 XclImpCellBorder aBorder;
614 aBorder.FillFromCF8( nLineStyle, nLineColor, nFlags );
615 aBorder.FillToItemSet( rStyleItemSet, rPalette );
618 // *** pattern block ***
620 if( ::get_flag( nFlags, EXC_CF_BLOCK_AREA ) )
622 sal_uInt16 nPattern(0), nColor(0);
623 nPattern = rStrm.ReaduInt16();
624 nColor = rStrm.ReaduInt16();
626 XclImpCellArea aArea;
627 aArea.FillFromCF8( nPattern, nColor, nFlags );
628 aArea.FillToItemSet( rStyleItemSet, rPalette );
631 if( get_flag( nFlags, EXC_CF_BLOCK_PROTECTION ) )
633 sal_uInt16 nCellProt;
634 nCellProt = rStrm.ReaduInt16();
635 XclImpCellProt aCellProt;
636 aCellProt.FillFromXF3(nCellProt);
637 aCellProt.FillToItemSet( rStyleItemSet );
640 // *** formulas ***
642 const ScAddress& rPos = maRanges.front().aStart; // assured above that maRanges is not empty
643 ExcelToSc& rFmlaConv = GetOldFmlaConverter();
645 ::std::unique_ptr< ScTokenArray > xTokArr1;
646 if( nFmlaSize1 > 0 )
648 std::unique_ptr<ScTokenArray> pTokArr;
649 rFmlaConv.Reset( rPos );
650 rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize1, false, FT_CondFormat );
651 // formula converter owns pTokArr -> create a copy of the token array
652 if( pTokArr )
654 xTokArr1 = std::move( pTokArr );
655 GetDoc().CheckLinkFormulaNeedingCheck( *xTokArr1);
659 ::std::unique_ptr< ScTokenArray > xTokArr2;
660 if( nFmlaSize2 > 0 )
662 std::unique_ptr<ScTokenArray> pTokArr;
663 rFmlaConv.Reset( rPos );
664 rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize2, false, FT_CondFormat );
665 // formula converter owns pTokArr -> create a copy of the token array
666 if( pTokArr )
668 xTokArr2 = std::move( pTokArr );
669 GetDoc().CheckLinkFormulaNeedingCheck( *xTokArr2);
673 // *** create the Calc conditional formatting ***
675 const ScAddress aPos(rPos); //in case maRanges.Join invalidates it
677 if( !mxScCondFmt.get() )
679 mxScCondFmt.reset( new ScConditionalFormat( 0/*nKey*/, &GetDoc() ) );
680 if(maRanges.size() > 1)
681 maRanges.Join(maRanges[0], true);
682 mxScCondFmt->SetRange(maRanges);
685 ScCondFormatEntry* pEntry = new ScCondFormatEntry(eMode, xTokArr1.get(), xTokArr2.get(), &GetDoc(), aPos, aStyleName);
686 mxScCondFmt->AddEntry( pEntry );
687 ++mnCondIndex;
690 void XclImpCondFormat::Apply()
692 if( mxScCondFmt.get() )
694 ScDocument& rDoc = GetDoc();
696 SCTAB nTab = maRanges.front().aStart.Tab();
697 sal_uLong nKey = rDoc.AddCondFormat( mxScCondFmt->Clone(), nTab );
699 rDoc.AddCondFormatData( maRanges, nTab, nKey );
703 XclImpCondFormatManager::XclImpCondFormatManager( const XclImpRoot& rRoot ) :
704 XclImpRoot( rRoot )
708 void XclImpCondFormatManager::ReadCondfmt( XclImpStream& rStrm )
710 XclImpCondFormat* pFmt = new XclImpCondFormat( GetRoot(), maCondFmtList.size() );
711 pFmt->ReadCondfmt( rStrm );
712 maCondFmtList.push_back( std::unique_ptr<XclImpCondFormat>(pFmt) );
715 void XclImpCondFormatManager::ReadCF( XclImpStream& rStrm )
717 OSL_ENSURE( !maCondFmtList.empty(), "XclImpCondFormatManager::ReadCF - CF without leading CONDFMT" );
718 if( !maCondFmtList.empty() )
719 maCondFmtList.back()->ReadCF( rStrm );
722 void XclImpCondFormatManager::Apply()
724 for( auto& rxFmt : maCondFmtList )
725 rxFmt->Apply();
726 maCondFmtList.clear();
729 // Data Validation ============================================================
731 XclImpValidationManager::DVItem::DVItem( const ScRangeList& rRanges, const ScValidationData& rValidData ) :
732 maRanges(rRanges), maValidData(rValidData) {}
734 XclImpValidationManager::XclImpValidationManager( const XclImpRoot& rRoot ) :
735 XclImpRoot( rRoot )
739 void XclImpValidationManager::ReadDval( XclImpStream& rStrm )
741 const XclImpRoot& rRoot = rStrm.GetRoot();
742 OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
744 sal_uInt32 nObjId(0);
745 rStrm.Ignore( 10 );
746 nObjId = rStrm.ReaduInt32();
747 if( nObjId != EXC_DVAL_NOOBJ )
749 OSL_ENSURE( nObjId <= 0xFFFF, "XclImpValidation::ReadDval - invalid object ID" );
750 rRoot.GetCurrSheetDrawing().SetSkipObj( static_cast< sal_uInt16 >( nObjId ) );
754 void XclImpValidationManager::ReadDV( XclImpStream& rStrm )
756 const XclImpRoot& rRoot = rStrm.GetRoot();
757 OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
759 ScDocument& rDoc = rRoot.GetDoc();
760 SCTAB nScTab = rRoot.GetCurrScTab();
761 ExcelToSc& rFmlaConv = rRoot.GetOldFmlaConverter();
763 // flags
764 sal_uInt32 nFlags = rStrm.ReaduInt32();
766 // message strings
767 /* Empty strings are single NUL characters in Excel (string length is 1).
768 -> Do not let the stream replace them with '?' characters. */
769 rStrm.SetNulSubstChar( '\0' );
770 OUString aPromptTitle( rStrm.ReadUniString() );
771 OUString aErrorTitle( rStrm.ReadUniString() );
772 OUString aPromptMessage( rStrm.ReadUniString() );
773 OUString aErrorMessage( rStrm.ReadUniString() );
774 rStrm.SetNulSubstChar(); // back to default
776 // formula(s)
777 if ( rStrm.GetRecLeft() <= 8 )
778 // Not enough bytes left in the record. Bail out.
779 return;
781 // first formula
782 // string list is single tStr token with NUL separators -> replace them with LF
783 rStrm.SetNulSubstChar( '\n' );
784 ::std::unique_ptr< ScTokenArray > xTokArr1;
786 // We can't import the formula directly because we need the range
787 sal_uInt16 nLenFormula1 = rStrm.ReaduInt16();
788 rStrm.Ignore( 2 );
789 XclImpStreamPos aPosFormula1;
790 rStrm.StorePosition(aPosFormula1);
791 rStrm.Ignore(nLenFormula1);
793 // second formula
794 ::std::unique_ptr< ScTokenArray > xTokArr2;
796 sal_uInt16 nLenFormula2 = rStrm.ReaduInt16();
797 rStrm.Ignore( 2 );
798 XclImpStreamPos aPosFormula2;
799 rStrm.StorePosition(aPosFormula2);
800 rStrm.Ignore(nLenFormula2);
802 // read all cell ranges
803 XclRangeList aXclRanges;
804 rStrm >> aXclRanges;
806 // convert to Calc range list
807 ScRangeList aScRanges;
808 rRoot.GetAddressConverter().ConvertRangeList( aScRanges, aXclRanges, nScTab, true );
810 // only continue if there are valid ranges
811 if ( aScRanges.empty() )
812 return;
814 ScRange aCombinedRange = aScRanges.Combine();
816 XclImpStreamPos aCurrentPos;
817 rStrm.StorePosition(aCurrentPos);
818 rStrm.RestorePosition(aPosFormula1);
819 if( nLenFormula1 > 0 )
821 std::unique_ptr<ScTokenArray> pTokArr;
822 rFmlaConv.Reset(aCombinedRange.aStart);
823 rFmlaConv.Convert( pTokArr, rStrm, nLenFormula1, false, FT_CondFormat );
824 // formula converter owns pTokArr -> create a copy of the token array
825 if( pTokArr )
826 xTokArr1 = std::move( pTokArr );
828 rStrm.SetNulSubstChar(); // back to default
829 if (nLenFormula2 > 0)
831 rStrm.RestorePosition(aPosFormula2);
832 std::unique_ptr<ScTokenArray> pTokArr;
833 rFmlaConv.Reset(aCombinedRange.aStart);
834 rFmlaConv.Convert( pTokArr, rStrm, nLenFormula2, false, FT_CondFormat );
835 // formula converter owns pTokArr -> create a copy of the token array
836 if( pTokArr )
837 xTokArr2 = std::move( pTokArr );
840 rStrm.RestorePosition(aCurrentPos);
842 bool bIsValid = true; // valid settings in flags field
844 ScValidationMode eValMode = SC_VALID_ANY;
845 switch( nFlags & EXC_DV_MODE_MASK )
847 case EXC_DV_MODE_ANY: eValMode = SC_VALID_ANY; break;
848 case EXC_DV_MODE_WHOLE: eValMode = SC_VALID_WHOLE; break;
849 case EXC_DV_MODE_DECIMAL: eValMode = SC_VALID_DECIMAL; break;
850 case EXC_DV_MODE_LIST: eValMode = SC_VALID_LIST; break;
851 case EXC_DV_MODE_DATE: eValMode = SC_VALID_DATE; break;
852 case EXC_DV_MODE_TIME: eValMode = SC_VALID_TIME; break;
853 case EXC_DV_MODE_TEXTLEN: eValMode = SC_VALID_TEXTLEN; break;
854 case EXC_DV_MODE_CUSTOM: eValMode = SC_VALID_CUSTOM; break;
855 default: bIsValid = false;
857 rRoot.GetTracer().TraceDVType(eValMode == SC_VALID_CUSTOM);
859 ScConditionMode eCondMode = ScConditionMode::Between;
860 switch( nFlags & EXC_DV_COND_MASK )
862 case EXC_DV_COND_BETWEEN: eCondMode = ScConditionMode::Between; break;
863 case EXC_DV_COND_NOTBETWEEN:eCondMode = ScConditionMode::NotBetween; break;
864 case EXC_DV_COND_EQUAL: eCondMode = ScConditionMode::Equal; break;
865 case EXC_DV_COND_NOTEQUAL: eCondMode = ScConditionMode::NotEqual; break;
866 case EXC_DV_COND_GREATER: eCondMode = ScConditionMode::Greater; break;
867 case EXC_DV_COND_LESS: eCondMode = ScConditionMode::Less; break;
868 case EXC_DV_COND_EQGREATER: eCondMode = ScConditionMode::EqGreater; break;
869 case EXC_DV_COND_EQLESS: eCondMode = ScConditionMode::EqLess; break;
870 default: bIsValid = false;
873 if ( !bIsValid )
874 // No valid validation found. Bail out.
875 return;
877 // The default value for comparison is _BETWEEN. However, custom
878 // rules are a formula, and thus the comparator should be ignored
879 // and only a true or false from the formula is evaluated. In Calc,
880 // formulas use comparison SC_COND_DIRECT.
881 if( eValMode == SC_VALID_CUSTOM )
883 eCondMode = ScConditionMode::Direct;
886 // first range for base address for relative references
887 const ScRange& rScRange = aScRanges.front(); // aScRanges is not empty
889 // process string list of a list validity (convert to list of string tokens)
890 if( xTokArr1.get() && (eValMode == SC_VALID_LIST) && ::get_flag( nFlags, EXC_DV_STRINGLIST ) )
891 XclTokenArrayHelper::ConvertStringToList(*xTokArr1, rDoc.GetSharedStringPool(), '\n');
893 maDVItems.push_back(
894 std::make_unique<DVItem>(aScRanges, ScValidationData(eValMode, eCondMode, xTokArr1.get(), xTokArr2.get(), &rDoc, rScRange.aStart)));
895 DVItem& rItem = *maDVItems.back();
897 rItem.maValidData.SetIgnoreBlank( ::get_flag( nFlags, EXC_DV_IGNOREBLANK ) );
898 rItem.maValidData.SetListType( ::get_flagvalue( nFlags, EXC_DV_SUPPRESSDROPDOWN, css::sheet::TableValidationVisibility::INVISIBLE, css::sheet::TableValidationVisibility::UNSORTED ) );
900 // *** prompt box ***
901 if( !aPromptTitle.isEmpty() || !aPromptMessage.isEmpty() )
903 // set any text stored in the record
904 rItem.maValidData.SetInput( aPromptTitle, aPromptMessage );
905 if( !::get_flag( nFlags, EXC_DV_SHOWPROMPT ) )
906 rItem.maValidData.ResetInput();
909 // *** error box ***
910 ScValidErrorStyle eErrStyle = SC_VALERR_STOP;
911 switch( nFlags & EXC_DV_ERROR_MASK )
913 case EXC_DV_ERROR_WARNING: eErrStyle = SC_VALERR_WARNING; break;
914 case EXC_DV_ERROR_INFO: eErrStyle = SC_VALERR_INFO; break;
916 // set texts and error style
917 rItem.maValidData.SetError( aErrorTitle, aErrorMessage, eErrStyle );
918 if( !::get_flag( nFlags, EXC_DV_SHOWERROR ) )
919 rItem.maValidData.ResetError();
922 void XclImpValidationManager::Apply()
924 ScDocument& rDoc = GetRoot().GetDoc();
925 for (const auto& rxDVItem : maDVItems)
927 DVItem& rItem = *rxDVItem;
928 // set the handle ID
929 sal_uLong nHandle = rDoc.AddValidationEntry( rItem.maValidData );
930 ScPatternAttr aPattern( rDoc.GetPool() );
931 aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_VALIDDATA, nHandle ) );
933 // apply all ranges
934 for ( size_t i = 0, nRanges = rItem.maRanges.size(); i < nRanges; ++i )
936 const ScRange & rScRange = rItem.maRanges[ i ];
937 rDoc.ApplyPatternAreaTab( rScRange.aStart.Col(), rScRange.aStart.Row(),
938 rScRange.aEnd.Col(), rScRange.aEnd.Row(), rScRange.aStart.Tab(), aPattern );
941 maDVItems.clear();
944 // Web queries ================================================================
946 XclImpWebQuery::XclImpWebQuery( const ScRange& rDestRange ) :
947 maDestRange( rDestRange ),
948 meMode( xlWQUnknown ),
949 mnRefresh( 0 )
953 void XclImpWebQuery::ReadParamqry( XclImpStream& rStrm )
955 sal_uInt16 nFlags = rStrm.ReaduInt16();
956 sal_uInt16 nType = ::extract_value< sal_uInt16 >( nFlags, 0, 3 );
957 if( (nType == EXC_PQRYTYPE_WEBQUERY) && ::get_flag( nFlags, EXC_PQRY_WEBQUERY ) )
959 if( ::get_flag( nFlags, EXC_PQRY_TABLES ) )
961 meMode = xlWQAllTables;
962 maTables = ScfTools::GetHTMLTablesName();
964 else
966 meMode = xlWQDocument;
967 maTables = ScfTools::GetHTMLDocName();
972 void XclImpWebQuery::ReadWqstring( XclImpStream& rStrm )
974 maURL = rStrm.ReadUniString();
977 void XclImpWebQuery::ReadWqsettings( XclImpStream& rStrm )
979 rStrm.Ignore( 10 );
980 sal_uInt16 nFlags = rStrm.ReaduInt16();
981 rStrm.Ignore( 10 );
982 mnRefresh = rStrm.ReaduInt16();
984 if( ::get_flag( nFlags, EXC_WQSETT_SPECTABLES ) && (meMode == xlWQAllTables) )
985 meMode = xlWQSpecTables;
988 void XclImpWebQuery::ReadWqtables( XclImpStream& rStrm )
990 if( meMode == xlWQSpecTables )
992 rStrm.Ignore( 4 );
993 OUString aTables( rStrm.ReadUniString() );
995 const sal_Unicode cSep = ';';
996 const OUString aQuotedPairs( "\"\"" );
997 maTables.clear();
998 for ( sal_Int32 nStringIx {aTables.isEmpty() ? -1 : 0}; nStringIx>=0; )
1000 OUString aToken( ScStringUtil::GetQuotedToken( aTables, 0, aQuotedPairs, ',', nStringIx ) );
1001 sal_Int32 nTabNum = CharClass::isAsciiNumeric( aToken ) ? aToken.toInt32() : 0;
1002 if( nTabNum > 0 )
1003 maTables = ScGlobal::addToken( maTables, ScfTools::GetNameFromHTMLIndex( static_cast< sal_uInt32 >( nTabNum ) ), cSep );
1004 else
1006 ScGlobal::EraseQuotes( aToken, '"', false );
1007 if( !aToken.isEmpty() )
1008 maTables = ScGlobal::addToken( maTables, ScfTools::GetNameFromHTMLName( aToken ), cSep );
1014 void XclImpWebQuery::Apply( ScDocument& rDoc, const OUString& rFilterName )
1016 if( !maURL.isEmpty() && (meMode != xlWQUnknown) && rDoc.GetDocumentShell() )
1018 ScAreaLink* pLink = new ScAreaLink( rDoc.GetDocumentShell(),
1019 maURL, rFilterName, EMPTY_OUSTRING, maTables, maDestRange, mnRefresh * 60UL );
1020 rDoc.GetLinkManager()->InsertFileLink( *pLink, OBJECT_CLIENT_FILE,
1021 maURL, &rFilterName, &maTables );
1025 XclImpWebQueryBuffer::XclImpWebQueryBuffer( const XclImpRoot& rRoot ) :
1026 XclImpRoot( rRoot )
1030 void XclImpWebQueryBuffer::ReadQsi( XclImpStream& rStrm )
1032 if( GetBiff() == EXC_BIFF8 )
1034 rStrm.Ignore( 10 );
1035 OUString aXclName( rStrm.ReadUniString() );
1037 // #i64794# Excel replaces spaces with underscores
1038 aXclName = aXclName.replaceAll( " ", "_" );
1040 // find the defined name used in Calc
1041 if( const XclImpName* pName = GetNameManager().FindName( aXclName, GetCurrScTab() ) )
1043 if( const ScRangeData* pRangeData = pName->GetScRangeData() )
1045 ScRange aRange;
1046 if( pRangeData->IsReference( aRange ) )
1047 maWQList.emplace_back( aRange );
1051 else
1053 DBG_ERROR_BIFF();
1057 void XclImpWebQueryBuffer::ReadParamqry( XclImpStream& rStrm )
1059 if (!maWQList.empty())
1060 maWQList.back().ReadParamqry( rStrm );
1063 void XclImpWebQueryBuffer::ReadWqstring( XclImpStream& rStrm )
1065 if (!maWQList.empty())
1066 maWQList.back().ReadWqstring( rStrm );
1069 void XclImpWebQueryBuffer::ReadWqsettings( XclImpStream& rStrm )
1071 if (!maWQList.empty())
1072 maWQList.back().ReadWqsettings( rStrm );
1075 void XclImpWebQueryBuffer::ReadWqtables( XclImpStream& rStrm )
1077 if (!maWQList.empty())
1078 maWQList.back().ReadWqtables( rStrm );
1081 void XclImpWebQueryBuffer::Apply()
1083 ScDocument& rDoc = GetDoc();
1084 for( auto& rQuery : maWQList )
1085 rQuery.Apply( rDoc, EXC_WEBQRY_FILTER );
1088 // Decryption =================================================================
1090 namespace {
1092 XclImpDecrypterRef lclReadFilepass5( XclImpStream& rStrm )
1094 XclImpDecrypterRef xDecr;
1095 OSL_ENSURE( rStrm.GetRecLeft() == 4, "lclReadFilepass5 - wrong record size" );
1096 if( rStrm.GetRecLeft() == 4 )
1098 sal_uInt16 nKey(0), nHash(0);
1099 nKey = rStrm.ReaduInt16();
1100 nHash = rStrm.ReaduInt16();
1101 xDecr.reset( new XclImpBiff5Decrypter( nKey, nHash ) );
1103 return xDecr;
1106 XclImpDecrypterRef lclReadFilepass8_Standard( XclImpStream& rStrm )
1108 XclImpDecrypterRef xDecr;
1109 OSL_ENSURE( rStrm.GetRecLeft() == 48, "lclReadFilepass8 - wrong record size" );
1110 if( rStrm.GetRecLeft() == 48 )
1112 std::vector<sal_uInt8> aSalt(16);
1113 std::vector<sal_uInt8> aVerifier(16);
1114 std::vector<sal_uInt8> aVerifierHash(16);
1115 rStrm.Read(aSalt.data(), 16);
1116 rStrm.Read(aVerifier.data(), 16);
1117 rStrm.Read(aVerifierHash.data(), 16);
1118 xDecr.reset(new XclImpBiff8StdDecrypter(aSalt, aVerifier, aVerifierHash));
1120 return xDecr;
1123 XclImpDecrypterRef lclReadFilepass8_Strong(XclImpStream& rStream)
1125 //It is possible there are other variants in existence but these
1126 //are the defaults I get with Excel 2013
1127 XclImpDecrypterRef xDecr;
1129 msfilter::RC4EncryptionInfo info;
1131 info.header.flags = rStream.ReaduInt32();
1132 if (oox::getFlag( info.header.flags, msfilter::ENCRYPTINFO_EXTERNAL))
1133 return xDecr;
1135 sal_uInt32 nHeaderSize = rStream.ReaduInt32();
1136 sal_uInt32 actualHeaderSize = sizeof(info.header);
1138 if( nHeaderSize < actualHeaderSize )
1139 return xDecr;
1141 info.header.flags = rStream.ReaduInt32();
1142 info.header.sizeExtra = rStream.ReaduInt32();
1143 info.header.algId = rStream.ReaduInt32();
1144 info.header.algIdHash = rStream.ReaduInt32();
1145 info.header.keyBits = rStream.ReaduInt32();
1146 info.header.providedType = rStream.ReaduInt32();
1147 info.header.reserved1 = rStream.ReaduInt32();
1148 info.header.reserved2 = rStream.ReaduInt32();
1150 rStream.Ignore(nHeaderSize - actualHeaderSize);
1152 info.verifier.saltSize = rStream.ReaduInt32();
1153 if (info.verifier.saltSize != msfilter::SALT_LENGTH)
1154 return xDecr;
1155 rStream.Read(&info.verifier.salt, sizeof(info.verifier.salt));
1156 rStream.Read(&info.verifier.encryptedVerifier, sizeof(info.verifier.encryptedVerifier));
1158 info.verifier.encryptedVerifierHashSize = rStream.ReaduInt32();
1159 if (info.verifier.encryptedVerifierHashSize != RTL_DIGEST_LENGTH_SHA1)
1160 return xDecr;
1161 rStream.Read(&info.verifier.encryptedVerifierHash, info.verifier.encryptedVerifierHashSize);
1163 // check flags and algorithm IDs, required are AES128 and SHA-1
1164 if (!oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_CRYPTOAPI))
1165 return xDecr;
1167 if (oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_AES))
1168 return xDecr;
1170 if (info.header.algId != msfilter::ENCRYPT_ALGO_RC4)
1171 return xDecr;
1173 // hash algorithm ID 0 defaults to SHA-1 too
1174 if (info.header.algIdHash != 0 && info.header.algIdHash != msfilter::ENCRYPT_HASH_SHA1)
1175 return xDecr;
1177 xDecr.reset(new XclImpBiff8CryptoAPIDecrypter(
1178 std::vector<sal_uInt8>(info.verifier.salt,
1179 info.verifier.salt + SAL_N_ELEMENTS(info.verifier.salt)),
1180 std::vector<sal_uInt8>(info.verifier.encryptedVerifier,
1181 info.verifier.encryptedVerifier + SAL_N_ELEMENTS(info.verifier.encryptedVerifier)),
1182 std::vector<sal_uInt8>(info.verifier.encryptedVerifierHash,
1183 info.verifier.encryptedVerifierHash + SAL_N_ELEMENTS(info.verifier.encryptedVerifierHash))));
1185 return xDecr;
1188 XclImpDecrypterRef lclReadFilepass8( XclImpStream& rStrm )
1190 XclImpDecrypterRef xDecr;
1192 sal_uInt16 nMode = rStrm.ReaduInt16();
1193 switch( nMode )
1195 case EXC_FILEPASS_BIFF5:
1196 xDecr = lclReadFilepass5( rStrm );
1197 break;
1199 case EXC_FILEPASS_BIFF8:
1201 sal_uInt32 nVersion = rStrm.ReaduInt32();
1202 if (nVersion == msfilter::VERSION_INFO_1997_FORMAT)
1204 //A Version structure where Version.vMajor MUST be 0x0001,
1205 //and Version.vMinor MUST be 0x0001.
1206 xDecr = lclReadFilepass8_Standard(rStrm);
1208 else if (nVersion == msfilter::VERSION_INFO_2007_FORMAT ||
1209 nVersion == msfilter::VERSION_INFO_2007_FORMAT_SP2)
1211 //Version.vMajor MUST be 0x0002, 0x0003 or 0x0004 and
1212 //Version.vMinor MUST be 0x0002.
1213 xDecr = lclReadFilepass8_Strong(rStrm);
1215 else
1216 OSL_FAIL("lclReadFilepass8 - unknown BIFF8 encryption sub mode");
1218 break;
1220 default:
1221 OSL_FAIL( "lclReadFilepass8 - unknown encryption mode" );
1224 return xDecr;
1227 } // namespace
1229 const ErrCode& XclImpDecryptHelper::ReadFilepass( XclImpStream& rStrm )
1231 XclImpDecrypterRef xDecr;
1232 rStrm.DisableDecryption();
1234 // read the FILEPASS record and create a new decrypter object
1235 switch( rStrm.GetRoot().GetBiff() )
1237 case EXC_BIFF2:
1238 case EXC_BIFF3:
1239 case EXC_BIFF4:
1240 case EXC_BIFF5: xDecr = lclReadFilepass5( rStrm ); break;
1241 case EXC_BIFF8: xDecr = lclReadFilepass8( rStrm ); break;
1242 default: DBG_ERROR_BIFF();
1245 // set decrypter at import stream
1246 rStrm.SetDecrypter( xDecr );
1248 // request and verify a password (decrypter implements IDocPasswordVerifier)
1249 if( xDecr )
1250 rStrm.GetRoot().RequestEncryptionData( *xDecr );
1252 // return error code (success, wrong password, etc.)
1253 return xDecr ? xDecr->GetError() : EXC_ENCR_ERROR_UNSUPP_CRYPT;
1256 // Document protection ========================================================
1258 XclImpDocProtectBuffer::XclImpDocProtectBuffer( const XclImpRoot& rRoot ) :
1259 XclImpRoot( rRoot ),
1260 mnPassHash(0x0000),
1261 mbDocProtect(false),
1262 mbWinProtect(false)
1266 void XclImpDocProtectBuffer::ReadDocProtect( XclImpStream& rStrm )
1268 mbDocProtect = rStrm.ReaduInt16() != 0;
1271 void XclImpDocProtectBuffer::ReadWinProtect( XclImpStream& rStrm )
1273 mbWinProtect = rStrm.ReaduInt16() != 0;
1276 void XclImpDocProtectBuffer::ReadPasswordHash( XclImpStream& rStrm )
1278 rStrm.EnableDecryption();
1279 mnPassHash = rStrm.ReaduInt16();
1282 void XclImpDocProtectBuffer::Apply() const
1284 if (!mbDocProtect && !mbWinProtect)
1285 // Excel requires either the structure or windows protection is set.
1286 // If neither is set then the document is not protected at all.
1287 return;
1289 unique_ptr<ScDocProtection> pProtect(new ScDocProtection);
1290 pProtect->setProtected(true);
1292 if (mnPassHash)
1294 // 16-bit password hash.
1295 Sequence<sal_Int8> aPass{sal_Int8(mnPassHash >> 8), sal_Int8(mnPassHash & 0xFF)};
1296 pProtect->setPasswordHash(aPass, PASSHASH_XL);
1299 // document protection options
1300 pProtect->setOption(ScDocProtection::STRUCTURE, mbDocProtect);
1301 pProtect->setOption(ScDocProtection::WINDOWS, mbWinProtect);
1303 GetDoc().SetDocProtection(pProtect.get());
1306 // Sheet Protection ===========================================================
1308 XclImpSheetProtectBuffer::Sheet::Sheet() :
1309 mbProtected(false),
1310 mnPasswordHash(0x0000),
1311 mnOptions(0x4400)
1315 XclImpSheetProtectBuffer::Sheet::Sheet(const Sheet& r) :
1316 mbProtected(r.mbProtected),
1317 mnPasswordHash(r.mnPasswordHash),
1318 mnOptions(r.mnOptions)
1322 XclImpSheetProtectBuffer::XclImpSheetProtectBuffer( const XclImpRoot& rRoot ) :
1323 XclImpRoot( rRoot )
1327 void XclImpSheetProtectBuffer::ReadProtect( XclImpStream& rStrm, SCTAB nTab )
1329 if ( rStrm.ReaduInt16() )
1331 Sheet* pSheet = GetSheetItem(nTab);
1332 if (pSheet)
1333 pSheet->mbProtected = true;
1337 void XclImpSheetProtectBuffer::ReadOptions( XclImpStream& rStrm, SCTAB nTab )
1339 // The flag size specifies the size of bytes that follows that stores
1340 // feature data. If -1 it depends on the feature type imported earlier.
1341 // For enhanced protection data, the size is always 4. For the most xls
1342 // documents out there this value is almost always -1.
1343 sal_Int32 nFlagSize = rStrm.ReadInt32();
1344 if (nFlagSize != -1)
1345 return;
1347 // There are actually 4 bytes to read, but the upper 2 bytes currently
1348 // don't store any bits.
1349 sal_uInt16 nOptions = rStrm.ReaduInt16();
1351 Sheet* pSheet = GetSheetItem(nTab);
1352 if (pSheet)
1353 pSheet->mnOptions = nOptions;
1356 void XclImpSheetProtectBuffer::AppendEnhancedProtection( const ScEnhancedProtection & rProt, SCTAB nTab )
1358 Sheet* pSheet = GetSheetItem(nTab);
1359 if (pSheet)
1360 pSheet->maEnhancedProtections.push_back( rProt);
1363 void XclImpSheetProtectBuffer::ReadPasswordHash( XclImpStream& rStrm, SCTAB nTab )
1365 sal_uInt16 nHash = rStrm.ReaduInt16();
1366 Sheet* pSheet = GetSheetItem(nTab);
1367 if (pSheet)
1368 pSheet->mnPasswordHash = nHash;
1371 void XclImpSheetProtectBuffer::Apply() const
1373 for (const auto& [rTab, rSheet] : maProtectedSheets)
1375 if (!rSheet.mbProtected)
1376 // This sheet is (for whatever reason) not protected.
1377 continue;
1379 unique_ptr<ScTableProtection> pProtect(new ScTableProtection);
1380 pProtect->setProtected(true);
1382 // 16-bit hash password
1383 const sal_uInt16 nHash = rSheet.mnPasswordHash;
1384 if (nHash)
1386 Sequence<sal_Int8> aPass{sal_Int8(nHash >> 8), sal_Int8(nHash & 0xFF)};
1387 pProtect->setPasswordHash(aPass, PASSHASH_XL);
1390 // sheet protection options
1391 const sal_uInt16 nOptions = rSheet.mnOptions;
1392 pProtect->setOption( ScTableProtection::OBJECTS, (nOptions & 0x0001) );
1393 pProtect->setOption( ScTableProtection::SCENARIOS, (nOptions & 0x0002) );
1394 pProtect->setOption( ScTableProtection::FORMAT_CELLS, (nOptions & 0x0004) );
1395 pProtect->setOption( ScTableProtection::FORMAT_COLUMNS, (nOptions & 0x0008) );
1396 pProtect->setOption( ScTableProtection::FORMAT_ROWS, (nOptions & 0x0010) );
1397 pProtect->setOption( ScTableProtection::INSERT_COLUMNS, (nOptions & 0x0020) );
1398 pProtect->setOption( ScTableProtection::INSERT_ROWS, (nOptions & 0x0040) );
1399 pProtect->setOption( ScTableProtection::INSERT_HYPERLINKS, (nOptions & 0x0080) );
1400 pProtect->setOption( ScTableProtection::DELETE_COLUMNS, (nOptions & 0x0100) );
1401 pProtect->setOption( ScTableProtection::DELETE_ROWS, (nOptions & 0x0200) );
1402 pProtect->setOption( ScTableProtection::SELECT_LOCKED_CELLS, (nOptions & 0x0400) );
1403 pProtect->setOption( ScTableProtection::SORT, (nOptions & 0x0800) );
1404 pProtect->setOption( ScTableProtection::AUTOFILTER, (nOptions & 0x1000) );
1405 pProtect->setOption( ScTableProtection::PIVOT_TABLES, (nOptions & 0x2000) );
1406 pProtect->setOption( ScTableProtection::SELECT_UNLOCKED_CELLS, (nOptions & 0x4000) );
1408 // Enhanced protection containing editable ranges and permissions.
1409 pProtect->setEnhancedProtection( rSheet.maEnhancedProtections);
1411 // all done. now commit.
1412 GetDoc().SetTabProtection(rTab, pProtect.get());
1416 XclImpSheetProtectBuffer::Sheet* XclImpSheetProtectBuffer::GetSheetItem( SCTAB nTab )
1418 ProtectedSheetMap::iterator itr = maProtectedSheets.find(nTab);
1419 if (itr == maProtectedSheets.end())
1421 // new sheet
1422 if ( !maProtectedSheets.emplace( nTab, Sheet() ).second )
1423 return nullptr;
1425 itr = maProtectedSheets.find(nTab);
1428 return &itr->second;
1431 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */