Version 5.2.6.1, tag libreoffice-5.2.6.1
[LibreOffice.git] / sc / source / filter / excel / xicontent.cxx
blob05a142d8b1f5ad3f1eeaf0b3e9d54e58c987e168
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 "xicontent.hxx"
21 #include <sfx2/objsh.hxx>
22 #include <sfx2/docfile.hxx>
23 #include <tools/urlobj.hxx>
24 #include <editeng/editeng.hxx>
25 #include <editeng/editobj.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/fhgtitem.hxx>
34 #include <editeng/wghtitem.hxx>
35 #include <editeng/udlnitem.hxx>
36 #include <editeng/postitem.hxx>
37 #include <editeng/colritem.hxx>
38 #include <editeng/crossedoutitem.hxx>
39 #include "stringutil.hxx"
40 #include "cellform.hxx"
41 #include "cellvalue.hxx"
42 #include "document.hxx"
43 #include "editutil.hxx"
44 #include "formulacell.hxx"
45 #include "validat.hxx"
46 #include "patattr.hxx"
47 #include "docpool.hxx"
48 #include "rangenam.hxx"
49 #include "arealink.hxx"
50 #include "stlsheet.hxx"
51 #include "scextopt.hxx"
52 #include "xlformula.hxx"
53 #include "xltracer.hxx"
54 #include "xistream.hxx"
55 #include "xihelper.hxx"
56 #include "xistyle.hxx"
57 #include "xiescher.hxx"
58 #include "xiname.hxx"
60 #include "excform.hxx"
61 #include "tabprotection.hxx"
62 #include "documentimport.hxx"
64 #include <memory>
65 #include <utility>
66 #include <o3tl/make_unique.hxx>
67 #include <oox/helper/helper.hxx>
69 using ::com::sun::star::uno::Sequence;
70 using ::std::unique_ptr;
72 // Shared string table ========================================================
74 XclImpSst::XclImpSst( const XclImpRoot& rRoot ) :
75 XclImpRoot( rRoot )
79 void XclImpSst::ReadSst( XclImpStream& rStrm )
81 rStrm.Ignore( 4 );
82 sal_uInt32 nStrCount = rStrm.ReaduInt32();
83 auto nBytesAvailable = rStrm.GetRecLeft();
84 if (nStrCount > nBytesAvailable)
86 SAL_WARN("sc.filter", "xls claimed to have " << nStrCount << " strings, but only " << nBytesAvailable << " bytes available, truncating");
87 nStrCount = nBytesAvailable;
89 maStrings.clear();
90 maStrings.reserve(nStrCount);
91 while( (nStrCount > 0) && rStrm.IsValid() )
93 XclImpString aString;
94 aString.Read( rStrm );
95 maStrings.push_back( aString );
96 --nStrCount;
100 const XclImpString* XclImpSst::GetString( sal_uInt32 nSstIndex ) const
102 return (nSstIndex < maStrings.size()) ? &maStrings[ nSstIndex ] : nullptr;
105 // Hyperlinks =================================================================
107 namespace {
109 /** Reads character array and stores it into rString.
110 @param nChars Number of following characters (not byte count!).
111 @param b16Bit true = 16-bit characters, false = 8-bit characters. */
112 void lclAppendString32( OUString& rString, XclImpStream& rStrm, sal_uInt32 nChars, bool b16Bit )
114 sal_uInt16 nReadChars = ulimit_cast< sal_uInt16 >( nChars );
115 rString += rStrm.ReadRawUniString( nReadChars, b16Bit );
116 // ignore remaining chars
117 sal_Size nIgnore = nChars - nReadChars;
118 if( b16Bit )
119 nIgnore *= 2;
120 rStrm.Ignore( nIgnore );
123 /** Reads 32-bit string length and the character array and stores it into rString.
124 @param b16Bit true = 16-bit characters, false = 8-bit characters. */
125 void lclAppendString32( OUString& rString, XclImpStream& rStrm, bool b16Bit )
127 lclAppendString32( rString, rStrm, rStrm.ReaduInt32(), b16Bit );
130 /** Reads 32-bit string length and ignores following character array.
131 @param b16Bit true = 16-bit characters, false = 8-bit characters. */
132 void lclIgnoreString32( XclImpStream& rStrm, bool b16Bit )
134 sal_uInt32 nChars(0);
135 nChars = rStrm.ReaduInt32();
136 if( b16Bit )
137 nChars *= 2;
138 rStrm.Ignore( nChars );
141 /** Converts a path to an absolute path.
142 @param rPath The source path. The resulting path is returned here.
143 @param nLevel Number of parent directories to add in front of the path. */
144 void lclGetAbsPath( OUString& rPath, sal_uInt16 nLevel, SfxObjectShell* pDocShell )
146 OUStringBuffer aTmpStr;
147 while( nLevel )
149 aTmpStr.append( "../" );
150 --nLevel;
152 aTmpStr.append( rPath );
154 if( pDocShell )
156 bool bWasAbs = false;
157 rPath = pDocShell->GetMedium()->GetURLObject().smartRel2Abs( aTmpStr.makeStringAndClear(), bWasAbs ).GetMainURL( INetURLObject::NO_DECODE );
158 // full path as stored in SvxURLField must be encoded
160 else
161 rPath = aTmpStr.makeStringAndClear();
164 /** Inserts the URL into a text cell. Does not modify value or formula cells. */
165 void lclInsertUrl( XclImpRoot& rRoot, const OUString& rUrl, SCCOL nScCol, SCROW nScRow, SCTAB nScTab )
167 ScDocumentImport& rDoc = rRoot.GetDocImport();
168 ScAddress aScPos( nScCol, nScRow, nScTab );
169 ScRefCellValue aCell(rDoc.getDoc(), aScPos);
170 switch( aCell.meType )
172 // #i54261# hyperlinks in string cells
173 case CELLTYPE_STRING:
174 case CELLTYPE_EDIT:
176 sal_uLong nNumFmt = rDoc.getDoc().GetNumberFormat(aScPos);
177 SvNumberFormatter* pFormatter = rDoc.getDoc().GetFormatTable();
178 Color* pColor;
179 OUString aDisplText;
180 ScCellFormat::GetString(aCell, nNumFmt, aDisplText, &pColor, *pFormatter, &rDoc.getDoc());
181 if (aDisplText.isEmpty())
182 aDisplText = rUrl;
184 ScEditEngineDefaulter& rEE = rRoot.GetEditEngine();
185 SvxURLField aUrlField( rUrl, aDisplText, SVXURLFORMAT_APPDEFAULT );
187 if( aCell.meType == CELLTYPE_EDIT )
189 const EditTextObject* pEditObj = aCell.mpEditText;
190 rEE.SetText( *pEditObj );
191 rEE.QuickInsertField( SvxFieldItem( aUrlField, EE_FEATURE_FIELD ), ESelection( 0, 0, EE_PARA_ALL, 0 ) );
193 else
195 rEE.SetText( EMPTY_OUSTRING );
196 rEE.QuickInsertField( SvxFieldItem( aUrlField, EE_FEATURE_FIELD ), ESelection() );
197 if( const ScPatternAttr* pPattern = rDoc.getDoc().GetPattern( aScPos.Col(), aScPos.Row(), nScTab ) )
199 SfxItemSet aItemSet( rEE.GetEmptyItemSet() );
200 pPattern->FillEditItemSet( &aItemSet );
201 rEE.QuickSetAttribs( aItemSet, ESelection( 0, 0, EE_PARA_ALL, 0 ) );
205 // The cell will own the text object instance.
206 rDoc.setEditCell(aScPos, rEE.CreateTextObject());
208 break;
210 default:
211 // Handle other cell types e.g. formulas ( and ? ) that have associated
212 // hyperlinks.
213 // Ideally all hyperlinks should be treated as below. For the moment,
214 // given the current absence of ods support lets just handle what we
215 // previously didn't handle the new way.
216 // Unfortunately we won't be able to preserve such hyperlinks when
217 // saving to ods. Note: when we are able to save such hyperlinks to ods
218 // we should handle *all* imported hyperlinks as below ( e.g. as cell
219 // attribute ) for better interoperability.
221 SfxStringItem aItem( ATTR_HYPERLINK, rUrl );
222 rDoc.getDoc().ApplyAttr(nScCol, nScRow, nScTab, aItem);
223 break;
228 } // namespace
230 void XclImpHyperlink::ReadHlink( XclImpStream& rStrm )
232 XclRange aXclRange( ScAddress::UNINITIALIZED );
233 rStrm >> aXclRange;
234 // #i80006# Excel silently ignores invalid hi-byte of column index (TODO: everywhere?)
235 aXclRange.maFirst.mnCol &= 0xFF;
236 aXclRange.maLast.mnCol &= 0xFF;
237 OUString aString = ReadEmbeddedData( rStrm );
238 if ( !aString.isEmpty() )
239 rStrm.GetRoot().GetXFRangeBuffer().SetHyperlink( aXclRange, aString );
242 OUString XclImpHyperlink::ReadEmbeddedData( XclImpStream& rStrm )
244 const XclImpRoot& rRoot = rStrm.GetRoot();
245 SfxObjectShell* pDocShell = rRoot.GetDocShell();
247 OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
249 XclGuid aGuid;
250 rStrm >> aGuid;
251 rStrm.Ignore( 4 );
252 sal_uInt32 nFlags(0);
253 nFlags = rStrm.ReaduInt32();
255 OSL_ENSURE( aGuid == XclTools::maGuidStdLink, "XclImpHyperlink::ReadEmbeddedData - unknown header GUID" );
257 ::std::unique_ptr< OUString > xLongName; // link / file name
258 ::std::unique_ptr< OUString > xShortName; // 8.3-representation of file name
259 ::std::unique_ptr< OUString > xTextMark; // text mark
261 // description (ignore)
262 if( ::get_flag( nFlags, EXC_HLINK_DESCR ) )
263 lclIgnoreString32( rStrm, true );
264 // target frame (ignore) !! DESCR/FRAME - is this the right order? (never seen them together)
265 if( ::get_flag( nFlags, EXC_HLINK_FRAME ) )
266 lclIgnoreString32( rStrm, true );
268 // URL fields are zero-terminated - do not let the stream replace them
269 // in the lclAppendString32() with the '?' character.
270 rStrm.SetNulSubstChar( '\0' );
272 // UNC path
273 if( ::get_flag( nFlags, EXC_HLINK_UNC ) )
275 xLongName.reset( new OUString );
276 lclAppendString32( *xLongName, rStrm, true );
277 lclGetAbsPath( *xLongName, 0, pDocShell );
279 // file link or URL
280 else if( ::get_flag( nFlags, EXC_HLINK_BODY ) )
282 rStrm >> aGuid;
284 if( aGuid == XclTools::maGuidFileMoniker )
286 sal_uInt16 nLevel = 0; // counter for level to climb down in path
287 nLevel = rStrm.ReaduInt16();
288 xShortName.reset( new OUString );
289 lclAppendString32( *xShortName, rStrm, false );
290 rStrm.Ignore( 24 );
292 sal_uInt32 nStrLen = 0;
293 nStrLen = rStrm.ReaduInt32();
294 if( nStrLen )
296 nStrLen = rStrm.ReaduInt32();
297 nStrLen /= 2; // it's byte count here...
298 rStrm.Ignore( 2 );
299 xLongName.reset( new OUString );
300 lclAppendString32( *xLongName, rStrm, nStrLen, true );
301 lclGetAbsPath( *xLongName, nLevel, pDocShell );
303 else
304 lclGetAbsPath( *xShortName, nLevel, pDocShell );
306 else if( aGuid == XclTools::maGuidUrlMoniker )
308 sal_uInt32 nStrLen(0);
309 nStrLen = rStrm.ReaduInt32();
310 nStrLen /= 2; // it's byte count here...
311 xLongName.reset( new OUString );
312 lclAppendString32( *xLongName, rStrm, nStrLen, true );
313 if( !::get_flag( nFlags, EXC_HLINK_ABS ) )
314 lclGetAbsPath( *xLongName, 0, pDocShell );
316 else
318 OSL_FAIL( "XclImpHyperlink::ReadEmbeddedData - unknown content GUID" );
322 // text mark
323 if( ::get_flag( nFlags, EXC_HLINK_MARK ) )
325 xTextMark.reset( new OUString );
326 lclAppendString32( *xTextMark, rStrm, true );
329 rStrm.SetNulSubstChar(); // back to default
331 OSL_ENSURE( rStrm.GetRecLeft() == 0, "XclImpHyperlink::ReadEmbeddedData - record size mismatch" );
333 if( !xLongName.get() && xShortName.get() )
334 xLongName = std::move(xShortName);
335 else if( !xLongName.get() && xTextMark.get() )
336 xLongName.reset( new OUString );
338 if( xLongName.get() )
340 if( xTextMark.get() )
342 if( xLongName->isEmpty() )
344 sal_Int32 nSepPos = xTextMark->lastIndexOf( '!' );
345 if( nSepPos > 0 )
347 // Do not attempt to blindly convert '#SheetName!A1' to
348 // '#SheetName.A1', it can be #SheetName!R1C1 as well.
349 // Hyperlink handler has to handle all, but prefer
350 // '#SheetName.A1' if possible.
351 if (nSepPos < xTextMark->getLength() - 1)
353 ScRange aRange;
354 if ((aRange.ParseAny( xTextMark->copy( nSepPos + 1 ), nullptr, formula::FormulaGrammar::CONV_XL_R1C1)
355 & ScRefFlags::VALID) == ScRefFlags::ZERO)
356 xTextMark.reset( new OUString( xTextMark->replaceAt( nSepPos, 1, OUString( '.' ))));
360 xLongName.reset( new OUString( *xLongName + "#" + *xTextMark ) );
362 return( *xLongName );
364 return( OUString() );
367 void XclImpHyperlink::ConvertToValidTabName(OUString& rUrl)
369 sal_Int32 n = rUrl.getLength();
370 if (n < 4)
371 // Needs at least 4 characters.
372 return;
374 if (rUrl[0] != '#')
375 // the 1st character must be '#'.
376 return;
378 OUString aNewUrl('#'), aTabName;
380 bool bInQuote = false;
381 bool bQuoteTabName = false;
382 for( sal_Int32 i = 1; i < n; ++i )
384 sal_Unicode c = rUrl[i];
385 if (c == '\'')
387 if (bInQuote && i+1 < n && rUrl[i+1] == '\'')
389 // Two consecutive single quotes ('') signify a single literal
390 // quite. When this occurs, the whole table name needs to be
391 // quoted.
392 bQuoteTabName = true;
393 aTabName += OUString(c);
394 aTabName += OUString(c);
395 ++i;
396 continue;
399 bInQuote = !bInQuote;
400 if (!bInQuote && !aTabName.isEmpty())
402 if (bQuoteTabName)
403 aNewUrl += "'";
404 aNewUrl += aTabName;
405 if (bQuoteTabName)
406 aNewUrl += "'";
409 else if (bInQuote)
410 aTabName += OUString(c);
411 else
412 aNewUrl += OUString(c);
415 if (bInQuote)
416 // It should be outside the quotes!
417 return;
419 // All is good. Pass the new URL.
420 rUrl = aNewUrl;
423 void XclImpHyperlink::InsertUrl( XclImpRoot& rRoot, const XclRange& rXclRange, const OUString& rUrl )
425 OUString aUrl(rUrl);
426 ConvertToValidTabName(aUrl);
428 SCTAB nScTab = rRoot.GetCurrScTab();
429 ScRange aScRange( ScAddress::UNINITIALIZED );
430 if( rRoot.GetAddressConverter().ConvertRange( aScRange, rXclRange, nScTab, nScTab, true ) )
432 SCCOL nScCol1, nScCol2;
433 SCROW nScRow1, nScRow2;
434 aScRange.GetVars( nScCol1, nScRow1, nScTab, nScCol2, nScRow2, nScTab );
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;
452 const ScRange* pScRange = nullptr;
454 XclRangeList aRowXclRanges, aColXclRanges;
455 rStrm >> aRowXclRanges >> aColXclRanges;
457 // row label ranges
458 ScRangeList aRowScRanges;
459 rAddrConv.ConvertRangeList( aRowScRanges, aRowXclRanges, nScTab, false );
460 xLabelRangesRef = rDoc.GetRowNameRangesRef();
461 for ( size_t i = 0, nRanges = aRowScRanges.size(); i < nRanges; ++i )
463 pScRange = aRowScRanges[ i ];
464 ScRange aDataRange( *pScRange );
465 if( aDataRange.aEnd.Col() < MAXCOL )
467 aDataRange.aStart.SetCol( aDataRange.aEnd.Col() + 1 );
468 aDataRange.aEnd.SetCol( MAXCOL );
470 else if( aDataRange.aStart.Col() > 0 )
472 aDataRange.aEnd.SetCol( aDataRange.aStart.Col() - 1 );
473 aDataRange.aStart.SetCol( 0 );
475 xLabelRangesRef->Append( ScRangePair( *pScRange, aDataRange ) );
478 // column label ranges
479 ScRangeList aColScRanges;
480 rAddrConv.ConvertRangeList( aColScRanges, aColXclRanges, nScTab, false );
481 xLabelRangesRef = rDoc.GetColNameRangesRef();
483 for ( size_t i = 0, nRanges = aColScRanges.size(); i < nRanges; ++i )
485 pScRange = aColScRanges[ i ];
486 ScRange aDataRange( *pScRange );
487 if( aDataRange.aEnd.Row() < MAXROW )
489 aDataRange.aStart.SetRow( aDataRange.aEnd.Row() + 1 );
490 aDataRange.aEnd.SetRow( MAXROW );
492 else if( aDataRange.aStart.Row() > 0 )
494 aDataRange.aEnd.SetRow( aDataRange.aStart.Row() - 1 );
495 aDataRange.aStart.SetRow( 0 );
497 xLabelRangesRef->Append( ScRangePair( *pScRange, aDataRange ) );
501 // Conditional formatting =====================================================
503 XclImpCondFormat::XclImpCondFormat( const XclImpRoot& rRoot, sal_uInt32 nFormatIndex ) :
504 XclImpRoot( rRoot ),
505 mnFormatIndex( nFormatIndex ),
506 mnCondCount( 0 ),
507 mnCondIndex( 0 )
511 XclImpCondFormat::~XclImpCondFormat()
515 void XclImpCondFormat::ReadCondfmt( XclImpStream& rStrm )
517 OSL_ENSURE( !mnCondCount, "XclImpCondFormat::ReadCondfmt - already initialized" );
518 XclRangeList aXclRanges;
519 mnCondCount = rStrm.ReaduInt16();
520 rStrm.Ignore( 10 );
521 rStrm >> aXclRanges;
522 GetAddressConverter().ConvertRangeList( maRanges, aXclRanges, GetCurrScTab(), true );
525 void XclImpCondFormat::ReadCF( XclImpStream& rStrm )
527 if( mnCondIndex >= mnCondCount )
529 OSL_FAIL( "XclImpCondFormat::ReadCF - CF without leading CONDFMT" );
530 return;
533 // entire conditional format outside of valid range?
534 if( maRanges.empty() )
535 return;
537 sal_uInt8 nType = rStrm.ReaduInt8();
538 sal_uInt8 nOperator = rStrm.ReaduInt8();
539 sal_uInt16 nFmlaSize1 = rStrm.ReaduInt16();
540 sal_uInt16 nFmlaSize2 = rStrm.ReaduInt16();
541 sal_uInt32 nFlags = rStrm.ReaduInt32();
542 rStrm.Ignore( 2 ); //nFlagsExtended
544 // *** mode and comparison operator ***
546 ScConditionMode eMode = SC_COND_NONE;
547 switch( nType )
549 case EXC_CF_TYPE_CELL:
551 switch( nOperator )
553 case EXC_CF_CMP_BETWEEN: eMode = SC_COND_BETWEEN; break;
554 case EXC_CF_CMP_NOT_BETWEEN: eMode = SC_COND_NOTBETWEEN; break;
555 case EXC_CF_CMP_EQUAL: eMode = SC_COND_EQUAL; break;
556 case EXC_CF_CMP_NOT_EQUAL: eMode = SC_COND_NOTEQUAL; break;
557 case EXC_CF_CMP_GREATER: eMode = SC_COND_GREATER; break;
558 case EXC_CF_CMP_LESS: eMode = SC_COND_LESS; break;
559 case EXC_CF_CMP_GREATER_EQUAL: eMode = SC_COND_EQGREATER; break;
560 case EXC_CF_CMP_LESS_EQUAL: eMode = SC_COND_EQLESS; break;
561 default:
562 SAL_INFO(
563 "sc.filter", "unknown CF comparison " << nOperator);
566 break;
568 case EXC_CF_TYPE_FMLA:
569 eMode = SC_COND_DIRECT;
570 break;
572 default:
573 SAL_INFO("sc.filter", "unknown CF mode " << nType);
574 return;
577 // *** create style sheet ***
579 OUString aStyleName( XclTools::GetCondFormatStyleName( GetCurrScTab(), mnFormatIndex, mnCondIndex ) );
580 SfxItemSet& rStyleItemSet = ScfTools::MakeCellStyleSheet( GetStyleSheetPool(), aStyleName, true ).GetItemSet();
582 const XclImpPalette& rPalette = GetPalette();
584 // number format
586 if( get_flag( nFlags, EXC_CF_BLOCK_NUMFMT ) )
588 XclImpNumFmtBuffer& rNumFmtBuffer = GetRoot().GetNumFmtBuffer();
589 bool bIFmt = get_flag( nFlags, EXC_CF_IFMT_USER );
590 sal_uInt16 nFormat = rNumFmtBuffer.ReadCFFormat( rStrm, bIFmt );
591 rNumFmtBuffer.FillToItemSet( rStyleItemSet, nFormat );
594 // *** font block ***
596 if( ::get_flag( nFlags, EXC_CF_BLOCK_FONT ) )
598 XclImpFont aFont( GetRoot() );
599 aFont.ReadCFFontBlock( rStrm );
600 aFont.FillToItemSet( rStyleItemSet, EXC_FONTITEM_CELL );
603 // alignment
604 if( get_flag( nFlags, EXC_CF_BLOCK_ALIGNMENT ) )
606 XclImpCellAlign aAlign;
607 sal_uInt16 nAlign(0);
608 sal_uInt16 nAlignMisc(0);
609 nAlign = rStrm.ReaduInt16();
610 nAlignMisc = rStrm.ReaduInt16();
611 aAlign.FillFromCF( nAlign, nAlignMisc );
612 aAlign.FillToItemSet( rStyleItemSet, nullptr );
613 rStrm.Ignore(4);
616 // *** border block ***
618 if( ::get_flag( nFlags, EXC_CF_BLOCK_BORDER ) )
620 sal_uInt16 nLineStyle(0);
621 sal_uInt32 nLineColor(0);
622 nLineStyle = rStrm.ReaduInt16();
623 nLineColor = rStrm.ReaduInt32();
624 rStrm.Ignore( 2 );
626 XclImpCellBorder aBorder;
627 aBorder.FillFromCF8( nLineStyle, nLineColor, nFlags );
628 aBorder.FillToItemSet( rStyleItemSet, rPalette );
631 // *** pattern block ***
633 if( ::get_flag( nFlags, EXC_CF_BLOCK_AREA ) )
635 sal_uInt16 nPattern(0), nColor(0);
636 nPattern = rStrm.ReaduInt16();
637 nColor = rStrm.ReaduInt16();
639 XclImpCellArea aArea;
640 aArea.FillFromCF8( nPattern, nColor, nFlags );
641 aArea.FillToItemSet( rStyleItemSet, rPalette );
644 if( get_flag( nFlags, EXC_CF_BLOCK_PROTECTION ) )
646 sal_uInt16 nCellProt;
647 nCellProt = rStrm.ReaduInt16();
648 XclImpCellProt aCellProt;
649 aCellProt.FillFromXF3(nCellProt);
650 aCellProt.FillToItemSet( rStyleItemSet );
653 // *** formulas ***
655 const ScAddress& rPos = maRanges.front()->aStart; // assured above that maRanges is not empty
656 ExcelToSc& rFmlaConv = GetOldFmlaConverter();
658 ::std::unique_ptr< ScTokenArray > xTokArr1;
659 if( nFmlaSize1 > 0 )
661 const ScTokenArray* pTokArr = nullptr;
662 rFmlaConv.Reset( rPos );
663 rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize1, false, FT_CondFormat );
664 // formula converter owns pTokArr -> create a copy of the token array
665 if( pTokArr )
666 xTokArr1.reset( pTokArr->Clone() );
669 ::std::unique_ptr< ScTokenArray > pTokArr2;
670 if( nFmlaSize2 > 0 )
672 const ScTokenArray* pTokArr = nullptr;
673 rFmlaConv.Reset( rPos );
674 rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize2, false, FT_CondFormat );
675 // formula converter owns pTokArr -> create a copy of the token array
676 if( pTokArr )
677 pTokArr2.reset( pTokArr->Clone() );
680 // *** create the Calc conditional formatting ***
682 if( !mxScCondFmt.get() )
684 sal_uLong nKey = 0;
685 mxScCondFmt.reset( new ScConditionalFormat( nKey, &GetDocRef() ) );
686 if(maRanges.size() > 1)
687 maRanges.Join(*maRanges[0], true);
688 mxScCondFmt->SetRange(maRanges);
691 ScCondFormatEntry* pEntry = new ScCondFormatEntry( eMode, xTokArr1.get(), pTokArr2.get(), &GetDocRef(), rPos, aStyleName );
692 mxScCondFmt->AddEntry( pEntry );
693 ++mnCondIndex;
696 void XclImpCondFormat::Apply()
698 if( mxScCondFmt.get() )
700 ScDocument& rDoc = GetDoc();
702 SCTAB nTab = maRanges.front()->aStart.Tab();
703 sal_uLong nKey = rDoc.AddCondFormat( mxScCondFmt->Clone(), nTab );
705 rDoc.AddCondFormatData( maRanges, nTab, nKey );
709 XclImpCondFormatManager::XclImpCondFormatManager( const XclImpRoot& rRoot ) :
710 XclImpRoot( rRoot )
714 void XclImpCondFormatManager::ReadCondfmt( XclImpStream& rStrm )
716 XclImpCondFormat* pFmt = new XclImpCondFormat( GetRoot(), maCondFmtList.size() );
717 pFmt->ReadCondfmt( rStrm );
718 maCondFmtList.push_back( std::unique_ptr<XclImpCondFormat>(pFmt) );
721 void XclImpCondFormatManager::ReadCF( XclImpStream& rStrm )
723 OSL_ENSURE( !maCondFmtList.empty(), "XclImpCondFormatManager::ReadCF - CF without leading CONDFMT" );
724 if( !maCondFmtList.empty() )
725 maCondFmtList.back()->ReadCF( rStrm );
728 void XclImpCondFormatManager::Apply()
730 for( XclImpCondFmtList::iterator itFmt = maCondFmtList.begin(); itFmt != maCondFmtList.end(); ++itFmt )
731 (*itFmt)->Apply();
732 maCondFmtList.clear();
735 // Data Validation ============================================================
737 XclImpValidationManager::DVItem::DVItem( const ScRangeList& rRanges, const ScValidationData& rValidData ) :
738 maRanges(rRanges), maValidData(rValidData) {}
740 XclImpValidationManager::XclImpValidationManager( const XclImpRoot& rRoot ) :
741 XclImpRoot( rRoot )
745 void XclImpValidationManager::ReadDval( XclImpStream& rStrm )
747 const XclImpRoot& rRoot = rStrm.GetRoot();
748 OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
750 sal_uInt32 nObjId(0);
751 rStrm.Ignore( 10 );
752 nObjId = rStrm.ReaduInt32();
753 if( nObjId != EXC_DVAL_NOOBJ )
755 OSL_ENSURE( nObjId <= 0xFFFF, "XclImpValidation::ReadDval - invalid object ID" );
756 rRoot.GetCurrSheetDrawing().SetSkipObj( static_cast< sal_uInt16 >( nObjId ) );
760 void XclImpValidationManager::ReadDV( XclImpStream& rStrm )
762 const XclImpRoot& rRoot = rStrm.GetRoot();
763 OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
765 ScDocument& rDoc = rRoot.GetDoc();
766 SCTAB nScTab = rRoot.GetCurrScTab();
767 ExcelToSc& rFmlaConv = rRoot.GetOldFmlaConverter();
769 // flags
770 sal_uInt32 nFlags(0);
771 nFlags = rStrm.ReaduInt32();
773 // message strings
774 /* Empty strings are single NUL characters in Excel (string length is 1).
775 -> Do not let the stream replace them with '?' characters. */
776 rStrm.SetNulSubstChar( '\0' );
777 OUString aPromptTitle( rStrm.ReadUniString() );
778 OUString aErrorTitle( rStrm.ReadUniString() );
779 OUString aPromptMessage( rStrm.ReadUniString() );
780 OUString aErrorMessage( rStrm.ReadUniString() );
781 rStrm.SetNulSubstChar(); // back to default
783 // formula(s)
784 if ( rStrm.GetRecLeft() <= 8 )
785 // Not enough bytes left in the record. Bail out.
786 return;
788 // first formula
789 // string list is single tStr token with NUL separators -> replace them with LF
790 rStrm.SetNulSubstChar( '\n' );
791 ::std::unique_ptr< ScTokenArray > xTokArr1;
793 // We can't import the formula directly because we need the range
794 sal_uInt16 nLenFormula1 = rStrm.ReaduInt16();
795 rStrm.Ignore( 2 );
796 XclImpStreamPos aPosFormula1;
797 rStrm.StorePosition(aPosFormula1);
798 rStrm.Ignore(nLenFormula1);
800 // second formula
801 ::std::unique_ptr< ScTokenArray > xTokArr2;
803 sal_uInt16 nLenFormula2 = rStrm.ReaduInt16();
804 rStrm.Ignore( 2 );
805 XclImpStreamPos aPosFormula2;
806 rStrm.StorePosition(aPosFormula2);
807 rStrm.Ignore(nLenFormula2);
809 // read all cell ranges
810 XclRangeList aXclRanges;
811 rStrm >> aXclRanges;
813 // convert to Calc range list
814 ScRangeList aScRanges;
815 rRoot.GetAddressConverter().ConvertRangeList( aScRanges, aXclRanges, nScTab, true );
817 // only continue if there are valid ranges
818 if ( aScRanges.empty() )
819 return;
821 ScRange aCombinedRange = aScRanges.Combine();
823 XclImpStreamPos aCurrentPos;
824 rStrm.StorePosition(aCurrentPos);
825 rStrm.RestorePosition(aPosFormula1);
826 if( nLenFormula1 > 0 )
828 const ScTokenArray* pTokArr = nullptr;
829 rFmlaConv.Reset(aCombinedRange.aStart);
830 rFmlaConv.Convert( pTokArr, rStrm, nLenFormula1, false, FT_CondFormat );
831 // formula converter owns pTokArr -> create a copy of the token array
832 if( pTokArr )
833 xTokArr1.reset( pTokArr->Clone() );
835 rStrm.SetNulSubstChar(); // back to default
836 if (nLenFormula2 > 0)
838 rStrm.RestorePosition(aPosFormula2);
839 const ScTokenArray* pTokArr = nullptr;
840 rFmlaConv.Reset(aCombinedRange.aStart);
841 rFmlaConv.Convert( pTokArr, rStrm, nLenFormula2, false, FT_CondFormat );
842 // formula converter owns pTokArr -> create a copy of the token array
843 if( pTokArr )
844 xTokArr2.reset( pTokArr->Clone() );
847 rStrm.RestorePosition(aCurrentPos);
849 bool bIsValid = true; // valid settings in flags field
851 ScValidationMode eValMode = SC_VALID_ANY;
852 switch( nFlags & EXC_DV_MODE_MASK )
854 case EXC_DV_MODE_ANY: eValMode = SC_VALID_ANY; break;
855 case EXC_DV_MODE_WHOLE: eValMode = SC_VALID_WHOLE; break;
856 case EXC_DV_MODE_DECIMAL: eValMode = SC_VALID_DECIMAL; break;
857 case EXC_DV_MODE_LIST: eValMode = SC_VALID_LIST; break;
858 case EXC_DV_MODE_DATE: eValMode = SC_VALID_DATE; break;
859 case EXC_DV_MODE_TIME: eValMode = SC_VALID_TIME; break;
860 case EXC_DV_MODE_TEXTLEN: eValMode = SC_VALID_TEXTLEN; break;
861 case EXC_DV_MODE_CUSTOM: eValMode = SC_VALID_CUSTOM; break;
862 default: bIsValid = false;
864 rRoot.GetTracer().TraceDVType(eValMode == SC_VALID_CUSTOM);
866 ScConditionMode eCondMode = SC_COND_BETWEEN;
867 switch( nFlags & EXC_DV_COND_MASK )
869 case EXC_DV_COND_BETWEEN: eCondMode = SC_COND_BETWEEN; break;
870 case EXC_DV_COND_NOTBETWEEN:eCondMode = SC_COND_NOTBETWEEN; break;
871 case EXC_DV_COND_EQUAL: eCondMode = SC_COND_EQUAL; break;
872 case EXC_DV_COND_NOTEQUAL: eCondMode = SC_COND_NOTEQUAL; break;
873 case EXC_DV_COND_GREATER: eCondMode = SC_COND_GREATER; break;
874 case EXC_DV_COND_LESS: eCondMode = SC_COND_LESS; break;
875 case EXC_DV_COND_EQGREATER: eCondMode = SC_COND_EQGREATER; break;
876 case EXC_DV_COND_EQLESS: eCondMode = SC_COND_EQLESS; break;
877 default: bIsValid = false;
880 if ( !bIsValid )
881 // No valid validation found. Bail out.
882 return;
884 // first range for base address for relative references
885 const ScRange& rScRange = *aScRanges.front(); // aScRanges is not empty
887 // process string list of a list validity (convert to list of string tokens)
888 if( xTokArr1.get() && (eValMode == SC_VALID_LIST) && ::get_flag( nFlags, EXC_DV_STRINGLIST ) )
889 XclTokenArrayHelper::ConvertStringToList(*xTokArr1, rDoc.GetSharedStringPool(), '\n', true);
891 maDVItems.push_back(
892 o3tl::make_unique<DVItem>(aScRanges, ScValidationData(eValMode, eCondMode, xTokArr1.get(), xTokArr2.get(), &rDoc, rScRange.aStart)));
893 DVItem& rItem = *maDVItems.back().get();
895 rItem.maValidData.SetIgnoreBlank( ::get_flag( nFlags, EXC_DV_IGNOREBLANK ) );
896 rItem.maValidData.SetListType( ::get_flagvalue( nFlags, EXC_DV_SUPPRESSDROPDOWN, css::sheet::TableValidationVisibility::INVISIBLE, css::sheet::TableValidationVisibility::UNSORTED ) );
898 // *** prompt box ***
899 if( !aPromptTitle.isEmpty() || !aPromptMessage.isEmpty() )
901 // set any text stored in the record
902 rItem.maValidData.SetInput( aPromptTitle, aPromptMessage );
903 if( !::get_flag( nFlags, EXC_DV_SHOWPROMPT ) )
904 rItem.maValidData.ResetInput();
907 // *** error box ***
908 ScValidErrorStyle eErrStyle = SC_VALERR_STOP;
909 switch( nFlags & EXC_DV_ERROR_MASK )
911 case EXC_DV_ERROR_WARNING: eErrStyle = SC_VALERR_WARNING; break;
912 case EXC_DV_ERROR_INFO: eErrStyle = SC_VALERR_INFO; break;
914 // set texts and error style
915 rItem.maValidData.SetError( aErrorTitle, aErrorMessage, eErrStyle );
916 if( !::get_flag( nFlags, EXC_DV_SHOWERROR ) )
917 rItem.maValidData.ResetError();
920 void XclImpValidationManager::Apply()
922 ScDocument& rDoc = GetRoot().GetDoc();
923 DVItemList::iterator itr = maDVItems.begin(), itrEnd = maDVItems.end();
924 for (; itr != itrEnd; ++itr)
926 DVItem& rItem = *itr->get();
927 // set the handle ID
928 sal_uLong nHandle = rDoc.AddValidationEntry( rItem.maValidData );
929 ScPatternAttr aPattern( rDoc.GetPool() );
930 aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_VALIDDATA, nHandle ) );
932 // apply all ranges
933 for ( size_t i = 0, nRanges = rItem.maRanges.size(); i < nRanges; ++i )
935 const ScRange* pScRange = rItem.maRanges[ i ];
936 rDoc.ApplyPatternAreaTab( pScRange->aStart.Col(), pScRange->aStart.Row(),
937 pScRange->aEnd.Col(), pScRange->aEnd.Row(), pScRange->aStart.Tab(), aPattern );
940 maDVItems.clear();
943 // Web queries ================================================================
945 XclImpWebQuery::XclImpWebQuery( const ScRange& rDestRange ) :
946 maDestRange( rDestRange ),
947 meMode( xlWQUnknown ),
948 mnRefresh( 0 )
952 void XclImpWebQuery::ReadParamqry( XclImpStream& rStrm )
954 sal_uInt16 nFlags = rStrm.ReaduInt16();
955 sal_uInt16 nType = ::extract_value< sal_uInt16 >( nFlags, 0, 3 );
956 if( (nType == EXC_PQRYTYPE_WEBQUERY) && ::get_flag( nFlags, EXC_PQRY_WEBQUERY ) )
958 if( ::get_flag( nFlags, EXC_PQRY_TABLES ) )
960 meMode = xlWQAllTables;
961 maTables = ScfTools::GetHTMLTablesName();
963 else
965 meMode = xlWQDocument;
966 maTables = ScfTools::GetHTMLDocName();
971 void XclImpWebQuery::ReadWqstring( XclImpStream& rStrm )
973 maURL = rStrm.ReadUniString();
976 void XclImpWebQuery::ReadWqsettings( XclImpStream& rStrm )
978 rStrm.Ignore( 10 );
979 sal_uInt16 nFlags(0);
980 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 OUString aQuotedPairs( "\"\"" );
997 sal_Int32 nTokenCnt = ScStringUtil::GetQuotedTokenCount( aTables, aQuotedPairs, ',' );
998 maTables.clear();
999 sal_Int32 nStringIx = 0;
1000 for( sal_Int32 nToken = 0; nToken < nTokenCnt; ++nToken )
1002 OUString aToken( ScStringUtil::GetQuotedToken( aTables, 0, aQuotedPairs, ',', nStringIx ) );
1003 sal_Int32 nTabNum = CharClass::isAsciiNumeric( aToken ) ? aToken.toInt32() : 0;
1004 if( nTabNum > 0 )
1005 maTables = ScGlobal::addToken( maTables, ScfTools::GetNameFromHTMLIndex( static_cast< sal_uInt32 >( nTabNum ) ), cSep );
1006 else
1008 ScGlobal::EraseQuotes( aToken, '"', false );
1009 if( !aToken.isEmpty() )
1010 maTables = ScGlobal::addToken( maTables, ScfTools::GetNameFromHTMLName( aToken ), cSep );
1016 void XclImpWebQuery::Apply( ScDocument& rDoc, const OUString& rFilterName )
1018 if( !maURL.isEmpty() && (meMode != xlWQUnknown) && rDoc.GetDocumentShell() )
1020 ScAreaLink* pLink = new ScAreaLink( rDoc.GetDocumentShell(),
1021 maURL, rFilterName, EMPTY_OUSTRING, maTables, maDestRange, mnRefresh * 60UL );
1022 rDoc.GetLinkManager()->InsertFileLink( *pLink, OBJECT_CLIENT_FILE,
1023 maURL, &rFilterName, &maTables );
1027 XclImpWebQueryBuffer::XclImpWebQueryBuffer( const XclImpRoot& rRoot ) :
1028 XclImpRoot( rRoot )
1032 void XclImpWebQueryBuffer::ReadQsi( XclImpStream& rStrm )
1034 if( GetBiff() == EXC_BIFF8 )
1036 rStrm.Ignore( 10 );
1037 OUString aXclName( rStrm.ReadUniString() );
1039 // #i64794# Excel replaces spaces with underscores
1040 aXclName = aXclName.replaceAll( " ", "_" );
1042 // find the defined name used in Calc
1043 if( const XclImpName* pName = GetNameManager().FindName( aXclName, GetCurrScTab() ) )
1045 if( const ScRangeData* pRangeData = pName->GetScRangeData() )
1047 ScRange aRange;
1048 if( pRangeData->IsReference( aRange ) )
1049 maWQList.push_back( XclImpWebQuery( aRange ) );
1053 else
1055 DBG_ERROR_BIFF();
1059 void XclImpWebQueryBuffer::ReadParamqry( XclImpStream& rStrm )
1061 if (!maWQList.empty())
1062 maWQList.back().ReadParamqry( rStrm );
1065 void XclImpWebQueryBuffer::ReadWqstring( XclImpStream& rStrm )
1067 if (!maWQList.empty())
1068 maWQList.back().ReadWqstring( rStrm );
1071 void XclImpWebQueryBuffer::ReadWqsettings( XclImpStream& rStrm )
1073 if (!maWQList.empty())
1074 maWQList.back().ReadWqsettings( rStrm );
1077 void XclImpWebQueryBuffer::ReadWqtables( XclImpStream& rStrm )
1079 if (!maWQList.empty())
1080 maWQList.back().ReadWqtables( rStrm );
1083 void XclImpWebQueryBuffer::Apply()
1085 ScDocument& rDoc = GetDoc();
1086 OUString aFilterName( EXC_WEBQRY_FILTER );
1087 for( XclImpWebQueryList::iterator itQuery = maWQList.begin(); itQuery != maWQList.end(); ++itQuery )
1088 itQuery->Apply( rDoc, aFilterName );
1091 // Decryption =================================================================
1093 namespace {
1095 XclImpDecrypterRef lclReadFilepass5( XclImpStream& rStrm )
1097 XclImpDecrypterRef xDecr;
1098 OSL_ENSURE( rStrm.GetRecLeft() == 4, "lclReadFilepass5 - wrong record size" );
1099 if( rStrm.GetRecLeft() == 4 )
1101 sal_uInt16 nKey(0), nHash(0);
1102 nKey = rStrm.ReaduInt16();
1103 nHash = rStrm.ReaduInt16();
1104 xDecr.reset( new XclImpBiff5Decrypter( nKey, nHash ) );
1106 return xDecr;
1109 XclImpDecrypterRef lclReadFilepass8_Standard( XclImpStream& rStrm )
1111 XclImpDecrypterRef xDecr;
1112 OSL_ENSURE( rStrm.GetRecLeft() == 48, "lclReadFilepass8 - wrong record size" );
1113 if( rStrm.GetRecLeft() == 48 )
1115 std::vector<sal_uInt8> aSalt(16);
1116 std::vector<sal_uInt8> aVerifier(16);
1117 std::vector<sal_uInt8> aVerifierHash(16);
1118 rStrm.Read(aSalt.data(), 16);
1119 rStrm.Read(aVerifier.data(), 16);
1120 rStrm.Read(aVerifierHash.data(), 16);
1121 xDecr.reset(new XclImpBiff8StdDecrypter(aSalt, aVerifier, aVerifierHash));
1123 return xDecr;
1126 XclImpDecrypterRef lclReadFilepass8_Strong(XclImpStream& rStream)
1128 //Its possible there are other variants in existance but these
1129 //are the defaults I get with Excel 2013
1130 XclImpDecrypterRef xDecr;
1132 msfilter::RC4EncryptionInfo info;
1134 info.header.flags = rStream.ReaduInt32();
1135 if (oox::getFlag( info.header.flags, msfilter::ENCRYPTINFO_EXTERNAL))
1136 return xDecr;
1138 sal_uInt32 nHeaderSize = rStream.ReaduInt32();
1139 sal_uInt32 actualHeaderSize = sizeof(info.header);
1141 if( (nHeaderSize < actualHeaderSize) )
1142 return xDecr;
1144 info.header.flags = rStream.ReaduInt32();
1145 info.header.sizeExtra = rStream.ReaduInt32();
1146 info.header.algId = rStream.ReaduInt32();
1147 info.header.algIdHash = rStream.ReaduInt32();
1148 info.header.keyBits = rStream.ReaduInt32();
1149 info.header.providedType = rStream.ReaduInt32();
1150 info.header.reserved1 = rStream.ReaduInt32();
1151 info.header.reserved2 = rStream.ReaduInt32();
1153 rStream.Ignore(nHeaderSize - actualHeaderSize);
1155 info.verifier.saltSize = rStream.ReaduInt32();
1156 if (info.verifier.saltSize != msfilter::SALT_LENGTH)
1157 return xDecr;
1158 rStream.Read(&info.verifier.salt, sizeof(info.verifier.salt));
1159 rStream.Read(&info.verifier.encryptedVerifier, sizeof(info.verifier.encryptedVerifier));
1161 info.verifier.encryptedVerifierHashSize = rStream.ReaduInt32();
1162 if (info.verifier.encryptedVerifierHashSize != RTL_DIGEST_LENGTH_SHA1)
1163 return xDecr;
1164 rStream.Read(&info.verifier.encryptedVerifierHash, info.verifier.encryptedVerifierHashSize);
1166 // check flags and algorithm IDs, required are AES128 and SHA-1
1167 if (!oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_CRYPTOAPI))
1168 return xDecr;
1170 if (oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_AES))
1171 return xDecr;
1173 if (info.header.algId != msfilter::ENCRYPT_ALGO_RC4)
1174 return xDecr;
1176 // hash algorithm ID 0 defaults to SHA-1 too
1177 if (info.header.algIdHash != 0 && info.header.algIdHash != msfilter::ENCRYPT_HASH_SHA1)
1178 return xDecr;
1180 xDecr.reset(new XclImpBiff8CryptoAPIDecrypter(
1181 std::vector<sal_uInt8>(info.verifier.salt,
1182 info.verifier.salt + SAL_N_ELEMENTS(info.verifier.salt)),
1183 std::vector<sal_uInt8>(info.verifier.encryptedVerifier,
1184 info.verifier.encryptedVerifier + SAL_N_ELEMENTS(info.verifier.encryptedVerifier)),
1185 std::vector<sal_uInt8>(info.verifier.encryptedVerifierHash,
1186 info.verifier.encryptedVerifierHash + SAL_N_ELEMENTS(info.verifier.encryptedVerifierHash))));
1188 return xDecr;
1191 XclImpDecrypterRef lclReadFilepass8( XclImpStream& rStrm )
1193 XclImpDecrypterRef xDecr;
1195 sal_uInt16 nMode(0);
1196 nMode = rStrm.ReaduInt16();
1197 switch( nMode )
1199 case EXC_FILEPASS_BIFF5:
1200 xDecr = lclReadFilepass5( rStrm );
1201 break;
1203 case EXC_FILEPASS_BIFF8:
1205 sal_uInt32 nVersion = rStrm.ReaduInt32();
1206 if (nVersion == msfilter::VERSION_INFO_1997_FORMAT)
1208 //A Version structure where Version.vMajor MUST be 0x0001,
1209 //and Version.vMinor MUST be 0x0001.
1210 xDecr = lclReadFilepass8_Standard(rStrm);
1212 else if (nVersion == msfilter::VERSION_INFO_2007_FORMAT ||
1213 nVersion == msfilter::VERSION_INFO_2007_FORMAT_SP2)
1215 //Version.vMajor MUST be 0x0002, 0x0003 or 0x0004 and
1216 //Version.vMinor MUST be 0x0002.
1217 xDecr = lclReadFilepass8_Strong(rStrm);
1219 else
1220 OSL_FAIL("lclReadFilepass8 - unknown BIFF8 encryption sub mode");
1222 break;
1224 default:
1225 OSL_FAIL( "lclReadFilepass8 - unknown encryption mode" );
1228 return xDecr;
1231 } // namespace
1233 ErrCode XclImpDecryptHelper::ReadFilepass( XclImpStream& rStrm )
1235 XclImpDecrypterRef xDecr;
1236 rStrm.DisableDecryption();
1238 // read the FILEPASS record and create a new decrypter object
1239 switch( rStrm.GetRoot().GetBiff() )
1241 case EXC_BIFF2:
1242 case EXC_BIFF3:
1243 case EXC_BIFF4:
1244 case EXC_BIFF5: xDecr = lclReadFilepass5( rStrm ); break;
1245 case EXC_BIFF8: xDecr = lclReadFilepass8( rStrm ); break;
1246 default: DBG_ERROR_BIFF();
1249 // set decrypter at import stream
1250 rStrm.SetDecrypter( xDecr );
1252 // request and verify a password (decrypter implements IDocPasswordVerifier)
1253 if( xDecr )
1254 rStrm.GetRoot().RequestEncryptionData( *xDecr );
1256 // return error code (success, wrong password, etc.)
1257 return xDecr ? xDecr->GetError() : EXC_ENCR_ERROR_UNSUPP_CRYPT;
1260 // Document protection ========================================================
1262 XclImpDocProtectBuffer::XclImpDocProtectBuffer( const XclImpRoot& rRoot ) :
1263 XclImpRoot( rRoot ),
1264 mnPassHash(0x0000),
1265 mbDocProtect(false),
1266 mbWinProtect(false)
1270 void XclImpDocProtectBuffer::ReadDocProtect( XclImpStream& rStrm )
1272 mbDocProtect = rStrm.ReaduInt16() != 0;
1275 void XclImpDocProtectBuffer::ReadWinProtect( XclImpStream& rStrm )
1277 mbWinProtect = rStrm.ReaduInt16() != 0;
1280 void XclImpDocProtectBuffer::ReadPasswordHash( XclImpStream& rStrm )
1282 rStrm.EnableDecryption();
1283 mnPassHash = rStrm.ReaduInt16();
1286 void XclImpDocProtectBuffer::Apply() const
1288 if (!mbDocProtect && !mbWinProtect)
1289 // Excel requires either the structure or windows protection is set.
1290 // If neither is set then the document is not protected at all.
1291 return;
1293 unique_ptr<ScDocProtection> pProtect(new ScDocProtection);
1294 pProtect->setProtected(true);
1296 if (mnPassHash)
1298 // 16-bit password hash.
1299 Sequence<sal_Int8> aPass(2);
1300 aPass[0] = (mnPassHash >> 8) & 0xFF;
1301 aPass[1] = mnPassHash & 0xFF;
1302 pProtect->setPasswordHash(aPass, PASSHASH_XL);
1305 // document protection options
1306 pProtect->setOption(ScDocProtection::STRUCTURE, mbDocProtect);
1307 pProtect->setOption(ScDocProtection::WINDOWS, mbWinProtect);
1309 GetDoc().SetDocProtection(pProtect.get());
1312 // Sheet Protection ===========================================================
1314 XclImpSheetProtectBuffer::Sheet::Sheet() :
1315 mbProtected(false),
1316 mnPasswordHash(0x0000),
1317 mnOptions(0x4400)
1321 XclImpSheetProtectBuffer::Sheet::Sheet(const Sheet& r) :
1322 mbProtected(r.mbProtected),
1323 mnPasswordHash(r.mnPasswordHash),
1324 mnOptions(r.mnOptions)
1328 XclImpSheetProtectBuffer::XclImpSheetProtectBuffer( const XclImpRoot& rRoot ) :
1329 XclImpRoot( rRoot )
1333 void XclImpSheetProtectBuffer::ReadProtect( XclImpStream& rStrm, SCTAB nTab )
1335 if ( rStrm.ReaduInt16() )
1337 Sheet* pSheet = GetSheetItem(nTab);
1338 if (pSheet)
1339 pSheet->mbProtected = true;
1343 void XclImpSheetProtectBuffer::ReadOptions( XclImpStream& rStrm, SCTAB nTab )
1345 // The flag size specifies the size of bytes that follows that stores
1346 // feature data. If -1 it depends on the feature type imported earlier.
1347 // For enhanced protection data, the size is always 4. For the most xls
1348 // documents out there this value is almost always -1.
1349 sal_Int32 nFlagSize(0);
1350 nFlagSize = rStrm.ReadInt32();
1351 if (nFlagSize != -1)
1352 return;
1354 // There are actually 4 bytes to read, but the upper 2 bytes currently
1355 // don't store any bits.
1356 sal_uInt16 nOptions(0);
1357 nOptions = rStrm.ReaduInt16();
1359 Sheet* pSheet = GetSheetItem(nTab);
1360 if (pSheet)
1361 pSheet->mnOptions = nOptions;
1364 void XclImpSheetProtectBuffer::AppendEnhancedProtection( const ScEnhancedProtection & rProt, SCTAB nTab )
1366 Sheet* pSheet = GetSheetItem(nTab);
1367 if (pSheet)
1368 pSheet->maEnhancedProtections.push_back( rProt);
1371 void XclImpSheetProtectBuffer::ReadPasswordHash( XclImpStream& rStrm, SCTAB nTab )
1373 sal_uInt16 nHash(0);
1374 nHash = rStrm.ReaduInt16();
1375 Sheet* pSheet = GetSheetItem(nTab);
1376 if (pSheet)
1377 pSheet->mnPasswordHash = nHash;
1380 void XclImpSheetProtectBuffer::Apply() const
1382 for (ProtectedSheetMap::const_iterator itr = maProtectedSheets.begin(), itrEnd = maProtectedSheets.end();
1383 itr != itrEnd; ++itr)
1385 if (!itr->second.mbProtected)
1386 // This sheet is (for whatever reason) not protected.
1387 continue;
1389 unique_ptr<ScTableProtection> pProtect(new ScTableProtection);
1390 pProtect->setProtected(true);
1392 // 16-bit hash password
1393 const sal_uInt16 nHash = itr->second.mnPasswordHash;
1394 if (nHash)
1396 Sequence<sal_Int8> aPass(2);
1397 aPass[0] = (nHash >> 8) & 0xFF;
1398 aPass[1] = nHash & 0xFF;
1399 pProtect->setPasswordHash(aPass, PASSHASH_XL);
1402 // sheet protection options
1403 const sal_uInt16 nOptions = itr->second.mnOptions;
1404 pProtect->setOption( ScTableProtection::OBJECTS, (nOptions & 0x0001) );
1405 pProtect->setOption( ScTableProtection::SCENARIOS, (nOptions & 0x0002) );
1406 pProtect->setOption( ScTableProtection::FORMAT_CELLS, (nOptions & 0x0004) );
1407 pProtect->setOption( ScTableProtection::FORMAT_COLUMNS, (nOptions & 0x0008) );
1408 pProtect->setOption( ScTableProtection::FORMAT_ROWS, (nOptions & 0x0010) );
1409 pProtect->setOption( ScTableProtection::INSERT_COLUMNS, (nOptions & 0x0020) );
1410 pProtect->setOption( ScTableProtection::INSERT_ROWS, (nOptions & 0x0040) );
1411 pProtect->setOption( ScTableProtection::INSERT_HYPERLINKS, (nOptions & 0x0080) );
1412 pProtect->setOption( ScTableProtection::DELETE_COLUMNS, (nOptions & 0x0100) );
1413 pProtect->setOption( ScTableProtection::DELETE_ROWS, (nOptions & 0x0200) );
1414 pProtect->setOption( ScTableProtection::SELECT_LOCKED_CELLS, (nOptions & 0x0400) );
1415 pProtect->setOption( ScTableProtection::SORT, (nOptions & 0x0800) );
1416 pProtect->setOption( ScTableProtection::AUTOFILTER, (nOptions & 0x1000) );
1417 pProtect->setOption( ScTableProtection::PIVOT_TABLES, (nOptions & 0x2000) );
1418 pProtect->setOption( ScTableProtection::SELECT_UNLOCKED_CELLS, (nOptions & 0x4000) );
1420 // Enhanced protection containing editable ranges and permissions.
1421 pProtect->setEnhancedProtection( itr->second.maEnhancedProtections);
1423 // all done. now commit.
1424 GetDoc().SetTabProtection(itr->first, pProtect.get());
1428 XclImpSheetProtectBuffer::Sheet* XclImpSheetProtectBuffer::GetSheetItem( SCTAB nTab )
1430 ProtectedSheetMap::iterator itr = maProtectedSheets.find(nTab);
1431 if (itr == maProtectedSheets.end())
1433 // new sheet
1434 if ( !maProtectedSheets.insert( ProtectedSheetMap::value_type(nTab, Sheet()) ).second )
1435 return nullptr;
1437 itr = maProtectedSheets.find(nTab);
1440 return &itr->second;
1443 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */