1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* libwps
3  * Version: MPL 2.0 / LGPLv2.1+
4  *
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/.
8  *
9  * Major Contributor(s):
10  * Copyright (C) 2009, 2011 Alonso Laurent (alonso@loria.fr)
11  * Copyright (C) 2006, 2007 Andrew Ziem
12  * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
13  * Copyright (C) 2004 Marc Maurer (uwog@uwog.net)
14  * Copyright (C) 2003-2005 William Lachance (william.lachance@sympatico.ca)
15  *
16  * For minor contributions see the git repository.
17  *
18  * Alternatively, the contents of this file may be used under the terms
19  * of the GNU Lesser General Public License Version 2.1 or later
20  * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
21  * applicable instead of those above.
22  *
23  * For further information visit http://libwps.sourceforge.net
24  */
25 
26 /*
27  *  freely inspired from istorage :
28  * ------------------------------------------------------------
29  *      Generic OLE Zones furnished with a copy of the file header
30  *
31  * Compound Storage (32 bit version)
32  * Storage implementation
33  *
34  * This file contains the compound file implementation
35  * of the storage interface.
36  *
37  * Copyright 1999 Francis Beaudet
38  * Copyright 1999 Sylvain St-Germain
39  * Copyright 1999 Thuy Nguyen
40  * Copyright 2005 Mike McCormack
41  *
42  * This library is free software; you can redistribute it and/or
43  * modify it under the terms of the GNU Lesser General Public
44  * License as published by the Free Software Foundation; either
45  * version 2.1 of the License, or (at your option) any later version.
46  *
47  * This library is distributed in the hope that it will be useful,
48  * but WITHOUT ANY WARRANTY; without even the implied warranty of
49  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
50  * Lesser General Public License for more details.
51  *
52  * You should have received a copy of the GNU Lesser General Public
53  * License along with this library; if not, write to the Free Software
54  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
55  *
56  */
57 
58 /*
59  * NOTES
60  *  The compound file implementation of IStorage used for create
61  *  and manage substorages and streams within a storage object
62  *  residing in a compound file object.
63  *
64  * ------------------------------------------------------------
65  */
66 
67 #include <cstdlib>
68 #include <cstring>
69 #include <map>
70 #include <sstream>
71 #include <string>
72 
73 #include <librevenge/librevenge.h>
74 
75 #include "WPSHeader.h"
76 #include "WPSPosition.h"
77 
78 #include "WPSOLEParser.h"
79 
80 using namespace libwps;
81 
82 //////////////////////////////////////////////////
83 // internal structure
84 //////////////////////////////////////////////////
85 namespace WPSOLEParserInternal
86 {
87 /** Internal: internal method to compobj definition */
88 class CompObj
89 {
90 public:
91 	//! the constructor
CompObj()92 	CompObj()
93 		: m_mapCls()
94 	{
95 		initCLSMap();
96 	}
97 
98 	/** return the CLS Name corresponding to an identifier */
getCLSName(unsigned long v)99 	char const *getCLSName(unsigned long v)
100 	{
101 		if (m_mapCls.find(v) == m_mapCls.end()) return nullptr;
102 		return m_mapCls[v];
103 	}
104 
105 protected:
106 	/** map CLSId <-> name */
107 	std::map<unsigned long, char const *> m_mapCls;
108 
109 	/** initialise a map CLSId <-> name */
initCLSMap()110 	void initCLSMap()
111 	{
112 		// source: binfilter/bf_so3/source/inplace/embobj.cxx
113 		m_mapCls[0x00000319]="Picture"; // addon Enhanced Metafile ( find in some file)
114 
115 		m_mapCls[0x00021290]="MSClipArtGalley2";
116 		m_mapCls[0x000212F0]="MSWordArt"; // or MSWordArt.2
117 		m_mapCls[0x00021302]="MSWorksREVENGEoc"; // addon
118 
119 		// MS Apps
120 		m_mapCls[0x00030000]= "ExcelWorksheet";
121 		m_mapCls[0x00030001]= "ExcelChart";
122 		m_mapCls[0x00030002]= "ExcelMacrosheet";
123 		m_mapCls[0x00030003]= "WordDocument";
124 		m_mapCls[0x00030004]= "MSPowerPoint";
125 		m_mapCls[0x00030005]= "MSPowerPointSho";
126 		m_mapCls[0x00030006]= "MSGraph";
127 		m_mapCls[0x00030007]= "MSDraw"; // find also ca003 ?
128 		m_mapCls[0x00030008]= "Note-It";
129 		m_mapCls[0x00030009]= "WordArt";
130 		m_mapCls[0x0003000a]= "PBrush";
131 		m_mapCls[0x0003000b]= "Equation"; // "Microsoft Equation Editor"
132 		m_mapCls[0x0003000c]= "Package";
133 		m_mapCls[0x0003000d]= "SoundRec";
134 		m_mapCls[0x0003000e]= "MPlayer";
135 		// MS Demos
136 		m_mapCls[0x0003000f]= "ServerDemo"; // "OLE 1.0 Server Demo"
137 		m_mapCls[0x00030010]= "Srtest"; // "OLE 1.0 Test Demo"
138 		m_mapCls[0x00030011]= "SrtInv"; //  "OLE 1.0 Inv Demo"
139 		m_mapCls[0x00030012]= "OleDemo"; //"OLE 1.0 Demo"
140 
141 		// Coromandel / Dorai Swamy / 718-793-7963
142 		m_mapCls[0x00030013]= "CoromandelIntegra";
143 		m_mapCls[0x00030014]= "CoromandelObjServer";
144 
145 		// 3-d Visions Corp / Peter Hirsch / 310-325-1339
146 		m_mapCls[0x00030015]= "StanfordGraphics";
147 
148 		// Deltapoint / Nigel Hearne / 408-648-4000
149 		m_mapCls[0x00030016]= "DGraphCHART";
150 		m_mapCls[0x00030017]= "DGraphDATA";
151 
152 		// Corel / Richard V. Woodend / 613-728-8200 x1153
153 		m_mapCls[0x00030018]= "PhotoPaint"; // "Corel PhotoPaint"
154 		m_mapCls[0x00030019]= "CShow"; // "Corel Show"
155 		m_mapCls[0x0003001a]= "CorelChart";
156 		m_mapCls[0x0003001b]= "CDraw"; // "Corel Draw"
157 
158 		// Inset Systems / Mark Skiba / 203-740-2400
159 		m_mapCls[0x0003001c]= "HJWIN1.0"; // "Inset Systems"
160 
161 		// Mark V Systems / Mark McGraw / 818-995-7671
162 		m_mapCls[0x0003001d]= "ObjMakerOLE"; // "MarkV Systems Object Maker"
163 
164 		// IdentiTech / Mike Gilger / 407-951-9503
165 		m_mapCls[0x0003001e]= "FYI"; // "IdentiTech FYI"
166 		m_mapCls[0x0003001f]= "FYIView"; // "IdentiTech FYI Viewer"
167 
168 		// Inventa Corporation / Balaji Varadarajan / 408-987-0220
169 		m_mapCls[0x00030020]= "Stickynote";
170 
171 		// ShapeWare Corp. / Lori Pearce / 206-467-6723
172 		m_mapCls[0x00030021]= "ShapewareVISIO10";
173 		m_mapCls[0x00030022]= "ImportServer"; // "Spaheware Import Server"
174 
175 		// test app SrTest
176 		m_mapCls[0x00030023]= "SrvrTest"; // "OLE 1.0 Server Test"
177 
178 		// test app ClTest.  Doesn't really work as a server but is in reg db
179 		m_mapCls[0x00030025]= "Cltest"; // "OLE 1.0 Client Test"
180 
181 		// Microsoft ClipArt Gallery   Sherry Larsen-Holmes
182 		m_mapCls[0x00030026]= "MS_ClipArt_Gallery";
183 		// Microsoft Project  Cory Reina
184 		m_mapCls[0x00030027]= "MSProject";
185 
186 		// Microsoft Works Chart
187 		m_mapCls[0x00030028]= "MSWorksChart";
188 
189 		// Microsoft Works Spreadsheet
190 		m_mapCls[0x00030029]= "MSWorksSpreadsheet";
191 
192 		// AFX apps - Dean McCrory
193 		m_mapCls[0x0003002A]= "MinSvr"; // "AFX Mini Server"
194 		m_mapCls[0x0003002B]= "HierarchyList"; // "AFX Hierarchy List"
195 		m_mapCls[0x0003002C]= "BibRef"; // "AFX BibRef"
196 		m_mapCls[0x0003002D]= "MinSvrMI"; // "AFX Mini Server MI"
197 		m_mapCls[0x0003002E]= "TestServ"; // "AFX Test Server"
198 
199 		// Ami Pro
200 		m_mapCls[0x0003002F]= "AmiProDocument";
201 
202 		// WordPerfect Presentations For Windows
203 		m_mapCls[0x00030030]= "WPGraphics";
204 		m_mapCls[0x00030031]= "WPCharts";
205 
206 		// MicroGrafx Charisma
207 		m_mapCls[0x00030032]= "Charisma";
208 		m_mapCls[0x00030033]= "Charisma_30"; // v 3.0
209 		m_mapCls[0x00030034]= "CharPres_30"; // v 3.0 Pres
210 		// MicroGrafx Draw
211 		m_mapCls[0x00030035]= "Draw"; //"MicroGrafx Draw"
212 		// MicroGrafx Designer
213 		m_mapCls[0x00030036]= "Designer_40"; // "MicroGrafx Designer 4.0"
214 
215 		// STAR DIVISION
216 		//m_mapCls[0x000424CA]= "StarMath"; // "StarMath 1.0"
217 		m_mapCls[0x00043AD2]= "FontWork"; // "Star FontWork"
218 		//m_mapCls[0x000456EE]= "StarMath2"; // "StarMath 2.0"
219 	}
220 };
221 
222 /** Internal: internal method to keep ole definition */
223 struct OleDef
224 {
OleDefWPSOLEParserInternal::OleDef225 	OleDef()
226 		: m_id(-1)
227 		, m_base("")
228 		, m_dir("")
229 		, m_name("") { }
230 	int m_id /**main id*/;
231 	std::string m_base/** the base name*/, m_dir/**the directory*/, m_name/**the complete name*/;
232 };
233 
234 /** Internal: the state of a WPSOLEParser */
235 struct State
236 {
237 	//! constructor
StateWPSOLEParserInternal::State238 	State(libwps_tools_win::Font::Type fontType, std::function<int(std::string const &)> const &dirToIdFunc)
239 		: m_fontType(fontType)
240 		, m_directoryToIdFunction(dirToIdFunc)
241 		, m_metaData()
242 		, m_unknownOLEs()
243 		, m_idToObjectMap()
244 		, m_compObjIdName()
245 	{
246 	}
247 	//! the default font type
248 	libwps_tools_win::Font::Type m_fontType;
249 	//! the function used to convert a directory name in a id
250 	std::function<int(std::string const &)> m_directoryToIdFunction;
251 	//! the meta data
252 	librevenge::RVNGPropertyList m_metaData;
253 	//! list of ole which can not be parsed
254 	std::vector<std::string> m_unknownOLEs;
255 	//! map id to object
256 	std::map<int, WPSEmbeddedObject> m_idToObjectMap;
257 
258 	//! a smart ptr used to stored the list of compobj id->name
259 	std::shared_ptr<WPSOLEParserInternal::CompObj> m_compObjIdName;
260 };
261 }
262 
263 // constructor/destructor
WPSOLEParser(const std::string & mainName,libwps_tools_win::Font::Type fontType,std::function<int (std::string const &)> const & dirToIdFunc)264 WPSOLEParser::WPSOLEParser(const std::string &mainName, libwps_tools_win::Font::Type fontType,
265                            std::function<int(std::string const &)> const &dirToIdFunc)
266 	: m_avoidOLE(mainName)
267 	, m_state(new WPSOLEParserInternal::State(fontType, dirToIdFunc))
268 {
269 }
270 
~WPSOLEParser()271 WPSOLEParser::~WPSOLEParser()
272 {
273 }
274 
275 
getNotParse() const276 std::vector<std::string> const &WPSOLEParser::getNotParse() const
277 {
278 	return m_state->m_unknownOLEs;
279 }
280 //! returns the list of data positions which have been read
getObjectsMap() const281 std::map<int,WPSEmbeddedObject> const &WPSOLEParser::getObjectsMap() const
282 {
283 	return m_state->m_idToObjectMap;
284 }
updateMetaData(librevenge::RVNGPropertyList & metaData) const285 void WPSOLEParser::updateMetaData(librevenge::RVNGPropertyList &metaData) const
286 {
287 	librevenge::RVNGPropertyList::Iter i(m_state->m_metaData);
288 	for (i.rewind(); i.next();)
289 	{
290 		if (!metaData[i.key()])
291 			metaData.insert(i.key(),i()->clone());
292 	}
293 }
294 // parsing
getIdFromDirectory(std::string const & dirName)295 int WPSOLEParser::getIdFromDirectory(std::string const &dirName)
296 {
297 	// try to retrieve the identificator stored in the directory
298 	//  MatOST/MatadorObject1/ -> 1, -1
299 	//  Object 2/ -> 2, -1
300 	auto dir=dirName+'/';
301 	auto pos = dir.find('/');
302 	while (pos != std::string::npos)
303 	{
304 		if (pos >= 1 && dir[pos-1] >= '0' && dir[pos-1] <= '9')
305 		{
306 			auto idP = pos-1;
307 			while (idP >=1 && dir[idP-1] >= '0' && dir[idP-1] <= '9')
308 				idP--;
309 			int val = std::atoi(dir.substr(idP, idP-pos).c_str());
310 			return val;
311 		}
312 		pos = dir.find('/', pos+1);
313 	}
314 	WPS_DEBUG_MSG(("WPSOLEParser::getIdFromDirectory: can not find id for %s\n", dirName.c_str()));
315 	return -1;
316 }
317 
parse(RVNGInputStreamPtr file)318 bool WPSOLEParser::parse(RVNGInputStreamPtr file)
319 {
320 	if (!m_state->m_compObjIdName)
321 		m_state->m_compObjIdName.reset(new WPSOLEParserInternal::CompObj);
322 
323 	m_state->m_unknownOLEs.resize(0);
324 	m_state->m_idToObjectMap.clear();
325 
326 	if (!file || !file->isStructured()) return false;
327 
328 	//
329 	// we begin by grouping the Ole by their potential main id
330 	//
331 	std::multimap<int, WPSOLEParserInternal::OleDef> listsById;
332 	std::vector<int> listIds;
333 	unsigned numSubStreams = file->subStreamCount();
334 	for (unsigned i = 0; i < numSubStreams; ++i)
335 	{
336 		char const *nm=file->subStreamName(i);
337 		if (!nm) continue;
338 		std::string name(nm);
339 		if (name.empty() || name[name.length()-1]=='/') continue;
340 		// separated the directory and the name
341 		//    MatOST/MatadorObject1/Ole10Native
342 		//      -> dir="MatOST/MatadorObject1", base="Ole10Native"
343 		auto pos = name.find_last_of('/');
344 
345 		std::string dir, base;
346 		if (pos == std::string::npos) base = name;
347 		else if (pos == 0) base = name.substr(1);
348 		else
349 		{
350 			dir = name.substr(0,pos);
351 			base = name.substr(pos+1);
352 		}
353 		if (dir == "" && base == m_avoidOLE) continue;
354 
355 #define PRINT_OLE_NAME
356 #ifdef PRINT_OLE_NAME
357 		WPS_DEBUG_MSG(("WPSOLEParser::parse: find OLEName=%s\n", name.c_str()));
358 #endif
359 		WPSOLEParserInternal::OleDef data;
360 		data.m_name = name;
361 		data.m_base = base;
362 		data.m_dir = dir;
363 		data.m_id = m_state->m_directoryToIdFunction(dir);
364 		if (listsById.find(data.m_id) == listsById.end())
365 			listIds.push_back(data.m_id);
366 		listsById.insert(std::multimap<int, WPSOLEParserInternal::OleDef>::value_type(data.m_id, data));
367 	}
368 
369 	for (auto id : listIds)
370 	{
371 		auto pos = listsById.lower_bound(id);
372 
373 		// try to find a representation for each id
374 		// FIXME: maybe we must also find some for each subid
375 		WPSEmbeddedObject pict;
376 
377 		while (pos != listsById.end())
378 		{
379 			auto const &dOle = pos->second;
380 			if (pos->first != id) break;
381 			++pos;
382 
383 			RVNGInputStreamPtr ole(file->getSubStreamByName(dOle.m_name.c_str()));
384 			if (!ole)
385 			{
386 				WPS_DEBUG_MSG(("WPSOLEParser: error: can not find OLE part: \"%s\"\n", dOle.m_name.c_str()));
387 				continue;
388 			}
389 
390 			libwps::DebugFile asciiFile(ole);
391 			asciiFile.open(dOle.m_name);
392 			bool ok = true;
393 
394 			try
395 			{
396 				librevenge::RVNGPropertyList pList;
397 				if (readMM(ole, dOle.m_base, asciiFile));
398 				else if (readSummaryInformation(ole, dOle.m_base, dOle.m_dir.empty() ? m_state->m_metaData : pList, asciiFile));
399 				else if (readObjInfo(ole, dOle.m_base, asciiFile));
400 				else if (readOle(ole, dOle.m_base, asciiFile));
401 				else if (readMN0AndCheckWKS(ole, dOle.m_base, pict, asciiFile));
402 				else if (isOlePres(ole, dOle.m_base) && readOlePres(ole, pict, asciiFile));
403 				else if (isOle10Native(ole, dOle.m_base) && readOle10Native(ole, pict, asciiFile));
404 				else if (readCompObj(ole, dOle.m_base, asciiFile));
405 				else if (readContents(ole, dOle.m_base, pict, asciiFile));
406 				else if (readCONTENTS(ole, dOle.m_base, pict, asciiFile));
407 				else
408 					ok = false;
409 			}
410 			catch (...)
411 			{
412 				ok = false;
413 			}
414 			if (!ok)
415 				m_state->m_unknownOLEs.push_back(dOle.m_name);
416 			asciiFile.reset();
417 		}
418 
419 		if (!pict.isEmpty())
420 			m_state->m_idToObjectMap[id]=pict;
421 	}
422 
423 	return true;
424 }
425 
426 
427 
428 ////////////////////////////////////////
429 //
430 // small structure
431 //
432 ////////////////////////////////////////
readOle(RVNGInputStreamPtr & ip,std::string const & oleName,libwps::DebugFile & ascii)433 bool WPSOLEParser::readOle(RVNGInputStreamPtr &ip, std::string const &oleName,
434                            libwps::DebugFile &ascii)
435 {
436 	if (!ip.get()) return false;
437 
438 	if (strcmp("Ole",oleName.c_str()) != 0) return false;
439 
440 	if (ip->seek(20, librevenge::RVNG_SEEK_SET) != 0 || ip->tell() != 20) return false;
441 
442 	ip->seek(0, librevenge::RVNG_SEEK_SET);
443 
444 	int val[20];
445 	for (int &i : val)
446 	{
447 		i = libwps::read8(ip);
448 		if (i < -10 || i > 10) return false;
449 	}
450 
451 	libwps::DebugStream f;
452 	f << "@@Ole: ";
453 	// always 1, 0, 2, 0*
454 	for (int i = 0; i < 20; i++)
455 		if (val[i]) f << "f" << i << "=" << val[i] << ",";
456 	ascii.addPos(0);
457 	ascii.addNote(f.str().c_str());
458 
459 	if (!ip->isEnd())
460 	{
461 		ascii.addPos(20);
462 		ascii.addNote("@@Ole:###");
463 	}
464 
465 	return true;
466 }
467 
readObjInfo(RVNGInputStreamPtr & input,std::string const & oleName,libwps::DebugFile & ascii)468 bool WPSOLEParser::readObjInfo(RVNGInputStreamPtr &input, std::string const &oleName,
469                                libwps::DebugFile &ascii)
470 {
471 	if (strcmp(oleName.c_str(),"ObjInfo") != 0) return false;
472 
473 	input->seek(14, librevenge::RVNG_SEEK_SET);
474 	if (input->tell() != 6 || !input->isEnd()) return false;
475 
476 	input->seek(0, librevenge::RVNG_SEEK_SET);
477 	libwps::DebugStream f;
478 	f << "@@ObjInfo:";
479 
480 	// always 0, 3, 4 ?
481 	for (int i = 0; i < 3; i++) f << libwps::read16(input) << ",";
482 
483 	ascii.addPos(0);
484 	ascii.addNote(f.str().c_str());
485 
486 	return true;
487 }
488 
readMM(RVNGInputStreamPtr & input,std::string const & oleName,libwps::DebugFile & ascii)489 bool WPSOLEParser::readMM(RVNGInputStreamPtr &input, std::string const &oleName,
490                           libwps::DebugFile &ascii)
491 {
492 	if (strcmp(oleName.c_str(),"MM") != 0) return false;
493 
494 	input->seek(14, librevenge::RVNG_SEEK_SET);
495 	if (input->tell() != 14 || !input->isEnd()) return false;
496 
497 	input->seek(0, librevenge::RVNG_SEEK_SET);
498 	int entete = libwps::readU16(input);
499 	if (entete != 0x444e)
500 	{
501 		if (entete == 0x4e44)
502 		{
503 			WPS_DEBUG_MSG(("WPSOLEParser::readMM: ERROR: endian mode probably bad, potentially bad PC/Mac mode detection.\n"));
504 		}
505 		return false;
506 	}
507 	libwps::DebugStream f;
508 	f << "@@MM:";
509 
510 	int val[6];
511 	for (int &i : val)
512 		i = libwps::read16(input);
513 
514 	switch (val[5])
515 	{
516 	case 0:
517 		f << "conversion,";
518 		break;
519 	case 2:
520 		f << "Works3,";
521 		break;
522 	case 4:
523 		f << "Works4,";
524 		break;
525 	default:
526 		f << "version=unknown,";
527 		break;
528 	}
529 
530 	// 1, 0, 0, 0, 0 : Mac file
531 	// 0, 1, 0, [0,1,2,4,6], 0 : Pc file
532 	for (int i = 0; i < 5; i++)
533 	{
534 		if ((i%2)!=1 && val[i]) f << "###";
535 		f << val[i] << ",";
536 	}
537 
538 	ascii.addPos(0);
539 	ascii.addNote(f.str().c_str());
540 
541 	return true;
542 }
543 
544 
readCompObj(RVNGInputStreamPtr & ip,std::string const & oleName,libwps::DebugFile & ascii)545 bool WPSOLEParser::readCompObj(RVNGInputStreamPtr &ip, std::string const &oleName, libwps::DebugFile &ascii)
546 {
547 	if (strncmp(oleName.c_str(), "CompObj", 7) != 0) return false;
548 
549 	// check minimal size
550 	const int minSize = 12 + 14+ 16 + 12; // size of header, clsid, footer, 3 string size
551 	if (ip->seek(minSize,librevenge::RVNG_SEEK_SET) != 0 || ip->tell() != minSize) return false;
552 
553 	libwps::DebugStream f;
554 	f << "@@CompObj(Header): ";
555 	ip->seek(0,librevenge::RVNG_SEEK_SET);
556 
557 	for (int i = 0; i < 6; i++)
558 	{
559 		auto val = long(libwps::readU16(ip));
560 		f << val << ", ";
561 	}
562 
563 	ascii.addPos(0);
564 	ascii.addNote(f.str().c_str());
565 
566 	ascii.addPos(12);
567 	// the clsid
568 	unsigned long clsData[4]; // ushort n1, n2, n3, b8, ... b15
569 	for (unsigned long &i : clsData)
570 		i = libwps::readU32(ip);
571 
572 	f.str("");
573 	f << "@@CompObj(CLSID):";
574 	if (clsData[1] == 0 && clsData[2] == 0xC0 && clsData[3] == 0x46000000L)
575 	{
576 		// normally, a referenced object
577 		char const *clsName = m_state->m_compObjIdName->getCLSName(clsData[0]);
578 		if (clsName)
579 			f << "'" << clsName << "'";
580 		else
581 		{
582 			WPS_DEBUG_MSG(("WPSOLEParser::readCompObj: unknown clsid=%lx\n", clsData[0]));
583 			f << "unknCLSID='" << std::hex << clsData[0] << "'";
584 		}
585 	}
586 	else
587 	{
588 		/* I found:
589 		  c1dbcd28e20ace11a29a00aa004a1a72     for MSWorks.Table
590 		  c2dbcd28e20ace11a29a00aa004a1a72     for Microsoft Works/MSWorksREVENGEoc
591 		  a3bcb394c2bd1b10a18306357c795b37     for Microsoft Drawing 1.01/MSDraw.1.01
592 		  b25aa40e0a9ed111a40700c04fb932ba     for Quill96 Story Group Class ( basic MSWorks doc?)
593 		  796827ed8bc9d111a75f00c04fb9667b     for MSWorks4Sheet
594 		*/
595 		f << "data0=(" << std::hex << clsData[0] << "," << clsData[1] << "), "
596 		  << "data1=(" << clsData[2] << "," << clsData[3] << ")";
597 	}
598 	ascii.addNote(f.str().c_str());
599 	f << std::dec;
600 	for (int ch = 0; ch < 3; ch++)
601 	{
602 		long actPos = ip->tell();
603 		long sz = libwps::read32(ip);
604 		bool waitNumber = sz == -1;
605 		if (waitNumber) sz = 4;
606 		if (sz < 0 || ip->seek(actPos+4+sz,librevenge::RVNG_SEEK_SET) != 0 ||
607 		        ip->tell() != actPos+4+sz) return false;
608 		ip->seek(actPos+4,librevenge::RVNG_SEEK_SET);
609 
610 		std::string st;
611 		if (waitNumber)
612 		{
613 			f.str("");
614 			f << libwps::read32(ip) << "[val*]";
615 			st = f.str();
616 		}
617 		else
618 		{
619 			for (int i = 0; i < sz; i++)
620 				st += char(libwps::readU8(ip));
621 		}
622 
623 		f.str("");
624 		f << "@@CompObj:";
625 		switch (ch)
626 		{
627 		case 0:
628 			f << "UserType=";
629 			break;
630 		case 1:
631 			f << "ClipName=";
632 			break;
633 		case 2:
634 			f << "ProgIdName=";
635 			break;
636 		default:
637 			break;
638 		}
639 		f << st;
640 		ascii.addPos(actPos);
641 		ascii.addNote(f.str().c_str());
642 	}
643 
644 	if (ip->isEnd()) return true;
645 
646 	long actPos = ip->tell();
647 	long nbElt = 4;
648 	if (ip->seek(actPos+16,librevenge::RVNG_SEEK_SET) != 0 ||
649 	        ip->tell() != actPos+16)
650 	{
651 		if ((ip->tell()-actPos)%4) return false;
652 		nbElt = (ip->tell()-actPos)/4;
653 	}
654 
655 	f.str("");
656 	f << "@@CompObj(Footer): " << std::hex;
657 	ip->seek(actPos,librevenge::RVNG_SEEK_SET);
658 	for (int i = 0; i < nbElt; i++)
659 		f << libwps::readU32(ip) << ",";
660 	ascii.addPos(actPos);
661 	ascii.addNote(f.str().c_str());
662 
663 	ascii.addPos(ip->tell());
664 
665 	return true;
666 }
667 
668 //////////////////////////////////////////////////
669 //
670 // OlePres001 seems to contained standart picture file and size
671 //    extract the picture if it is possible
672 //
673 //////////////////////////////////////////////////
674 
isOlePres(RVNGInputStreamPtr & ip,std::string const & oleName)675 bool WPSOLEParser::isOlePres(RVNGInputStreamPtr &ip, std::string const &oleName)
676 {
677 	if (!ip.get()) return false;
678 
679 	if (strncmp("OlePres",oleName.c_str(),7) != 0) return false;
680 
681 	if (ip->seek(40, librevenge::RVNG_SEEK_SET) != 0 || ip->tell() != 40) return false;
682 
683 	ip->seek(0, librevenge::RVNG_SEEK_SET);
684 	for (int i= 0; i < 2; i++)
685 	{
686 		long val = libwps::read32(ip);
687 		if (val < -10 || val > 10) return false;
688 	}
689 
690 	long actPos = ip->tell();
691 	int hSize = libwps::read32(ip);
692 	if (hSize < 4) return false;
693 	if (ip->seek(actPos+hSize+28, librevenge::RVNG_SEEK_SET) != 0
694 	        || ip->tell() != actPos+hSize+28)
695 		return false;
696 
697 	ip->seek(actPos+hSize, librevenge::RVNG_SEEK_SET);
698 	for (int i= 3; i < 7; i++)
699 	{
700 		long val = libwps::read32(ip);
701 		if (val < -10 || val > 10)
702 		{
703 			if (i != 5 || val > 256) return false;
704 		}
705 	}
706 
707 	ip->seek(8, librevenge::RVNG_SEEK_CUR);
708 	long size = libwps::read32(ip);
709 
710 	if (size <= 0) return ip->isEnd();
711 
712 	actPos = ip->tell();
713 	if (ip->seek(actPos+size, librevenge::RVNG_SEEK_SET) != 0
714 	        || ip->tell() != actPos+size)
715 		return false;
716 
717 	return true;
718 }
719 
readOlePres(RVNGInputStreamPtr & ip,WPSEmbeddedObject & obj,libwps::DebugFile & ascii)720 bool WPSOLEParser::readOlePres(RVNGInputStreamPtr &ip, WPSEmbeddedObject &obj, libwps::DebugFile &ascii)
721 {
722 	if (!isOlePres(ip, "OlePres")) return false;
723 
724 	libwps::DebugStream f;
725 	f << "@@OlePress(header): ";
726 	ip->seek(0,librevenge::RVNG_SEEK_SET);
727 	for (int i = 0; i < 2; i++)
728 	{
729 		long val = libwps::read32(ip);
730 		f << val << ", ";
731 	}
732 
733 	long actPos = ip->tell();
734 	int hSize = libwps::read32(ip);
735 	if (hSize < 4) return false;
736 	f << "hSize = " << hSize;
737 	ascii.addPos(0);
738 	ascii.addNote(f.str().c_str());
739 
740 	long endHPos = actPos+hSize;
741 	if (hSize > 4)   // CHECKME
742 	{
743 		bool ok = true;
744 		f.str("");
745 		f << "@@OlePress(headerA): ";
746 		if (hSize < 14) ok = false;
747 		else
748 		{
749 			// 12,21,32|48,0
750 			for (int i = 0; i < 4; i++) f << libwps::read16(ip) << ",";
751 			// 3 name of creator
752 			for (int ch=0; ch < 3; ch++)
753 			{
754 				std::string str;
755 				bool find = false;
756 				while (ip->tell() < endHPos)
757 				{
758 					unsigned char c = libwps::readU8(ip);
759 					if (c == 0)
760 					{
761 						find = true;
762 						break;
763 					}
764 					str += char(c);
765 				}
766 				if (!find)
767 				{
768 					ok = false;
769 					break;
770 				}
771 				f << ", name" <<  ch << "=" << str;
772 			}
773 			if (ok) ok = ip->tell() == endHPos;
774 		}
775 		// FIXME, normally they remain only a few bits (size unknown)
776 		if (!ok) f << "###";
777 		ascii.addPos(actPos);
778 		ascii.addNote(f.str().c_str());
779 	}
780 	if (ip->seek(endHPos+28, librevenge::RVNG_SEEK_SET) != 0
781 	        || ip->tell() != endHPos+28)
782 		return false;
783 
784 	ip->seek(endHPos, librevenge::RVNG_SEEK_SET);
785 
786 	actPos = ip->tell();
787 	f.str("");
788 	f << "@@OlePress(headerB): ";
789 	for (int i = 3; i < 7; i++)
790 	{
791 		long val = libwps::read32(ip);
792 		f << val << ", ";
793 	}
794 	// dim in TWIP ?
795 	auto extendX = long(libwps::readU32(ip));
796 	auto extendY = long(libwps::readU32(ip));
797 	if (extendX > 0 && extendY > 0 && obj.m_size!=Vec2f())
798 		obj.m_size=Vec2f(float(extendX)/1440.f, float(extendY)/1440.f);
799 	long fSize = libwps::read32(ip);
800 	f << "extendX="<< extendX << ", extendY=" << extendY << ", fSize=" << fSize;
801 
802 	ascii.addPos(actPos);
803 	ascii.addNote(f.str().c_str());
804 
805 	if (fSize == 0) return ip->isEnd();
806 
807 	librevenge::RVNGBinaryData data;
808 	if (!libwps::readData(ip, static_cast<unsigned long>(fSize), data)) return false;
809 	obj.add(data);
810 #ifdef DEBUG_WITH_FILES
811 	std::stringstream s;
812 	static int num=0;
813 	s << "OlePress" << num++;
814 	libwps::Debug::dumpFile(data, s.str().c_str());
815 #endif
816 	if (!ip->isEnd())
817 	{
818 		ascii.addPos(ip->tell());
819 		ascii.addNote("@@OlePress###");
820 	}
821 
822 	ascii.skipZone(36+hSize,36+hSize+fSize-1);
823 	return true;
824 }
825 
826 //////////////////////////////////////////////////
827 //
828 //  Ole10Native: basic Windows picture, with no size
829 //          - in general used to store a bitmap
830 //
831 //////////////////////////////////////////////////
832 
isOle10Native(RVNGInputStreamPtr & ip,std::string const & oleName)833 bool WPSOLEParser::isOle10Native(RVNGInputStreamPtr &ip, std::string const &oleName)
834 {
835 	if (strncmp("Ole10Native",oleName.c_str(),11) != 0) return false;
836 
837 	if (ip->seek(4, librevenge::RVNG_SEEK_SET) != 0 || ip->tell() != 4) return false;
838 
839 	ip->seek(0, librevenge::RVNG_SEEK_SET);
840 	long size = libwps::read32(ip);
841 
842 	if (size <= 0) return false;
843 	if (ip->seek(4+size, librevenge::RVNG_SEEK_SET) != 0 || ip->tell() != 4+size)
844 		return false;
845 
846 	return true;
847 }
848 
readOle10Native(RVNGInputStreamPtr & ip,WPSEmbeddedObject & obj,libwps::DebugFile & ascii)849 bool WPSOLEParser::readOle10Native(RVNGInputStreamPtr &ip, WPSEmbeddedObject &obj, libwps::DebugFile &ascii)
850 {
851 	if (!isOle10Native(ip, "Ole10Native")) return false;
852 
853 	libwps::DebugStream f;
854 	f << "@@Ole10Native(Header): ";
855 	ip->seek(0,librevenge::RVNG_SEEK_SET);
856 	long fSize = libwps::read32(ip);
857 	f << "fSize=" << fSize;
858 
859 	ascii.addPos(0);
860 	ascii.addNote(f.str().c_str());
861 
862 	librevenge::RVNGBinaryData data;
863 	if (!libwps::readData(ip, static_cast<unsigned long>(fSize), data)) return false;
864 	obj.add(data);
865 #ifdef DEBUG_WITH_FILES
866 	std::stringstream s;
867 	static int num=0;
868 	s << "Ole10_" << num++ << ".bmp";
869 	libwps::Debug::dumpFile(data, s.str().c_str());
870 #endif
871 
872 	if (!ip->isEnd())
873 	{
874 		ascii.addPos(ip->tell());
875 		ascii.addNote("@@Ole10Native###");
876 	}
877 	ascii.skipZone(4,4+fSize-1);
878 	return true;
879 }
880 
881 ////////////////////////////////////////////////////////////////
882 //
883 // In general a picture : a PNG, an JPEG, a basic metafile,
884 //    find also a MSDraw.1.01 picture (with first bytes 0x78563412="xV4") or WordArt,
885 //    ( with first bytes "WordArt" )  which are not sucefull read
886 //    (can probably contain a list of data, but do not know how to
887 //     detect that)
888 //
889 // To check: does this is related to MSO_BLIPTYPE ?
890 //        or OO/filter/sources/msfilter/msdffimp.cxx ?
891 //
892 ////////////////////////////////////////////////////////////////
readContents(RVNGInputStreamPtr & input,std::string const & oleName,WPSEmbeddedObject & obj,libwps::DebugFile & ascii)893 bool WPSOLEParser::readContents(RVNGInputStreamPtr &input,
894                                 std::string const &oleName,
895                                 WPSEmbeddedObject &obj,
896                                 libwps::DebugFile &ascii)
897 {
898 	if (strcmp(oleName.c_str(),"Contents") != 0) return false;
899 
900 	libwps::DebugStream f;
901 	input->seek(0, librevenge::RVNG_SEEK_SET);
902 	f << "@@Contents:";
903 
904 	bool ok = true;
905 	// bdbox 0 : size in the file ?
906 	int dim[2];
907 	dim[0] = libwps::read32(input);
908 	if (dim[0]==0x12345678)
909 	{
910 		WPS_DEBUG_MSG(("WPSOLEParser: warning: find a MSDraw picture, ignored\n"));
911 		ascii.addPos(0);
912 		ascii.addNote("Entries(MSDraw):");
913 		return false;
914 	}
915 	dim[1] = libwps::read32(input);
916 	f << "bdbox0=(" << dim[0] << "," << dim[1]<<"),";
917 	for (int i = 0; i < 3; i++)
918 	{
919 		/* 0,{10|21|75|101|116}x2 */
920 		auto val = long(libwps::readU32(input));
921 		if (val < 1000)
922 			f << val << ",";
923 		else
924 			f << std::hex << "0x" << val << std::dec << ",";
925 		if (val > 0x10000) ok=false;
926 	}
927 	// new bdbox : size of the picture ?
928 	int naturalSize[2];
929 	naturalSize[0] = libwps::read32(input);
930 	naturalSize[1] = libwps::read32(input);
931 	f << std::dec << "bdbox1=(" << naturalSize[0] << "," << naturalSize[1]<<"),";
932 	f << "unk=" << libwps::readU32(input) << ","; // 24 or 32
933 	if (input->isEnd())
934 	{
935 		WPS_DEBUG_MSG(("WPSOLEParser: warning: Contents header length\n"));
936 		return false;
937 	}
938 	long actPos = input->tell();
939 	auto size = long(libwps::readU32(input));
940 	if (size <= 0) ok = false;
941 	if (ok)
942 	{
943 		input->seek(actPos+size+4, librevenge::RVNG_SEEK_SET);
944 		if (input->tell() != actPos+size+4 || !input->isEnd())
945 		{
946 			ok = false;
947 			WPS_DEBUG_MSG(("WPSOLEParser: warning: Contents unexpected file size=%ld\n",
948 			               size));
949 		}
950 	}
951 
952 	if (!ok) f << "###";
953 	f << "dataSize=" << size;
954 
955 	ascii.addPos(0);
956 	ascii.addNote(f.str().c_str());
957 
958 	input->seek(actPos+4, librevenge::RVNG_SEEK_SET);
959 
960 	if (ok)
961 	{
962 		librevenge::RVNGBinaryData data;
963 		if (libwps::readData(input,static_cast<unsigned long>(size), data))
964 		{
965 			obj.add(data);
966 			ascii.skipZone(actPos+4, actPos+size+4-1);
967 #ifdef DEBUG_WITH_FILES
968 			std::stringstream s;
969 			static int fileId=0;
970 			s << oleName << ++fileId << "cntents.pict";
971 			libwps::Debug::dumpFile(data, s.str().c_str());
972 #endif
973 		}
974 		else
975 		{
976 			input->seek(actPos+4, librevenge::RVNG_SEEK_SET);
977 			ok = false;
978 		}
979 	}
980 	if (ok)
981 	{
982 		if (dim[0] > 0 && dim[0] < 3000 && dim[1] > 0 && dim[1] < 3000 && obj.m_size!=Vec2f())
983 			obj.m_size=Vec2f(float(dim[0])/72.f,float(dim[1])/72.f);
984 		else
985 		{
986 			WPS_DEBUG_MSG(("WPSOLEParser: warning: Contents odd size : %d %d\n", dim[0], dim[1]));
987 		}
988 		if (naturalSize[0] > 0 && naturalSize[0] < 5000 &&
989 		        naturalSize[1] > 0 && naturalSize[1] < 5000 && obj.m_size!=Vec2f())
990 			obj.m_size=Vec2f(float(dim[0])/72.f,float(dim[1])/72.f);
991 		else
992 		{
993 			WPS_DEBUG_MSG(("WPSOLEParser: warning: Contents odd naturalsize : %d %d\n", naturalSize[0], naturalSize[1]));
994 		}
995 	}
996 	if (!input->isEnd())
997 	{
998 		ascii.addPos(actPos);
999 		ascii.addNote("@@Contents:###");
1000 	}
1001 
1002 	if (!ok)
1003 	{
1004 		WPS_DEBUG_MSG(("WPSOLEParser: warning: read ole Contents: failed\n"));
1005 	}
1006 	return ok;
1007 }
1008 
1009 ////////////////////////////////////////////////////////////////
1010 //
1011 // Another different type of contents (this time in majuscule)
1012 // we seem to contain the header of a EMF and then the EMF file
1013 //
1014 ////////////////////////////////////////////////////////////////
readCONTENTS(RVNGInputStreamPtr & input,std::string const & oleName,WPSEmbeddedObject & obj,libwps::DebugFile & ascii)1015 bool WPSOLEParser::readCONTENTS(RVNGInputStreamPtr &input,
1016                                 std::string const &oleName,
1017                                 WPSEmbeddedObject &obj,
1018                                 libwps::DebugFile &ascii)
1019 {
1020 	if (strcmp(oleName.c_str(),"CONTENTS") != 0) return false;
1021 
1022 	libwps::DebugStream f;
1023 
1024 	input->seek(0, librevenge::RVNG_SEEK_SET);
1025 	f << "@@CONTENTS:";
1026 
1027 	auto hSize = long(libwps::readU32(input));
1028 	if (input->isEnd()) return false;
1029 	f << "hSize=" << std::hex << hSize << std::dec;
1030 
1031 	if (hSize <= 52 || input->seek(hSize+8,librevenge::RVNG_SEEK_SET) != 0
1032 	        || input->tell() != hSize+8)
1033 	{
1034 		WPS_DEBUG_MSG(("WPSOLEParser: warning: CONTENTS headerSize=%ld\n",
1035 		               hSize));
1036 		return false;
1037 	}
1038 
1039 	// minimal checking of the "copied" header
1040 	input->seek(4, librevenge::RVNG_SEEK_SET);
1041 	auto type = long(libwps::readU32(input));
1042 	if (type < 0 || type > 4) return false;
1043 	auto newSize = long(libwps::readU32(input));
1044 
1045 	f << ", type=" << type;
1046 	if (newSize < 8) return false;
1047 
1048 	if (newSize != hSize) // can sometimes happen, pb after a conversion ?
1049 		f << ", ###newSize=" << std::hex << newSize << std::dec;
1050 
1051 	// checkme: two bdbox, in document then data : units ?
1052 	//     Maybe first in POINT, second in TWIP ?
1053 	for (int st = 0; st < 2 ; st++)
1054 	{
1055 		int dim[4];
1056 		for (int &i : dim) i = libwps::read32(input);
1057 
1058 		bool ok = dim[0] >= 0 && dim[2] > dim[0] && dim[1] >= 0 && dim[3] > dim[2];
1059 		if (ok && st==0 && obj.m_size==Vec2f())
1060 			obj.m_size=Vec2f(float(dim[2]-dim[0])/72.f, float(dim[3]-dim[1])/72.f);
1061 		if (st==0) f << ", bdbox(Text)";
1062 		else f << ", bdbox(Data)";
1063 		if (!ok) f << "###";
1064 		f << "=(" << dim[0] << "x" << dim[1] << "<->" << dim[2] << "x" << dim[3] << ")";
1065 	}
1066 	char dataType[5];
1067 	for (int i = 0; i < 4; i++) dataType[i] = char(libwps::readU8(input));
1068 	dataType[4] = '\0';
1069 	f << ",typ=\""<<dataType<<"\""; // always " EMF" ?
1070 
1071 	for (int i = 0; i < 2; i++)   // always id0=0, id1=1 ?
1072 	{
1073 		int val = libwps::readU16(input);
1074 		if (val) f << ",id"<< i << "=" << val;
1075 	}
1076 	auto dataLength = long(libwps::readU32(input));
1077 	f << ",length=" << dataLength+hSize;
1078 
1079 	ascii.addPos(0);
1080 	ascii.addNote(f.str().c_str());
1081 
1082 	ascii.addPos(input->tell());
1083 	f.str("");
1084 	f << "@@CONTENTS(2)";
1085 	for (int i = 0; i < 12 && 4*i+52 < hSize; i++)
1086 	{
1087 		// f0=7,f1=1,f5=500,f6=320,f7=1c4,f8=11a
1088 		// or f0=a,f1=1,f2=2,f3=6c,f5=480,f6=360,f7=140,f8=f0
1089 		// or f0=61,f1=1,f2=2,f3=58,f5=280,f6=1e0,f7=a9,f8=7f
1090 		// f3=some header sub size ? f5/f6 and f7/f8 two other bdbox ?
1091 		auto val = long(libwps::readU32(input));
1092 		if (val) f << std::hex << ",f" << i << "=" << val;
1093 	}
1094 	for (int i = 0; 2*i+100 < hSize; i++)
1095 	{
1096 		// g0=e3e3,g1=6,g2=4e6e,g3=4
1097 		// g0=e200,g1=4,g2=a980,g3=3,g4=4c,g5=50
1098 		// ---
1099 		long val = libwps::readU16(input);
1100 		if (val) f << std::hex << ",g" << i << "=" << val;
1101 	}
1102 	ascii.addNote(f.str().c_str());
1103 
1104 	if (dataLength <= 0 || input->seek(hSize+4+dataLength,librevenge::RVNG_SEEK_SET) != 0
1105 	        || input->tell() != hSize+4+dataLength || !input->isEnd())
1106 	{
1107 		WPS_DEBUG_MSG(("WPSOLEParser: warning: CONTENTS unexpected file length=%ld\n",
1108 		               dataLength));
1109 		return false;
1110 	}
1111 
1112 	input->seek(4+hSize, librevenge::RVNG_SEEK_SET);
1113 	librevenge::RVNGBinaryData data;
1114 	if (!libwps::readDataToEnd(input, data)) return false;
1115 	obj.add(data);
1116 #ifdef DEBUG_WITH_FILES
1117 	std::stringstream s;
1118 	static int fileId=0;
1119 	s << oleName << ++fileId << "Contents.pict";
1120 	libwps::Debug::dumpFile(data, s.str().c_str());
1121 #endif
1122 
1123 	ascii.skipZone(hSize+4, input->tell()-1);
1124 	return true;
1125 }
1126 
1127 ////////////////////////////////////////////////////////////////
1128 //
1129 // the MN0 subdirectory
1130 //
1131 ////////////////////////////////////////////////////////////////
readMN0AndCheckWKS(RVNGInputStreamPtr & input,std::string const & oleName,WPSEmbeddedObject & obj,libwps::DebugFile &)1132 bool WPSOLEParser::readMN0AndCheckWKS(RVNGInputStreamPtr &input, std::string const &oleName,
1133                                       WPSEmbeddedObject &obj, libwps::DebugFile &/*ascii*/)
1134 {
1135 	if (strcmp(oleName.c_str(),"MN0") != 0) return false;
1136 	std::unique_ptr<WPSHeader> header(WPSHeader::constructHeader(input));
1137 	if (!header) return false;
1138 	bool ok=header->getKind()==WPS_SPREADSHEET;
1139 	if (!ok) return false;
1140 
1141 	input->seek(0, librevenge::RVNG_SEEK_SET);
1142 	librevenge::RVNGBinaryData data;
1143 	if (!libwps::readDataToEnd(input, data))
1144 		return false;
1145 	obj.add(data,"image/wks-ods");
1146 #ifdef DEBUG_WITH_FILES
1147 	std::stringstream s;
1148 	static int fileId=0;
1149 	s << oleName << ++fileId << ".wks";
1150 	libwps::Debug::dumpFile(data, s.str().c_str());
1151 #endif
1152 	return true;
1153 }
1154 
1155 ////////////////////////////////////////////////////////////
1156 //
1157 // Summary function
1158 //
1159 ////////////////////////////////////////////////////////////
readSummaryInformation(RVNGInputStreamPtr input,std::string const & oleName,librevenge::RVNGPropertyList & pList,libwps::DebugFile & ascii) const1160 bool WPSOLEParser::readSummaryInformation(RVNGInputStreamPtr input, std::string const &oleName,
1161                                           librevenge::RVNGPropertyList &pList, libwps::DebugFile &ascii) const
1162 {
1163 	if (oleName!="SummaryInformation") return false;
1164 	input->seek(0, librevenge::RVNG_SEEK_END);
1165 	long endPos=input->tell();
1166 	input->seek(0, librevenge::RVNG_SEEK_SET);
1167 	libwps::DebugStream f;
1168 	f << "Entries(SumInfo):";
1169 	auto val=int(libwps::readU16(input));
1170 	if (endPos<48 || val!=0xfffe)
1171 	{
1172 		WPS_DEBUG_MSG(("WPSOLEParser::readSummaryInformation: header seems bad\n"));
1173 		f << "###";
1174 		ascii.addPos(0);
1175 		ascii.addNote(f.str().c_str());
1176 		return true;
1177 	}
1178 	for (int i=0; i<11; ++i)   // f1=1, f2=0-2
1179 	{
1180 		val=int(libwps::readU16(input));
1181 		if (val) f << "f" << i << "=" << val << ",";
1182 	}
1183 	unsigned long lVal=libwps::readU32(input);
1184 	if (lVal==0 || lVal>15)   // find 1 or 2 sections, unsure about the maximum numbers
1185 	{
1186 		WPS_DEBUG_MSG(("WPSOLEParser::readSummaryInformation: summary info is bad\n"));
1187 		f << "###sumInfo=" << std::hex << lVal << std::dec << ",";
1188 		ascii.addPos(0);
1189 		ascii.addNote(f.str().c_str());
1190 		return true;
1191 	}
1192 	auto numSection=int(lVal);
1193 	if (numSection!=1)
1194 		f << "num[section]=" << numSection << ",";
1195 	for (int i=0; i<4; ++i)
1196 	{
1197 		val=int(libwps::readU32(input));
1198 		static int const expected[]= {int(0xf29f85e0),0x10684ff9,0x891ab,int(0xd9b3272b)};
1199 		if (val==expected[i]) continue;
1200 		f << "#fmid" << i << "=" << std::hex << val << std::dec << ",";
1201 		static bool first=true;
1202 		if (first)
1203 		{
1204 			WPS_DEBUG_MSG(("WPSOLEParser::readSummaryInformation: fmid is bad\n"));
1205 			first=false;
1206 		}
1207 	}
1208 	auto decal=int(libwps::readU32(input));
1209 	if (decal<0x30 || endPos<decal)
1210 	{
1211 		WPS_DEBUG_MSG(("WPSOLEParser::readSummaryInformation: decal is bad\n"));
1212 		f << "decal=" << val << ",";
1213 		ascii.addPos(0);
1214 		ascii.addNote(f.str().c_str());
1215 		return true;
1216 
1217 	}
1218 	ascii.addPos(0);
1219 	ascii.addNote(f.str().c_str());
1220 	if (decal!=0x30)
1221 	{
1222 		ascii.addPos(0x30);
1223 		ascii.addNote("_");
1224 		input->seek(decal, librevenge::RVNG_SEEK_SET);
1225 	}
1226 
1227 	for (int sect=0; sect<numSection; ++sect)
1228 	{
1229 		long pos=input->tell();
1230 		f.str("");
1231 		f << "SumInfo-A:";
1232 		auto pSectSize=long(libwps::readU32(input));
1233 		long endSect=pos+pSectSize;
1234 		auto N=int(libwps::readU32(input));
1235 		f << "N=" << N << ",";
1236 		if (pSectSize<0 || endPos-pos<pSectSize || (pSectSize-8)/8<N)
1237 		{
1238 			WPS_DEBUG_MSG(("WPSOLEParser::readSummaryInformation: psetstruct is bad\n"));
1239 			f << "###";
1240 			ascii.addPos(pos);
1241 			ascii.addNote(f.str().c_str());
1242 			return true;
1243 		}
1244 		f << "[";
1245 		std::map<long,int> posToTypeMap;
1246 		for (int i=0; i<N; ++i)
1247 		{
1248 			auto type=int(libwps::readU32(input));
1249 			auto depl=int(libwps::readU32(input));
1250 			if (depl<=0) continue;
1251 			f << std::hex << depl << std::dec << ":" << type << ",";
1252 			if ((depl-8)/8<N || depl>pSectSize-4 || posToTypeMap.find(pos+depl)!=posToTypeMap.end())
1253 			{
1254 				f << "###";
1255 				continue;
1256 			}
1257 			posToTypeMap[pos+depl]=type;
1258 		}
1259 		f << "],";
1260 		ascii.addPos(pos);
1261 		ascii.addNote(f.str().c_str());
1262 
1263 		for (auto it=posToTypeMap.begin(); it!=posToTypeMap.end(); ++it)
1264 		{
1265 			pos=it->first;
1266 			auto nextIt=it;
1267 			if (++nextIt!=posToTypeMap.end()) endPos=nextIt->first;
1268 			input->seek(pos, librevenge::RVNG_SEEK_SET);
1269 			f.str("");
1270 			f << "SumInfo-B" << it->second << ":";
1271 			auto type=int(libwps::readU32(input));
1272 			if (sect==0 && it->second==1 && type==2)
1273 			{
1274 				long value=-1;
1275 				if (readSummaryPropertyLong(input,endPos,type,value,f) && value>=0 && value<10000) // 10000 is mac
1276 					f << "encoding,"; // USEME: never seems actually
1277 			}
1278 			else if (sect==0 && type==0x1e && ((it->second>=2 && it->second<=6) || it->second==8))
1279 			{
1280 				librevenge::RVNGString text;
1281 				if (readSummaryPropertyString(input, endPos, type, text, f) && !text.empty())
1282 				{
1283 					static char const *attribNames[] =
1284 					{
1285 						"", "", "dc:title", "dc:subject", "meta:initial-creator",
1286 						"meta:keywords", "dc:description"/*comment*/, "", "dc:creator"
1287 					};
1288 					pList.insert(attribNames[it->second], text);
1289 				}
1290 			}
1291 			else if (!readSummaryProperty(input, endPos, type, ascii, f))
1292 			{
1293 				WPS_DEBUG_MSG(("WPSOLEParser::readSummaryInformation: find unknown type\n"));
1294 				f << "##type=" << std::hex << type << std::dec << ",";
1295 			}
1296 			if (input->tell()!=endPos && input->tell()!=pos)
1297 				ascii.addDelimiter(input->tell(),'|');
1298 			ascii.addPos(pos);
1299 			ascii.addNote(f.str().c_str());
1300 		}
1301 		input->seek(endSect, librevenge::RVNG_SEEK_SET);
1302 	}
1303 	return true;
1304 }
1305 
readSummaryPropertyString(RVNGInputStreamPtr input,long endPos,int type,librevenge::RVNGString & string,libwps::DebugStream & f) const1306 bool WPSOLEParser::readSummaryPropertyString(RVNGInputStreamPtr input, long endPos, int type,
1307                                              librevenge::RVNGString &string, libwps::DebugStream &f) const
1308 {
1309 	if (!input) return false;
1310 	long pos=input->tell();
1311 	string.clear();
1312 	auto sSz=long(libwps::readU32(input));
1313 	if (sSz<0 || (endPos-pos-4)<sSz || pos+4+sSz>endPos)
1314 	{
1315 		WPS_DEBUG_MSG(("WPSOLEParser::readSummaryPropertyString: string size is bad\n"));
1316 		f << "##stringSz=" << sSz << ",";
1317 		return false;
1318 	}
1319 	std::string text("");
1320 	for (long c=0; c < sSz; ++c)
1321 	{
1322 		auto ch=static_cast<unsigned char>(libwps::readU8(input));
1323 		if (ch)
1324 			text+=char(ch);
1325 		else if (c+1!=sSz)
1326 			text+="##";
1327 	}
1328 	f << text;
1329 	if (!text.empty())
1330 		string=libwps_tools_win::Font::unicodeString(text, m_state->m_fontType);
1331 	if (type==0x1f && (sSz%4))
1332 		input->seek(sSz%4, librevenge::RVNG_SEEK_CUR);
1333 	return true;
1334 }
1335 
readSummaryPropertyLong(RVNGInputStreamPtr input,long endPos,int type,long & value,libwps::DebugStream & f)1336 bool WPSOLEParser::readSummaryPropertyLong(RVNGInputStreamPtr input, long endPos, int type, long &value,
1337                                            libwps::DebugStream &f)
1338 {
1339 	if (!input) return false;
1340 	long pos=input->tell();
1341 	switch (type)
1342 	{
1343 	case 2: // int
1344 	case 0x12: // uint
1345 		if (pos+2>endPos)
1346 			return false;
1347 		value=type==2 ? long(libwps::read16(input)) : long(libwps::readU16(input));
1348 		break;
1349 	case 3: // int
1350 	case 9: // uint
1351 		if (pos+4>endPos)
1352 			return false;
1353 		value=type==3 ? long(libwps::read32(input)) : long(libwps::readU32(input));
1354 		break;
1355 	default:
1356 		return false;
1357 	}
1358 	f << "val=" << value << ",";
1359 	return true;
1360 }
1361 
readSummaryProperty(RVNGInputStreamPtr input,long endPos,int type,libwps::DebugFile & ascii,libwps::DebugStream & f) const1362 bool WPSOLEParser::readSummaryProperty(RVNGInputStreamPtr input, long endPos, int type,
1363                                        libwps::DebugFile &ascii, libwps::DebugStream &f) const
1364 {
1365 	if (!input) return false;
1366 	long pos=input->tell();
1367 	// see propread.cxx
1368 	if (type&0x1000)
1369 	{
1370 		auto N=int(libwps::readU32(input));
1371 		f << "N=" << N << ",";
1372 		f << "[";
1373 		for (int n=0; n<N; ++n)
1374 		{
1375 			pos=input->tell();
1376 			f << "[";
1377 			if (!readSummaryProperty(input, endPos, type&0xFFF, ascii, f))
1378 			{
1379 				input->seek(pos, librevenge::RVNG_SEEK_SET);
1380 				return false;
1381 			}
1382 			f << "],";
1383 		}
1384 		f << "],";
1385 		return true;
1386 	}
1387 	switch (type)
1388 	{
1389 	case 0x10: // int1
1390 	case 0x11: // uint1
1391 		if (pos+1>endPos)
1392 			return false;
1393 		f << "val=" << char(libwps::readU8(input));
1394 		break;
1395 	case 2: // int
1396 	case 0xb: // bool
1397 	case 0x12: // uint
1398 		if (pos+2>endPos)
1399 			return false;
1400 		if (type==2)
1401 			f << "val=" << int(libwps::read16(input)) << ",";
1402 		else if (type==0x12)
1403 			f << "val=" << int(libwps::readU16(input)) << ",";
1404 		else if (libwps::readU16(input))
1405 			f << "true,";
1406 		break;
1407 	case 3: // int
1408 	case 4: // float
1409 	case 9: // uint
1410 		if (pos+4>endPos)
1411 			return false;
1412 		if (type==3)
1413 			f << "val=" << int(libwps::read32(input)) << ",";
1414 		else if (type==9)
1415 			f << "val=" << int(libwps::readU32(input)) << ",";
1416 		else
1417 			f << "val[fl4]=" << std::hex << libwps::readU32(input) << std::dec << ",";
1418 		break;
1419 	case 5: // double
1420 	case 6:
1421 	case 7:
1422 	case 20:
1423 	case 21:
1424 	case 0x40:
1425 		if (pos+8>endPos)
1426 			return false;
1427 		ascii.addDelimiter(input->tell(),'|');
1428 		if (type==5)
1429 			f << "double,";
1430 		else if (type==6)
1431 			f << "cy,";
1432 		else if (type==7)
1433 			f << "date,";
1434 		else if (type==20)
1435 			f << "long,";
1436 		else if (type==21)
1437 			f << "ulong,";
1438 		else
1439 			f << "fileTime,"; // readme 8 byte
1440 		input->seek(pos+8, librevenge::RVNG_SEEK_SET);
1441 		break;
1442 	case 0xc: // variant
1443 		if (pos+4>endPos)
1444 			return false;
1445 		type=int(libwps::readU32(input));
1446 		return readSummaryProperty(input, endPos, type, ascii, f);
1447 	// case 20: int64
1448 	// case 21: uint64
1449 	case 8:
1450 	case 0x1e:
1451 	case 0x1f:
1452 	{
1453 		librevenge::RVNGString string;
1454 		if (!readSummaryPropertyString(input, endPos, type, string, f))
1455 			return false;
1456 		break;
1457 	}
1458 	case 0x41:
1459 	case 0x46:
1460 	case 0x47:
1461 	{
1462 		if (pos+4>endPos)
1463 			return false;
1464 		f << (type==0x41 ? "blob" : type==0x46 ? "blob[object]" : "clipboard") << ",";
1465 		auto dSz=long(libwps::readU32(input));
1466 		if (dSz<0 || pos+4+dSz>endPos)
1467 			return false;
1468 		if (dSz)
1469 		{
1470 			ascii.skipZone(pos+4, pos+4+dSz-1);
1471 			input->seek(dSz, librevenge::RVNG_SEEK_CUR);
1472 		}
1473 		break;
1474 	}
1475 	/* todo type==0x47, vtcf clipboard */
1476 	default:
1477 		return false;
1478 	}
1479 	return true;
1480 }
1481 /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
1482