xref: /haiku/src/kits/app/MessageAdapter.cpp (revision 5ce80a78c976c96d3afe4e9bd9eb473cb54c362c)
1 /*
2  * Copyright 2005-2015, Haiku Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Axel Dörfler, axeld@pinc-software.de
7  *		Michael Lotz <mmlr@mlotz.ch>
8  */
9 
10 
11 #include <MessageAdapter.h>
12 #include <MessagePrivate.h>
13 #include <MessageUtils.h>
14 
15 #include <stdlib.h>
16 
17 
18 namespace BPrivate {
19 
20 #define R5_MESSAGE_FLAG_VALID			0x01
21 #define R5_MESSAGE_FLAG_INCLUDE_TARGET	0x02
22 #define R5_MESSAGE_FLAG_INCLUDE_REPLY	0x04
23 #define R5_MESSAGE_FLAG_SCRIPT_MESSAGE	0x08
24 
25 #define R5_FIELD_FLAG_VALID				0x01
26 #define R5_FIELD_FLAG_MINI_DATA			0x02
27 #define R5_FIELD_FLAG_FIXED_SIZE		0x04
28 #define R5_FIELD_FLAG_SINGLE_ITEM		0x08
29 
30 
31 enum {
32 	SECTION_MESSAGE_HEADER = 'FOB2',
33 	SECTION_OFFSET_TABLE = 'STof',
34 	SECTION_TARGET_INFORMATION = 'ENwh',
35 	SECTION_SINGLE_ITEM_DATA = 'SGDa',
36 	SECTION_FIXED_SIZE_ARRAY_DATA = 'FADa',
37 	SECTION_VARIABLE_SIZE_ARRAY_DATA = 'VADa',
38 	SECTION_SORTED_INDEX_TABLE = 'DXIn',
39 	SECTION_END_OF_DATA = 'DDEn'
40 };
41 
42 
43 struct r5_message_header {
44 	uint32	magic;
45 	uint32	checksum;
46 	int32	flattened_size;
47 	int32	what;
48 	uint8	flags;
49 } _PACKED;
50 
51 
52 struct dano_section_header {
53 	uint32		code;
54 	int32		size;
55 	uint8		data[0];
56 } _PACKED;
57 
58 
59 struct dano_message_header {
60 	int32		what;
61 	int32		padding;
62 } _PACKED;
63 
64 
65 typedef struct offset_table_s {
66 	int32		indexTable;
67 	int32		endOfData;
68 	int64		padding;
69 } OffsetTable;
70 
71 
72 struct dano_single_item {
73 	type_code	type;
74 	int32		item_size;
75 	uint8		name_length;
76 	char		name[0];
77 } _PACKED;
78 
79 
80 struct dano_fixed_size_array {
81 	type_code	type;
82 	int32		size_per_item;
83 	uint8		name_length;
84 	char		name[0];
85 } _PACKED;
86 
87 
88 struct dano_variable_size_array {
89 	type_code	type;
90 	int32		padding;
91 	uint8		name_length;
92 	char		name[0];
93 } _PACKED;
94 
95 
96 inline int32
pad_to_8(int32 value)97 pad_to_8(int32 value)
98 {
99 	return (value + 7) & ~7;
100 }
101 
102 
103 /*static*/ ssize_t
FlattenedSize(uint32 format,const BMessage * from)104 MessageAdapter::FlattenedSize(uint32 format, const BMessage *from)
105 {
106 	switch (format) {
107 		case MESSAGE_FORMAT_R5:
108 		case MESSAGE_FORMAT_R5_SWAPPED:
109 			return _R5FlattenedSize(from);
110 	}
111 
112 	return -1;
113 }
114 
115 
116 /*static*/ status_t
Flatten(uint32 format,const BMessage * from,char * buffer,ssize_t * size)117 MessageAdapter::Flatten(uint32 format, const BMessage *from, char *buffer,
118 	ssize_t *size)
119 {
120 	switch (format) {
121 		case MESSAGE_FORMAT_R5:
122 		case MESSAGE_FORMAT_R5_SWAPPED:
123 			return _FlattenR5Message(format, from, buffer, size);
124 	}
125 
126 	return B_ERROR;
127 }
128 
129 
130 /*static*/ status_t
Flatten(uint32 format,const BMessage * from,BDataIO * stream,ssize_t * size)131 MessageAdapter::Flatten(uint32 format, const BMessage *from, BDataIO *stream,
132 	ssize_t *size)
133 {
134 	switch (format) {
135 		case MESSAGE_FORMAT_R5:
136 		case MESSAGE_FORMAT_R5_SWAPPED:
137 		{
138 			ssize_t flattenedSize = _R5FlattenedSize(from);
139 			char *buffer = (char *)malloc(flattenedSize);
140 			if (!buffer)
141 				return B_NO_MEMORY;
142 
143 			status_t result = _FlattenR5Message(format, from, buffer,
144 				&flattenedSize);
145 			if (result < B_OK) {
146 				free(buffer);
147 				return result;
148 			}
149 
150 			ssize_t written = stream->Write(buffer, flattenedSize);
151 			if (written != flattenedSize) {
152 				free(buffer);
153 				return (written >= 0 ? B_ERROR : written);
154 			}
155 
156 			if (size)
157 				*size = flattenedSize;
158 
159 			free(buffer);
160 			return B_OK;
161 		}
162 	}
163 
164 	return B_ERROR;
165 }
166 
167 
168 /*static*/ status_t
Unflatten(uint32 format,BMessage * into,const char * buffer)169 MessageAdapter::Unflatten(uint32 format, BMessage *into, const char *buffer)
170 {
171 	if (format == KMessage::kMessageHeaderMagic) {
172 		KMessage message;
173 		status_t result = message.SetTo(buffer,
174 			((KMessage::Header *)buffer)->size);
175 		if (result != B_OK)
176 			return result;
177 
178 		return _ConvertFromKMessage(&message, into);
179 	}
180 
181 	try {
182 		switch (format) {
183 			case MESSAGE_FORMAT_R5:
184 			{
185 				r5_message_header *header = (r5_message_header *)buffer;
186 				BMemoryIO stream(buffer + sizeof(uint32),
187 					header->flattened_size - sizeof(uint32));
188 				return _UnflattenR5Message(format, into, &stream);
189 			}
190 
191 			case MESSAGE_FORMAT_R5_SWAPPED:
192 			{
193 				r5_message_header *header = (r5_message_header *)buffer;
194 				BMemoryIO stream(buffer + sizeof(uint32),
195 					__swap_int32(header->flattened_size) - sizeof(uint32));
196 				return _UnflattenR5Message(format, into, &stream);
197 			}
198 
199 			case MESSAGE_FORMAT_DANO:
200 			case MESSAGE_FORMAT_DANO_SWAPPED:
201 			{
202 				dano_section_header *header = (dano_section_header *)buffer;
203 				ssize_t size = header->size;
204 				if (header->code == MESSAGE_FORMAT_DANO_SWAPPED)
205 					size = __swap_int32(size);
206 
207 				BMemoryIO stream(buffer + sizeof(uint32), size - sizeof(uint32));
208 				return _UnflattenDanoMessage(format, into, &stream);
209 			}
210 		}
211 	} catch (status_t error) {
212 		into->MakeEmpty();
213 		return error;
214 	}
215 
216 	return B_NOT_A_MESSAGE;
217 }
218 
219 
220 /*static*/ status_t
Unflatten(uint32 format,BMessage * into,BDataIO * stream)221 MessageAdapter::Unflatten(uint32 format, BMessage *into, BDataIO *stream)
222 {
223 	try {
224 		switch (format) {
225 			case MESSAGE_FORMAT_R5:
226 			case MESSAGE_FORMAT_R5_SWAPPED:
227 				return _UnflattenR5Message(format, into, stream);
228 
229 			case MESSAGE_FORMAT_DANO:
230 			case MESSAGE_FORMAT_DANO_SWAPPED:
231 				return _UnflattenDanoMessage(format, into, stream);
232 		}
233 	} catch (status_t error) {
234 		into->MakeEmpty();
235 		return error;
236 	}
237 
238 	return B_NOT_A_MESSAGE;
239 }
240 
241 
242 /*static*/ status_t
ConvertToKMessage(const BMessage * from,KMessage & to)243 MessageAdapter::ConvertToKMessage(const BMessage* from, KMessage& to)
244 {
245 	if (from == NULL)
246 		return B_BAD_VALUE;
247 
248 	BMessage::Private fromPrivate(const_cast<BMessage*>(from));
249 	BMessage::message_header* header = fromPrivate.GetMessageHeader();
250 	uint8* data = fromPrivate.GetMessageData();
251 
252 	// Iterate through the fields and import them in the target message
253 	BMessage::field_header* field = fromPrivate.GetMessageFields();
254 	for (uint32 i = 0; i < header->field_count; i++, field++) {
255 		const char* name = (const char*)data + field->offset;
256 		const uint8* fieldData = data + field->offset + field->name_length;
257 		bool fixedSize = (field->flags & FIELD_FLAG_FIXED_SIZE) != 0;
258 
259 		if (fixedSize) {
260 			status_t status = to.AddArray(name, field->type, fieldData,
261 				field->data_size / field->count, field->count);
262 			if (status != B_OK)
263 				return status;
264 		} else {
265 			for (uint32 i = 0; i < field->count; i++) {
266 				uint32 itemSize = *(uint32*)fieldData;
267 				fieldData += sizeof(uint32);
268 				status_t status = to.AddData(name, field->type, fieldData,
269 					itemSize, false);
270 				if (status != B_OK)
271 					return status;
272 				fieldData += itemSize;
273 			}
274 		}
275 	}
276 	return B_OK;
277 }
278 
279 
280 /*static*/ status_t
_ConvertFromKMessage(const KMessage * fromMessage,BMessage * toMessage)281 MessageAdapter::_ConvertFromKMessage(const KMessage *fromMessage,
282 	BMessage *toMessage)
283 {
284 	if (!fromMessage || !toMessage)
285 		return B_BAD_VALUE;
286 
287 	// make empty and init what of the target message
288 	toMessage->MakeEmpty();
289 	toMessage->what = fromMessage->What();
290 
291 	BMessage::Private toPrivate(toMessage);
292 	toPrivate.SetTarget(fromMessage->TargetToken());
293 	toPrivate.SetReply(B_SYSTEM_TEAM, fromMessage->ReplyPort(),
294 		fromMessage->ReplyToken());
295 	if (fromMessage->ReplyPort() >= 0) {
296 		toPrivate.GetMessageHeader()->flags |= MESSAGE_FLAG_REPLY_AS_KMESSAGE
297 			| MESSAGE_FLAG_REPLY_REQUIRED;
298 	}
299 
300 	// Iterate through the fields and import them in the target message
301 	KMessageField field;
302 	while (fromMessage->GetNextField(&field) == B_OK) {
303 		int32 elementCount = field.CountElements();
304 		if (elementCount > 0) {
305 			for (int32 i = 0; i < elementCount; i++) {
306 				int32 size;
307 				const void *data = field.ElementAt(i, &size);
308 				status_t result;
309 
310 				if (field.TypeCode() == B_MESSAGE_TYPE) {
311 					// message type: if it's a KMessage, convert it
312 					KMessage message;
313 					if (message.SetTo(data, size) == B_OK) {
314 						BMessage bMessage;
315 						result = _ConvertFromKMessage(&message, &bMessage);
316 						if (result < B_OK)
317 							return result;
318 
319 						result = toMessage->AddMessage(field.Name(), &bMessage);
320 					} else {
321 						// just add it
322 						result = toMessage->AddData(field.Name(),
323 							field.TypeCode(), data, size,
324 							field.HasFixedElementSize(), 1);
325 					}
326 				} else {
327 					result = toMessage->AddData(field.Name(), field.TypeCode(),
328 						data, size, field.HasFixedElementSize(), 1);
329 				}
330 
331 				if (result < B_OK)
332 					return result;
333 			}
334 		}
335 	}
336 
337 	return B_OK;
338 }
339 
340 
341 /*static*/ ssize_t
_R5FlattenedSize(const BMessage * from)342 MessageAdapter::_R5FlattenedSize(const BMessage *from)
343 {
344 	BMessage::Private messagePrivate((BMessage *)from);
345 	BMessage::message_header* header = messagePrivate.GetMessageHeader();
346 
347 	// header size (variable, depending on the flags)
348 
349 	ssize_t flattenedSize = sizeof(r5_message_header);
350 
351 	if (header->target != B_NULL_TOKEN)
352 		flattenedSize += sizeof(int32);
353 
354 	if (header->reply_port >= 0 && header->reply_target != B_NULL_TOKEN
355 		&& header->reply_team >= 0) {
356 		// reply info + big flags
357 		flattenedSize += sizeof(port_id) + sizeof(int32) + sizeof(team_id) + 4;
358 	}
359 
360 	// field size
361 
362 	uint8 *data = messagePrivate.GetMessageData();
363 	BMessage::field_header *field = messagePrivate.GetMessageFields();
364 	for (uint32 i = 0; i < header->field_count; i++, field++) {
365 		// flags and type
366 		flattenedSize += 1 + sizeof(type_code);
367 
368 #if 0
369 		bool miniData = field->dataSize <= 255 && field->count <= 255;
370 #else
371 		// TODO: we don't know the R5 dataSize yet (padding)
372 		bool miniData = false;
373 #endif
374 
375 		// item count
376 		if (field->count > 1)
377 			flattenedSize += (miniData ? sizeof(uint8) : sizeof(uint32));
378 
379 		// data size
380 		flattenedSize += (miniData ? sizeof(uint8) : sizeof(size_t));
381 
382 		// name length and name
383 		flattenedSize += 1 + min_c(field->name_length - 1, 255);
384 
385 		// data
386 		if (field->flags & FIELD_FLAG_FIXED_SIZE)
387 			flattenedSize += field->data_size;
388 		else {
389 			uint8 *source = data + field->offset + field->name_length;
390 
391 			for (uint32 i = 0; i < field->count; i++) {
392 				ssize_t itemSize = *(ssize_t *)source + sizeof(ssize_t);
393 				flattenedSize += pad_to_8(itemSize);
394 				source += itemSize;
395 			}
396 		}
397 	}
398 
399 	// pseudo field with flags 0
400 	return flattenedSize + 1;
401 }
402 
403 
404 /*static*/ status_t
_FlattenR5Message(uint32 format,const BMessage * from,char * buffer,ssize_t * size)405 MessageAdapter::_FlattenR5Message(uint32 format, const BMessage *from,
406 	char *buffer, ssize_t *size)
407 {
408 	BMessage::Private messagePrivate((BMessage *)from);
409 	BMessage::message_header *header = messagePrivate.GetMessageHeader();
410 	uint8 *data = messagePrivate.GetMessageData();
411 
412 	r5_message_header *r5header = (r5_message_header *)buffer;
413 	uint8 *pointer = (uint8 *)buffer + sizeof(r5_message_header);
414 
415 	r5header->magic = MESSAGE_FORMAT_R5;
416 	r5header->what = from->what;
417 	r5header->checksum = 0;
418 
419 	uint8 flags = R5_MESSAGE_FLAG_VALID;
420 	if (header->target != B_NULL_TOKEN) {
421 		*(int32 *)pointer = header->target;
422 		pointer += sizeof(int32);
423 		flags |= R5_MESSAGE_FLAG_INCLUDE_TARGET;
424 	}
425 
426 	if (header->reply_port >= 0 && header->reply_target != B_NULL_TOKEN
427 		&& header->reply_team >= 0) {
428 		// reply info
429 		*(port_id *)pointer = header->reply_port;
430 		pointer += sizeof(port_id);
431 		*(int32 *)pointer = header->reply_target;
432 		pointer += sizeof(int32);
433 		*(team_id *)pointer = header->reply_team;
434 		pointer += sizeof(team_id);
435 
436 		// big flags
437 		*pointer = (header->reply_target == B_PREFERRED_TOKEN ? 1 : 0);
438 		pointer++;
439 
440 		*pointer = (header->flags & MESSAGE_FLAG_REPLY_REQUIRED ? 1 : 0);
441 		pointer++;
442 
443 		*pointer = (header->flags & MESSAGE_FLAG_REPLY_DONE ? 1 : 0);
444 		pointer++;
445 
446 		*pointer = (header->flags & MESSAGE_FLAG_IS_REPLY ? 1 : 0);
447 		pointer++;
448 
449 		flags |= R5_MESSAGE_FLAG_INCLUDE_REPLY;
450 	}
451 
452 	if (header->flags & MESSAGE_FLAG_HAS_SPECIFIERS)
453 		flags |= R5_MESSAGE_FLAG_SCRIPT_MESSAGE;
454 
455 	r5header->flags = flags;
456 
457 	// store the header size - used for the checksum later
458 	ssize_t headerSize = (addr_t)pointer - (addr_t)buffer;
459 
460 	// collect and add the data
461 	BMessage::field_header *field = messagePrivate.GetMessageFields();
462 	for (uint32 i = 0; i < header->field_count; i++, field++) {
463 		flags = R5_FIELD_FLAG_VALID;
464 
465 		if (field->count == 1)
466 			flags |= R5_FIELD_FLAG_SINGLE_ITEM;
467 		// TODO: we don't really know the data size now (padding missing)
468 //		if (field->data_size <= 255 && field->count <= 255)
469 //			flags |= R5_FIELD_FLAG_MINI_DATA;
470 		if (field->flags & FIELD_FLAG_FIXED_SIZE)
471 			flags |= R5_FIELD_FLAG_FIXED_SIZE;
472 
473 		*pointer = flags;
474 		pointer++;
475 
476 		*(type_code *)pointer = field->type;
477 		pointer += sizeof(type_code);
478 
479 		if (!(flags & R5_FIELD_FLAG_SINGLE_ITEM)) {
480 			if (flags & R5_FIELD_FLAG_MINI_DATA) {
481 				*pointer = (uint8)field->count;
482 				pointer++;
483 			} else {
484 				*(int32 *)pointer = field->count;
485 				pointer += sizeof(int32);
486 			}
487 		}
488 
489 		// we may have to adjust this to account for padding later
490 		uint8 *fieldSize = pointer;
491 		if (flags & R5_FIELD_FLAG_MINI_DATA) {
492 			*pointer = (uint8)field->data_size;
493 			pointer++;
494 		} else {
495 			*(ssize_t *)pointer = field->data_size;
496 			pointer += sizeof(ssize_t);
497 		}
498 
499 		// name
500 		int32 nameLength = min_c(field->name_length - 1, 255);
501 		*pointer = (uint8)nameLength;
502 		pointer++;
503 
504 		strncpy((char *)pointer, (char *)data + field->offset, nameLength);
505 		pointer += nameLength;
506 
507 		// data
508 		uint8 *source = data + field->offset + field->name_length;
509 		if (flags & R5_FIELD_FLAG_FIXED_SIZE) {
510 			memcpy(pointer, source, field->data_size);
511 			pointer += field->data_size;
512 		} else {
513 			uint8 *previous = pointer;
514 			for (uint32 i = 0; i < field->count; i++) {
515 				ssize_t itemSize = *(ssize_t *)source + sizeof(ssize_t);
516 				memcpy(pointer, source, itemSize);
517 				ssize_t paddedSize = pad_to_8(itemSize);
518 				memset(pointer + itemSize, 0, paddedSize - itemSize);
519 				pointer += paddedSize;
520 				source += itemSize;
521 			}
522 
523 			// adjust the field size to the padded value
524 			if (flags & R5_FIELD_FLAG_MINI_DATA)
525 				*fieldSize = (uint8)(pointer - previous);
526 			else
527 				*(ssize_t *)fieldSize = (pointer - previous);
528 		}
529 	}
530 
531 	// terminate the fields with a pseudo field with flags 0 (not valid)
532 	*pointer = 0;
533 	pointer++;
534 
535 	// calculate the flattened size from the pointers
536 	r5header->flattened_size = (addr_t)pointer - (addr_t)buffer;
537 	r5header->checksum = CalculateChecksum((uint8 *)(buffer + 8),
538 		headerSize - 8);
539 
540 	if (size)
541 		*size = r5header->flattened_size;
542 
543 	return B_OK;
544 }
545 
546 
547 /*static*/ status_t
_UnflattenR5Message(uint32 format,BMessage * into,BDataIO * stream)548 MessageAdapter::_UnflattenR5Message(uint32 format, BMessage *into,
549 	BDataIO *stream)
550 {
551 	into->MakeEmpty();
552 
553 	BMessage::Private messagePrivate(into);
554 	BMessage::message_header *header = messagePrivate.GetMessageHeader();
555 
556 	TReadHelper reader(stream);
557 	if (format == MESSAGE_FORMAT_R5_SWAPPED)
558 		reader.SetSwap(true);
559 
560 	// the stream is already advanced by the size of the "format"
561 	r5_message_header r5header;
562 	reader(((uint8 *)&r5header) + sizeof(uint32),
563 		sizeof(r5header) - sizeof(uint32));
564 
565 	header->what = into->what = r5header.what;
566 	if (r5header.flags & R5_MESSAGE_FLAG_INCLUDE_TARGET)
567 		reader(&header->target, sizeof(header->target));
568 
569 	if (r5header.flags & R5_MESSAGE_FLAG_INCLUDE_REPLY) {
570 		// reply info
571 		reader(&header->reply_port, sizeof(header->reply_port));
572 		reader(&header->reply_target, sizeof(header->reply_target));
573 		reader(&header->reply_team, sizeof(header->reply_team));
574 
575 		// big flags
576 		uint8 bigFlag;
577 		reader(bigFlag);
578 		if (bigFlag)
579 			header->reply_target = B_PREFERRED_TOKEN;
580 
581 		reader(bigFlag);
582 		if (bigFlag)
583 			header->flags |= MESSAGE_FLAG_REPLY_REQUIRED;
584 
585 		reader(bigFlag);
586 		if (bigFlag)
587 			header->flags |= MESSAGE_FLAG_REPLY_DONE;
588 
589 		reader(bigFlag);
590 		if (bigFlag)
591 			header->flags |= MESSAGE_FLAG_IS_REPLY;
592 	}
593 
594 	if (r5header.flags & R5_MESSAGE_FLAG_SCRIPT_MESSAGE)
595 		header->flags |= MESSAGE_FLAG_HAS_SPECIFIERS;
596 
597 	uint8 flags;
598 	reader(flags);
599 	while ((flags & R5_FIELD_FLAG_VALID) != 0) {
600 		bool fixedSize = flags & R5_FIELD_FLAG_FIXED_SIZE;
601 		bool miniData = flags & R5_FIELD_FLAG_MINI_DATA;
602 		bool singleItem = flags & R5_FIELD_FLAG_SINGLE_ITEM;
603 
604 		type_code type;
605 		reader(type);
606 
607 		int32 itemCount;
608 		if (!singleItem) {
609 			if (miniData) {
610 				uint8 miniCount;
611 				reader(miniCount);
612 				itemCount = miniCount;
613 			} else
614 				reader(itemCount);
615 		} else
616 			itemCount = 1;
617 
618 		int32 dataSize;
619 		if (miniData) {
620 			uint8 miniSize;
621 			reader(miniSize);
622 			dataSize = miniSize;
623 		} else
624 			reader(dataSize);
625 
626 		if (dataSize <= 0)
627 			return B_ERROR;
628 
629 		// name
630 		uint8 nameLength;
631 		reader(nameLength);
632 
633 		char nameBuffer[256];
634 		reader(nameBuffer, nameLength);
635 		nameBuffer[nameLength] = '\0';
636 
637 		uint8 *buffer = (uint8 *)malloc(dataSize);
638 		uint8 *pointer = buffer;
639 		reader(buffer, dataSize);
640 
641 		status_t result = B_OK;
642 		int32 itemSize = 0;
643 		if (fixedSize)
644 			itemSize = dataSize / itemCount;
645 
646 		if (format == MESSAGE_FORMAT_R5) {
647 			for (int32 i = 0; i < itemCount; i++) {
648 				if (!fixedSize) {
649 					itemSize = *(int32 *)pointer;
650 					pointer += sizeof(int32);
651 				}
652 
653 				result = into->AddData(nameBuffer, type, pointer, itemSize,
654 					fixedSize, itemCount);
655 
656 				if (result < B_OK) {
657 					free(buffer);
658 					return result;
659 				}
660 
661 				if (fixedSize)
662 					pointer += itemSize;
663 				else {
664 					pointer += pad_to_8(itemSize + sizeof(int32))
665 						- sizeof(int32);
666 				}
667 			}
668 		} else {
669 			for (int32 i = 0; i < itemCount; i++) {
670 				if (!fixedSize) {
671 					itemSize = __swap_int32(*(int32 *)pointer);
672 					pointer += sizeof(int32);
673 				}
674 
675 				swap_data(type, pointer, itemSize, B_SWAP_ALWAYS);
676 				result = into->AddData(nameBuffer, type, pointer, itemSize,
677 					fixedSize, itemCount);
678 
679 				if (result < B_OK) {
680 					free(buffer);
681 					return result;
682 				}
683 
684 				if (fixedSize)
685 					pointer += itemSize;
686 				else {
687 					pointer += pad_to_8(itemSize + sizeof(int32))
688 						- sizeof(int32);
689 				}
690 			}
691 		}
692 
693 		free(buffer);
694 
695 		// flags of next field or termination byte
696 		reader(flags);
697 	}
698 
699 	return B_OK;
700 }
701 
702 
703 /*static*/ status_t
_UnflattenDanoMessage(uint32 format,BMessage * into,BDataIO * stream)704 MessageAdapter::_UnflattenDanoMessage(uint32 format, BMessage *into,
705 	BDataIO *stream)
706 {
707 	into->MakeEmpty();
708 
709 	TReadHelper reader(stream);
710 	if (format == MESSAGE_FORMAT_DANO_SWAPPED)
711 		reader.SetSwap(true);
712 
713 	ssize_t size;
714 	reader(size);
715 
716 	dano_message_header header;
717 	reader(header);
718 	into->what = header.what;
719 
720 	size -= sizeof(dano_section_header) + sizeof(dano_message_header);
721 	int32 offset = 0;
722 
723 	while (offset < size) {
724 		dano_section_header sectionHeader;
725 		reader(sectionHeader);
726 
727 		// be safe. this shouldn't be necessary but in some testcases it was.
728 		sectionHeader.size = pad_to_8(sectionHeader.size);
729 
730 		if (offset + sectionHeader.size > size || sectionHeader.size < 0)
731 			return B_BAD_DATA;
732 
733 		ssize_t fieldSize = sectionHeader.size - sizeof(dano_section_header);
734 		uint8 *fieldBuffer = NULL;
735 		if (fieldSize <= 0) {
736 			// there may be no data. we shouldn't fail because of that
737 			offset += sectionHeader.size;
738 			continue;
739 		}
740 
741 		fieldBuffer = (uint8 *)malloc(fieldSize);
742 		if (fieldBuffer == NULL)
743 			throw (status_t)B_NO_MEMORY;
744 
745 		reader(fieldBuffer, fieldSize);
746 
747 		switch (sectionHeader.code) {
748 			case SECTION_OFFSET_TABLE:
749 			case SECTION_TARGET_INFORMATION:
750 			case SECTION_SORTED_INDEX_TABLE:
751 			case SECTION_END_OF_DATA:
752 				// discard
753 				break;
754 
755 			case SECTION_SINGLE_ITEM_DATA:
756 			{
757 				dano_single_item *field = (dano_single_item *)fieldBuffer;
758 
759 				int32 dataOffset = sizeof(dano_single_item)
760 					+ field->name_length + 1;
761 				dataOffset = pad_to_8(dataOffset);
762 
763 				if (offset + dataOffset + field->item_size > size)
764 					return B_BAD_DATA;
765 
766 				// support for fixed size is not possible with a single item
767 				bool fixedSize = false;
768 				switch (field->type) {
769 					case B_RECT_TYPE:
770 					case B_POINT_TYPE:
771 					case B_INT8_TYPE:
772 					case B_INT16_TYPE:
773 					case B_INT32_TYPE:
774 					case B_INT64_TYPE:
775 					case B_BOOL_TYPE:
776 					case B_FLOAT_TYPE:
777 					case B_DOUBLE_TYPE:
778 					case B_POINTER_TYPE:
779 					case B_MESSENGER_TYPE:
780 						fixedSize = true;
781 						break;
782 					default:
783 						break;
784 				}
785 
786 				status_t result = into->AddData(field->name, field->type,
787 					fieldBuffer + dataOffset, field->item_size, fixedSize);
788 
789 				if (result != B_OK) {
790 					free(fieldBuffer);
791 					throw result;
792 				}
793 				break;
794 			}
795 
796 			case SECTION_FIXED_SIZE_ARRAY_DATA: {
797 				dano_fixed_size_array *field
798 					= (dano_fixed_size_array *)fieldBuffer;
799 
800 				int32 dataOffset = sizeof(dano_fixed_size_array)
801 					+ field->name_length + 1;
802 				dataOffset = pad_to_8(dataOffset);
803 				int32 count = *(int32 *)(fieldBuffer + dataOffset);
804 				dataOffset += 8; /* count and padding */
805 
806 				if (offset + dataOffset + count * field->size_per_item > size)
807 					return B_BAD_DATA;
808 
809 				status_t result = B_OK;
810 				for (int32 i = 0; i < count; i++) {
811 					result = into->AddData(field->name, field->type,
812 						fieldBuffer + dataOffset, field->size_per_item, true,
813 						count);
814 
815 					if (result != B_OK) {
816 						free(fieldBuffer);
817 						throw result;
818 					}
819 
820 					dataOffset += field->size_per_item;
821 				}
822 				break;
823 			}
824 
825 			case SECTION_VARIABLE_SIZE_ARRAY_DATA: {
826 				dano_variable_size_array *field
827 					= (dano_variable_size_array *)fieldBuffer;
828 
829 				int32 dataOffset = sizeof(dano_variable_size_array)
830 					+ field->name_length + 1;
831 				dataOffset = pad_to_8(dataOffset);
832 				int32 count = *(int32 *)(fieldBuffer + dataOffset);
833 				dataOffset += sizeof(int32);
834 				ssize_t totalSize = *(ssize_t *)(fieldBuffer + dataOffset);
835 				dataOffset += sizeof(ssize_t);
836 
837 				int32 *endPoints = (int32 *)(fieldBuffer + dataOffset
838 					+ totalSize);
839 
840 				status_t result = B_OK;
841 				for (int32 i = 0; i < count; i++) {
842 					int32 itemOffset = (i > 0 ? pad_to_8(endPoints[i - 1]) : 0);
843 
844 					result = into->AddData(field->name, field->type,
845 						fieldBuffer + dataOffset + itemOffset,
846 						endPoints[i] - itemOffset, false, count);
847 
848 					if (result != B_OK) {
849 						free(fieldBuffer);
850 						throw result;
851 					}
852 				}
853 				break;
854 			}
855 		}
856 
857 		free(fieldBuffer);
858 		offset += sectionHeader.size;
859 	}
860 
861 	return B_OK;
862 }
863 
864 
865 } // namespace BPrivate
866