1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sd.hxx"
26 #include <propread.hxx>
27 #include <tools/bigint.hxx>
28 #include "tools/debug.hxx"
29 #include "rtl/tencinfo.h"
30 #include "rtl/textenc.h"
31
32 // ------------------------------------------------------------------------
33
34 struct PropEntry
35 {
36 sal_uInt32 mnId;
37 sal_uInt32 mnSize;
38 sal_uInt16 mnTextEnc;
39 sal_uInt8* mpBuf;
40
41 PropEntry( sal_uInt32 nId, const sal_uInt8* pBuf, sal_uInt32 nBufSize, sal_uInt16 nTextEnc );
42 PropEntry( const PropEntry& rProp );
~PropEntryPropEntry43 ~PropEntry() { delete[] mpBuf; } ;
44
45 const PropEntry& operator=(const PropEntry& rPropEntry);
46 };
47
PropEntry(sal_uInt32 nId,const sal_uInt8 * pBuf,sal_uInt32 nBufSize,sal_uInt16 nTextEnc)48 PropEntry::PropEntry( sal_uInt32 nId, const sal_uInt8* pBuf, sal_uInt32 nBufSize, sal_uInt16 nTextEnc ) :
49 mnId ( nId ),
50 mnSize ( nBufSize ),
51 mnTextEnc ( nTextEnc ),
52 mpBuf ( new sal_uInt8[ nBufSize ] )
53 {
54 memcpy( (void*)mpBuf, (void*)pBuf, nBufSize );
55 };
56
PropEntry(const PropEntry & rProp)57 PropEntry::PropEntry( const PropEntry& rProp ) :
58 mnId ( rProp.mnId ),
59 mnSize ( rProp.mnSize ),
60 mnTextEnc ( rProp.mnTextEnc ),
61 mpBuf ( new sal_uInt8[ mnSize ] )
62 {
63 memcpy( (void*)mpBuf, (void*)rProp.mpBuf, mnSize );
64 };
65
operator =(const PropEntry & rPropEntry)66 const PropEntry& PropEntry::operator=(const PropEntry& rPropEntry)
67 {
68 if ( this != &rPropEntry )
69 {
70 delete[] mpBuf;
71 mnId = rPropEntry.mnId;
72 mnSize = rPropEntry.mnSize;
73 mnTextEnc = rPropEntry.mnTextEnc;
74 mpBuf = new sal_uInt8[ mnSize ];
75 memcpy( (void*)mpBuf, (void*)rPropEntry.mpBuf, mnSize );
76 }
77 return *this;
78 }
79
80 // -----------------------------------------------------------------------
81
Clear()82 void PropItem::Clear()
83 {
84 Seek( STREAM_SEEK_TO_BEGIN );
85 delete[] (sal_uInt8*)SwitchBuffer();
86 }
87
88 // -----------------------------------------------------------------------
89
lcl_getMaxSafeStrLen(sal_uInt32 nSize)90 static xub_StrLen lcl_getMaxSafeStrLen(sal_uInt32 nSize)
91 {
92 nSize -= 1; //Drop NULL terminator
93
94 //If it won't fit in a string, clip it to the max size that does
95 if (nSize > STRING_MAXLEN)
96 nSize = STRING_MAXLEN;
97
98 return static_cast< xub_StrLen >( nSize );
99 }
100
Read(String & rString,sal_uInt32 nStringType,sal_Bool bAlign)101 sal_Bool PropItem::Read( String& rString, sal_uInt32 nStringType, sal_Bool bAlign )
102 {
103 sal_uInt32 i, nItemSize, nType, nItemPos;
104 sal_Bool bRetValue = sal_False;
105
106 nItemPos = Tell();
107
108 if ( nStringType == VT_EMPTY )
109 *this >> nType;
110 else
111 nType = nStringType & VT_TYPEMASK;
112
113 *this >> nItemSize;
114
115 switch( nType )
116 {
117 case VT_LPSTR :
118 {
119 if ( nItemSize )
120 {
121 try
122 {
123 sal_Char* pString = new sal_Char[ nItemSize ];
124 if ( mnTextEnc == RTL_TEXTENCODING_UCS2 )
125 {
126 nItemSize >>= 1;
127 if ( nItemSize > 1 )
128 {
129 sal_Unicode* pWString = (sal_Unicode*)pString;
130 for ( i = 0; i < nItemSize; i++ )
131 *this >> pWString[ i ];
132 rString = String( pWString, lcl_getMaxSafeStrLen(nItemSize) );
133 }
134 else
135 rString = String();
136 bRetValue = sal_True;
137 }
138 else
139 {
140 SvMemoryStream::Read( pString, nItemSize );
141 if ( pString[ nItemSize - 1 ] == 0 )
142 {
143 if ( nItemSize > 1 )
144 rString = String( ByteString( pString ), mnTextEnc );
145 else
146 rString = String();
147 bRetValue = sal_True;
148 }
149 }
150 delete[] pString;
151 }
152 catch( const std::bad_alloc& )
153 {
154 DBG_ERROR( "sd PropItem::Read bad alloc" );
155 }
156 }
157 if ( bAlign )
158 SeekRel( ( 4 - ( nItemSize & 3 ) ) & 3 ); // dword align
159 }
160 break;
161
162 case VT_LPWSTR :
163 {
164 if ( nItemSize )
165 {
166 try
167 {
168 sal_Unicode* pString = new sal_Unicode[ nItemSize ];
169 for ( i = 0; i < nItemSize; i++ )
170 *this >> pString[ i ];
171 if ( pString[ i - 1 ] == 0 )
172 {
173 if ( (sal_uInt16)nItemSize > 1 )
174 rString = String( pString, lcl_getMaxSafeStrLen(nItemSize) );
175 else
176 rString = String();
177 bRetValue = sal_True;
178 }
179 delete[] pString;
180 }
181 catch( const std::bad_alloc& )
182 {
183 DBG_ERROR( "sd PropItem::Read bad alloc" );
184 }
185 }
186 if ( bAlign && ( nItemSize & 1 ) )
187 SeekRel( 2 ); // dword align
188 }
189 break;
190 }
191 if ( !bRetValue )
192 Seek( nItemPos );
193 return bRetValue;
194 }
195
196 // -----------------------------------------------------------------------
197
operator =(PropItem & rPropItem)198 PropItem& PropItem::operator=( PropItem& rPropItem )
199 {
200 if ( this != &rPropItem )
201 {
202 Seek( STREAM_SEEK_TO_BEGIN );
203 delete[] (sal_uInt8*)SwitchBuffer();
204
205 mnTextEnc = rPropItem.mnTextEnc;
206 sal_uInt32 nItemPos = rPropItem.Tell();
207 rPropItem.Seek( STREAM_SEEK_TO_END );
208 SvMemoryStream::Write( rPropItem.GetData(), rPropItem.Tell() );
209 rPropItem.Seek( nItemPos );
210 }
211 return *this;
212 }
213
214 // -----------------------------------------------------------------------
215
216 struct Dict
217 {
218 sal_uInt32 mnId;
219 String aString;
220
DictDict221 Dict( sal_uInt32 nId, String rString ) { mnId = nId; aString = rString; };
222 };
223
224 // -----------------------------------------------------------------------
225
~Dictionary()226 Dictionary::~Dictionary()
227 {
228 for ( void* pPtr = First(); pPtr; pPtr = Next() )
229 delete (Dict*)pPtr;
230 }
231
232 // -----------------------------------------------------------------------
233
AddProperty(sal_uInt32 nId,const String & rString)234 void Dictionary::AddProperty( sal_uInt32 nId, const String& rString )
235 {
236 if ( rString.Len() ) // eindeutige namen bei properties
237 {
238 // pruefen, ob es die Propertybeschreibung in der Dictionary schon gibt
239 for ( Dict* pDict = (Dict*)First(); pDict; pDict = (Dict*)Next() )
240 {
241 if ( pDict->mnId == nId )
242 {
243 pDict->aString = rString;
244 return;
245 }
246 }
247 Insert( new Dict( nId, rString ), LIST_APPEND );
248 }
249 }
250
251 // -----------------------------------------------------------------------
252
GetProperty(const String & rString)253 sal_uInt32 Dictionary::GetProperty( const String& rString )
254 {
255 for ( Dict* pDict = (Dict*)First(); pDict; pDict = (Dict*)Next() )
256 {
257 if ( pDict->aString == rString )
258 return pDict->mnId;
259 }
260 return 0;
261 }
262
263 // -----------------------------------------------------------------------
264
operator =(Dictionary & rDictionary)265 Dictionary& Dictionary::operator=( Dictionary& rDictionary )
266 {
267 void* pPtr;
268
269 if ( this != &rDictionary )
270 {
271 for ( pPtr = First(); pPtr; pPtr = Next() )
272 delete (Dict*)pPtr;
273
274 for ( pPtr = rDictionary.First(); pPtr; pPtr = rDictionary.Next() )
275 Insert( new Dict( ((Dict*)pPtr)->mnId, ((Dict*)pPtr)->aString ), LIST_APPEND );
276 }
277 return *this;
278 }
279
280 // -----------------------------------------------------------------------
281
Section(Section & rSection)282 Section::Section( Section& rSection )
283 : List()
284 {
285 mnTextEnc = rSection.mnTextEnc;
286 for ( int i = 0; i < 16; i++ )
287 aFMTID[ i ] = rSection.aFMTID[ i ];
288 for ( PropEntry* pProp = (PropEntry*)rSection.First(); pProp; pProp = (PropEntry*)rSection.Next() )
289 Insert( new PropEntry( *pProp ), LIST_APPEND );
290 }
291
292 // -----------------------------------------------------------------------
293
Section(const sal_uInt8 * pFMTID)294 Section::Section( const sal_uInt8* pFMTID )
295 {
296 mnTextEnc = RTL_TEXTENCODING_MS_1252;
297 for ( int i = 0; i < 16; i++ )
298 aFMTID[ i ] = pFMTID[ i ];
299 }
300
301 // -----------------------------------------------------------------------
302
GetProperty(sal_uInt32 nId,PropItem & rPropItem)303 sal_Bool Section::GetProperty( sal_uInt32 nId, PropItem& rPropItem )
304 {
305 PropEntry* pProp;
306 if ( nId )
307 {
308 for ( pProp = (PropEntry*)First(); pProp; pProp = (PropEntry*)Next() )
309 {
310 if ( pProp->mnId == nId )
311 break;
312 }
313 if ( pProp )
314 {
315 rPropItem.Clear();
316 rPropItem.SetTextEncoding( mnTextEnc );
317 rPropItem.Write( pProp->mpBuf, pProp->mnSize );
318 rPropItem.Seek( STREAM_SEEK_TO_BEGIN );
319 return sal_True;
320 }
321 }
322 return sal_False;
323 }
324
325 // -----------------------------------------------------------------------
326
AddProperty(sal_uInt32 nId,const sal_uInt8 * pBuf,sal_uInt32 nBufSize)327 void Section::AddProperty( sal_uInt32 nId, const sal_uInt8* pBuf, sal_uInt32 nBufSize )
328 {
329 // kleiner id check
330
331 if ( !nId )
332 return;
333 if ( nId == 0xffffffff )
334 nId = 0;
335
336 // keine doppelten PropId's zulassen, sortieren
337 for ( sal_uInt32 i = 0; i < Count(); i++ )
338 {
339 PropEntry* pPropEntry = (PropEntry*)GetObject( i );
340 if ( pPropEntry->mnId == nId )
341 delete (PropEntry*)Replace( new PropEntry( nId, pBuf, nBufSize, mnTextEnc ), i );
342 else if ( pPropEntry->mnId > nId )
343 Insert( new PropEntry( nId, pBuf, nBufSize, mnTextEnc ), i );
344 else
345 continue;
346 return;
347 }
348 Insert( new PropEntry( nId, pBuf, nBufSize, mnTextEnc ), LIST_APPEND );
349 }
350
351 // -----------------------------------------------------------------------
352
GetDictionary(Dictionary & rDict)353 sal_Bool Section::GetDictionary( Dictionary& rDict )
354 {
355 sal_Bool bRetValue = sal_False;
356
357 Dictionary aDict;
358 PropEntry* pProp;
359
360 for ( pProp = (PropEntry*)First(); pProp; pProp = (PropEntry*)Next() )
361 {
362 if ( pProp->mnId == 0 )
363 break;
364 }
365 if ( pProp )
366 {
367 sal_uInt32 nDictCount, nId, nSize, nPos;
368 SvMemoryStream aStream( (sal_Int8*)pProp->mpBuf, pProp->mnSize, STREAM_READ );
369 aStream.Seek( STREAM_SEEK_TO_BEGIN );
370 aStream >> nDictCount;
371 for ( sal_uInt32 i = 0; i < nDictCount; i++ )
372 {
373 aStream >> nId >> nSize;
374 if ( nSize )
375 {
376 String aString;
377 nPos = aStream.Tell();
378 try
379 {
380 sal_Char* pString = new sal_Char[ nSize ];
381 aStream.Read( pString, nSize );
382 if ( mnTextEnc == RTL_TEXTENCODING_UCS2 )
383 {
384 nSize >>= 1;
385 aStream.Seek( nPos );
386 sal_Unicode* pWString = (sal_Unicode*)pString;
387 for ( i = 0; i < nSize; i++ )
388 aStream >> pWString[ i ];
389 aString = String( pWString, lcl_getMaxSafeStrLen(nSize) );
390 }
391 else
392 aString = String( ByteString( pString, lcl_getMaxSafeStrLen(nSize) ), mnTextEnc );
393 delete[] pString;
394 }
395 catch( const std::bad_alloc& )
396 {
397 DBG_ERROR( "sd Section::GetDictionary bad alloc" );
398 }
399 if ( !aString.Len() )
400 break;
401 aDict.AddProperty( nId, aString );
402 }
403 bRetValue = sal_True;
404 }
405 }
406 rDict = aDict;
407 return bRetValue;
408 }
409
410 // -----------------------------------------------------------------------
411
~Section()412 Section::~Section()
413 {
414 for ( PropEntry* pProp = (PropEntry*)First(); pProp; pProp = (PropEntry*)Next() )
415 delete pProp;
416 }
417
418 // -----------------------------------------------------------------------
419
Read(SvStorageStream * pStrm)420 void Section::Read( SvStorageStream *pStrm )
421 {
422 sal_uInt32 i, nSecOfs, nSecSize, nPropCount, nPropId, nPropOfs, nPropType, nPropSize, nCurrent, nVectorCount, nTemp, nStrmSize;
423 nSecOfs = pStrm->Tell();
424
425 pStrm->Seek( STREAM_SEEK_TO_END );
426 nStrmSize = pStrm->Tell();
427 pStrm->Seek( nSecOfs );
428
429 mnTextEnc = RTL_TEXTENCODING_MS_1252;
430 *pStrm >> nSecSize >> nPropCount;
431 while( nPropCount-- && ( pStrm->GetError() == ERRCODE_NONE ) )
432 {
433 *pStrm >> nPropId >> nPropOfs;
434 nCurrent = pStrm->Tell();
435 pStrm->Seek( nPropOfs + nSecOfs );
436 if ( nPropId ) // dictionary wird nicht eingelesen
437 {
438
439 *pStrm >> nPropType;
440
441 nPropSize = 4;
442
443 if ( nPropType & VT_VECTOR )
444 {
445 *pStrm >> nVectorCount;
446 nPropType &=~VT_VECTOR;
447 nPropSize += 4;
448 }
449 else
450 nVectorCount = 1;
451
452
453 sal_Bool bVariant = ( nPropType == VT_VARIANT );
454
455 for ( i = 0; nPropSize && ( i < nVectorCount ); i++ )
456 {
457 if ( bVariant )
458 {
459 *pStrm >> nPropType;
460 nPropSize += 4;
461 }
462 switch( nPropType )
463 {
464 case VT_UI1 :
465 nPropSize++;
466 break;
467
468 case VT_I2 :
469 case VT_UI2 :
470 case VT_BOOL :
471 nPropSize += 2;
472 break;
473
474 case VT_I4 :
475 case VT_R4 :
476 case VT_UI4 :
477 case VT_ERROR :
478 nPropSize += 4;
479 break;
480
481 case VT_I8 :
482 case VT_R8 :
483 case VT_CY :
484 case VT_UI8 :
485 case VT_DATE :
486 case VT_FILETIME :
487 nPropSize += 8;
488 break;
489
490 case VT_BSTR :
491 *pStrm >> nTemp;
492 nPropSize += ( nTemp + 4 );
493 break;
494
495 case VT_LPSTR :
496 *pStrm >> nTemp;
497 nPropSize += ( nTemp + 4 );
498 break;
499
500 case VT_LPWSTR :
501 *pStrm >> nTemp;
502 nPropSize += ( nTemp << 1 ) + 4;
503 break;
504
505 case VT_BLOB_OBJECT :
506 case VT_BLOB :
507 case VT_CF :
508 *pStrm >> nTemp;
509 nPropSize += ( nTemp + 4 );
510 break;
511
512 case VT_CLSID :
513 case VT_STREAM :
514 case VT_STORAGE :
515 case VT_STREAMED_OBJECT :
516 case VT_STORED_OBJECT :
517 case VT_VARIANT :
518 case VT_VECTOR :
519 default :
520 nPropSize = 0;
521 }
522 if ( nPropSize )
523 {
524 if ( ( nVectorCount - i ) > 1 )
525 pStrm->Seek( nPropOfs + nSecOfs + nPropSize );
526 }
527 else
528 break;
529 }
530 if ( nPropSize )
531 {
532 if ( nPropSize > nStrmSize )
533 {
534 nPropCount = 0;
535 break;
536 }
537 pStrm->Seek( nPropOfs + nSecOfs );
538 sal_uInt8* pBuf = new sal_uInt8[ nPropSize ];
539 pStrm->Read( pBuf, nPropSize );
540 AddProperty( nPropId, pBuf, nPropSize );
541 delete[] pBuf;
542 }
543 if ( nPropId == 1 )
544 {
545 PropItem aPropItem;
546 if ( GetProperty( 1, aPropItem ) )
547 {
548 sal_uInt16 nCodePage;
549 aPropItem >> nPropType;
550 if ( nPropType == VT_I2 )
551 {
552 aPropItem >> nCodePage;
553
554 if ( nCodePage == 1200 )
555 {
556 mnTextEnc = RTL_TEXTENCODING_UCS2;
557 }
558 else
559 {
560 mnTextEnc = rtl_getTextEncodingFromWindowsCodePage( nCodePage );
561 if ( mnTextEnc == RTL_TEXTENCODING_DONTKNOW )
562 mnTextEnc = RTL_TEXTENCODING_MS_1252;
563 }
564 }
565 else
566 {
567 mnTextEnc = RTL_TEXTENCODING_MS_1252;
568 }
569 }
570 }
571 }
572 else
573 {
574 sal_uInt32 nDictCount, nSize;
575 *pStrm >> nDictCount;
576 for ( i = 0; i < nDictCount; i++ )
577 {
578 *pStrm >> nSize >> nSize;
579 pStrm->SeekRel( nSize );
580 }
581 nSize = pStrm->Tell();
582 pStrm->Seek( nPropOfs + nSecOfs );
583 nSize -= pStrm->Tell();
584 if ( nSize > nStrmSize )
585 {
586 nPropCount = 0;
587 break;
588 }
589 sal_uInt8* pBuf = new sal_uInt8[ nSize ];
590 pStrm->Read( pBuf, nSize );
591 AddProperty( 0xffffffff, pBuf, nSize );
592 delete[] pBuf;
593 }
594 pStrm->Seek( nCurrent );
595 }
596 pStrm->Seek( nSecOfs + nSecSize );
597 }
598
599 // -----------------------------------------------------------------------
600
operator =(Section & rSection)601 Section& Section::operator=( Section& rSection )
602 {
603 PropEntry* pProp;
604
605 if ( this != &rSection )
606 {
607 memcpy( (void*)aFMTID, (void*)rSection.aFMTID, 16 );
608 for ( pProp = (PropEntry*)First(); pProp; pProp = (PropEntry*)Next() )
609 delete pProp;
610 Clear();
611 for ( pProp = (PropEntry*)rSection.First(); pProp; pProp = (PropEntry*)rSection.Next() )
612 Insert( new PropEntry( *pProp ), LIST_APPEND );
613 }
614 return *this;
615 }
616
617 // -----------------------------------------------------------------------
618
PropRead(SvStorage & rStorage,const String & rName)619 PropRead::PropRead( SvStorage& rStorage, const String& rName ) :
620 mbStatus ( sal_False ),
621 mnByteOrder ( 0xfffe ),
622 mnFormat ( 0 ),
623 mnVersionLo ( 4 ),
624 mnVersionHi ( 2 )
625 {
626 if ( rStorage.IsStream( rName ) )
627 {
628 mpSvStream = rStorage.OpenSotStream( rName, STREAM_STD_READ );
629 if ( mpSvStream )
630 {
631 mpSvStream->SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN );
632 memset( mApplicationCLSID, 0, 16 );
633 mbStatus = sal_True;
634 }
635 }
636 }
637
638 // -----------------------------------------------------------------------
639
AddSection(Section & rSection)640 void PropRead::AddSection( Section& rSection )
641 {
642 Insert( new Section( rSection ), LIST_APPEND );
643 }
644
645 // -----------------------------------------------------------------------
646
GetSection(const sal_uInt8 * pFMTID)647 const Section* PropRead::GetSection( const sal_uInt8* pFMTID )
648 {
649 Section* pSection;
650
651 for ( pSection = (Section*)First(); pSection; pSection = (Section*)Next() )
652 {
653 if ( memcmp( pSection->GetFMTID(), pFMTID, 16 ) == 0 )
654 break;
655 }
656 return pSection;
657 }
658
659 // -----------------------------------------------------------------------
660
~PropRead()661 PropRead::~PropRead()
662 {
663 for ( Section* pSection = (Section*)First(); pSection; pSection = (Section*)Next() )
664 delete pSection;
665 }
666
667 // -----------------------------------------------------------------------
668
Read()669 void PropRead::Read()
670 {
671 for ( Section* pSection = (Section*)First(); pSection; pSection = (Section*)Next() )
672 delete pSection;
673 Clear();
674 if ( mbStatus )
675 {
676 sal_uInt32 nSections;
677 sal_uInt32 nSectionOfs;
678 sal_uInt32 nCurrent;
679 *mpSvStream >> mnByteOrder >> mnFormat >> mnVersionLo >> mnVersionHi;
680 if ( mnByteOrder == 0xfffe )
681 {
682 sal_uInt8* pSectCLSID = new sal_uInt8[ 16 ];
683 mpSvStream->Read( mApplicationCLSID, 16 );
684 *mpSvStream >> nSections;
685 if ( nSections > 2 ) // sj: PowerPoint documents are containing max 2 sections
686 {
687 mbStatus = sal_False;
688 }
689 else for ( sal_uInt32 i = 0; i < nSections; i++ )
690 {
691 mpSvStream->Read( pSectCLSID, 16 );
692 *mpSvStream >> nSectionOfs;
693 nCurrent = mpSvStream->Tell();
694 mpSvStream->Seek( nSectionOfs );
695 Section aSection( pSectCLSID );
696 aSection.Read( mpSvStream );
697 AddSection( aSection );
698 mpSvStream->Seek( nCurrent );
699 }
700 delete[] pSectCLSID;
701 }
702 }
703 }
704
705 // -----------------------------------------------------------------------
706
operator =(PropRead & rPropRead)707 PropRead& PropRead::operator=( PropRead& rPropRead )
708 {
709 Section* pSection;
710
711 if ( this != &rPropRead )
712 {
713 mbStatus = rPropRead.mbStatus;
714 mpSvStream = rPropRead.mpSvStream;
715
716 mnByteOrder = rPropRead.mnByteOrder;
717 mnFormat = rPropRead.mnFormat;
718 mnVersionLo = rPropRead.mnVersionLo;
719 mnVersionHi = rPropRead.mnVersionHi;
720 memcpy( mApplicationCLSID, rPropRead.mApplicationCLSID, 16 );
721
722 for ( pSection = (Section*)First(); pSection; pSection = (Section*)Next() )
723 delete pSection;
724 Clear();
725 for ( pSection = (Section*)rPropRead.First(); pSection; pSection = (Section*)rPropRead.Next() )
726 Insert( new Section( *pSection ), LIST_APPEND );
727 }
728 return *this;
729 }
730