1/*
2 * Copyright 2001-2012 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Rene Gollent (rene@gollent.com)
7 *		Erik Jaesler (erik@cgsoftware.com)
8 *		Alex Wilson (yourpalal2@gmail.com)
9 */
10
11/*!	BArchivable mix-in class defines the archiving protocol.
12	Also some global archiving functions.
13*/
14
15
16#include <ctype.h>
17#include <errno.h>
18#include <stdlib.h>
19#include <stdio.h>
20#include <string>
21#include <syslog.h>
22#include <typeinfo>
23#include <vector>
24
25#include <AppFileInfo.h>
26#include <Archivable.h>
27#include <Entry.h>
28#include <List.h>
29#include <OS.h>
30#include <Path.h>
31#include <Roster.h>
32#include <String.h>
33
34#include <binary_compatibility/Support.h>
35
36#include "ArchivingManagers.h"
37
38
39using std::string;
40using std::vector;
41
42using namespace BPrivate::Archiving;
43
44const char* B_CLASS_FIELD = "class";
45const char* B_ADD_ON_FIELD = "add_on";
46const int32 FUNC_NAME_LEN = 1024;
47
48// TODO: consider moving these to a separate module, and making them more
49//	full-featured (e.g., taking NS::ClassName::Function(Param p) instead
50//	of just NS::ClassName)
51
52
53static status_t
54demangle_class_name(const char* name, BString& out)
55{
56// TODO: add support for template classes
57//	_find__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCccUlUl
58
59	out = "";
60
61#if __GNUC__ >= 4
62	if (name[0] == 'N')
63		name++;
64	int nameLen;
65	bool first = true;
66	while ((nameLen = strtoul(name, (char**)&name, 10))) {
67		if (!first)
68			out += "::";
69		else
70			first = false;
71		out.Append(name, nameLen);
72		name += nameLen;
73	}
74	if (first)
75		return B_BAD_VALUE;
76
77#else
78	if (name[0] == 'Q') {
79		// The name is in a namespace
80		int namespaceCount = 0;
81		name++;
82		if (name[0] == '_') {
83			// more than 10 namespaces deep
84			if (!isdigit(*++name))
85				return B_BAD_VALUE;
86
87			namespaceCount = strtoul(name, (char**)&name, 10);
88			if (name[0] != '_')
89				return B_BAD_VALUE;
90		} else
91			namespaceCount = name[0] - '0';
92
93		name++;
94
95		for (int i = 0; i < namespaceCount - 1; i++) {
96			if (!isdigit(name[0]))
97				return B_BAD_VALUE;
98
99			int nameLength = strtoul(name, (char**)&name, 10);
100			out.Append(name, nameLength);
101			out += "::";
102			name += nameLength;
103		}
104	}
105
106	int nameLength = strtoul(name, (char**)&name, 10);
107	out.Append(name, nameLength);
108#endif
109
110	return B_OK;
111}
112
113
114static void
115mangle_class_name(const char* name, BString& out)
116{
117// TODO: add support for template classes
118//	_find__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCccUlUl
119
120	//	Chop this:
121	//		testthree::testfour::Testthree::Testfour
122	//	up into little bite-sized pieces
123	int count = 0;
124	string origName(name);
125	vector<string> spacenames;
126
127	string::size_type pos = 0;
128	string::size_type oldpos = 0;
129	while (pos != string::npos) {
130		pos = origName.find_first_of("::", oldpos);
131		spacenames.push_back(string(origName, oldpos, pos - oldpos));
132		pos = origName.find_first_not_of("::", pos);
133		oldpos = pos;
134		++count;
135	}
136
137	//	Now mangle it into this:
138	//		9testthree8testfour9Testthree8Testfour
139	//			(for __GNUC__ > 2)
140	//			this isn't always the proper mangled class name, it should
141	//			actually have an 'N' prefix and 'E' suffix if the name is
142	//			in > 0 namespaces, but these would have to be removed in
143	//			build_function_name() (the only place this function is called)
144	//			so we don't add them.
145	//	or this:
146	//		Q49testthree8testfour9Testthree8Testfour
147	//			(for __GNUC__ == 2)
148
149	out = "";
150#if __GNUC__ == 2
151	if (count > 1) {
152		out += 'Q';
153		if (count > 10)
154			out += '_';
155		out << count;
156		if (count > 10)
157			out += '_';
158	}
159#endif
160
161	for (unsigned int i = 0; i < spacenames.size(); ++i) {
162		out << (int)spacenames[i].length();
163		out += spacenames[i].c_str();
164	}
165}
166
167
168static void
169build_function_name(const BString& className, BString& funcName)
170{
171	funcName = "";
172
173	//	This is what we're after:
174	//		Instantiate__Q28OpenBeOS11BArchivableP8BMessage
175	mangle_class_name(className.String(), funcName);
176#if __GNUC__ >= 4
177	funcName.Prepend("_ZN");
178	funcName.Append("11InstantiateE");
179#else
180	funcName.Prepend("Instantiate__");
181#endif
182	funcName.Append("P8BMessage");
183}
184
185
186static bool
187add_private_namespace(BString& name)
188{
189	if (name.Compare("_", 1) != 0)
190		return false;
191
192	name.Prepend("BPrivate::");
193	return true;
194}
195
196
197static instantiation_func
198find_function_in_image(BString& funcName, image_id id, status_t& err)
199{
200	instantiation_func instantiationFunc = NULL;
201	err = get_image_symbol(id, funcName.String(), B_SYMBOL_TYPE_TEXT,
202		(void**)&instantiationFunc);
203	if (err != B_OK)
204		return NULL;
205
206	return instantiationFunc;
207}
208
209
210static status_t
211check_signature(const char* signature, image_info& info)
212{
213	if (signature == NULL) {
214		// If it wasn't specified, anything "matches"
215		return B_OK;
216	}
217
218	// Get image signature
219	BFile file(info.name, B_READ_ONLY);
220	status_t err = file.InitCheck();
221	if (err != B_OK)
222		return err;
223
224	char imageSignature[B_MIME_TYPE_LENGTH];
225	BAppFileInfo appFileInfo(&file);
226	err = appFileInfo.GetSignature(imageSignature);
227	if (err != B_OK) {
228		syslog(LOG_ERR, "instantiate_object - couldn't get mime sig for %s",
229			info.name);
230		return err;
231	}
232
233	if (strcmp(signature, imageSignature) != 0)
234		return B_MISMATCHED_VALUES;
235
236	return B_OK;
237}
238
239
240namespace BPrivate {
241
242instantiation_func
243find_instantiation_func(const char* className, const char* signature,
244	image_id* id)
245{
246	if (className == NULL) {
247		errno = B_BAD_VALUE;
248		return NULL;
249	}
250
251	thread_info threadInfo;
252	status_t err = get_thread_info(find_thread(NULL), &threadInfo);
253	if (err != B_OK) {
254		errno = err;
255		return NULL;
256	}
257
258	instantiation_func instantiationFunc = NULL;
259	image_info imageInfo;
260
261	BString name = className;
262	for (int32 pass = 0; pass < 2; pass++) {
263		BString funcName;
264		build_function_name(name, funcName);
265
266		// for each image_id in team_id
267		int32 cookie = 0;
268		while (instantiationFunc == NULL
269			&& get_next_image_info(threadInfo.team, &cookie, &imageInfo)
270				== B_OK) {
271			instantiationFunc = find_function_in_image(funcName, imageInfo.id,
272				err);
273		}
274		if (instantiationFunc != NULL) {
275			// if requested, save the image id in
276			// which the function was found
277			if (id != NULL)
278				*id = imageInfo.id;
279			break;
280		}
281
282		// Check if we have a private class, and add the BPrivate namespace
283		// (for backwards compatibility)
284		if (!add_private_namespace(name))
285			break;
286	}
287
288	if (instantiationFunc != NULL
289		&& check_signature(signature, imageInfo) != B_OK)
290		return NULL;
291
292	return instantiationFunc;
293}
294
295}	// namespace BPrivate
296
297
298//	#pragma mark - BArchivable
299
300
301BArchivable::BArchivable()
302	:
303	fArchivingToken(NULL_TOKEN)
304{
305}
306
307
308BArchivable::BArchivable(BMessage* from)
309	:
310	fArchivingToken(NULL_TOKEN)
311{
312	if (BUnarchiver::IsArchiveManaged(from)) {
313		BUnarchiver::PrepareArchive(from);
314		BUnarchiver(from).RegisterArchivable(this);
315	}
316}
317
318
319BArchivable::~BArchivable()
320{
321}
322
323
324status_t
325BArchivable::Archive(BMessage* into, bool deep) const
326{
327	if (!into) {
328		// TODO: logging/other error reporting?
329		return B_BAD_VALUE;
330	}
331
332	if (BManagerBase::ArchiveManager(into))
333		BArchiver(into).RegisterArchivable(this);
334
335	BString name;
336	status_t status = demangle_class_name(typeid(*this).name(), name);
337	if (status != B_OK)
338		return status;
339
340	return into->AddString(B_CLASS_FIELD, name);
341}
342
343
344BArchivable*
345BArchivable::Instantiate(BMessage* from)
346{
347	debugger("Can't create a plain BArchivable object");
348	return NULL;
349}
350
351
352status_t
353BArchivable::Perform(perform_code d, void* arg)
354{
355	switch (d) {
356		case PERFORM_CODE_ALL_UNARCHIVED:
357		{
358			perform_data_all_unarchived* data =
359				(perform_data_all_unarchived*)arg;
360
361			data->return_value = BArchivable::AllUnarchived(data->archive);
362			return B_OK;
363		}
364
365		case PERFORM_CODE_ALL_ARCHIVED:
366		{
367			perform_data_all_archived* data =
368				(perform_data_all_archived*)arg;
369
370			data->return_value = BArchivable::AllArchived(data->archive);
371			return B_OK;
372		}
373	}
374
375	return B_NAME_NOT_FOUND;
376}
377
378
379status_t
380BArchivable::AllUnarchived(const BMessage* archive)
381{
382	return B_OK;
383}
384
385
386status_t
387BArchivable::AllArchived(BMessage* archive) const
388{
389	return B_OK;
390}
391
392
393// #pragma mark - BArchiver
394
395
396BArchiver::BArchiver(BMessage* archive)
397	:
398	fManager(BManagerBase::ArchiveManager(archive)),
399	fArchive(archive),
400	fFinished(false)
401{
402	if (fManager == NULL)
403		fManager = new BArchiveManager(this);
404}
405
406
407BArchiver::~BArchiver()
408{
409	if (!fFinished)
410		fManager->ArchiverLeaving(this, B_OK);
411}
412
413
414status_t
415BArchiver::AddArchivable(const char* name, BArchivable* archivable, bool deep)
416{
417	int32 token;
418	status_t err = GetTokenForArchivable(archivable, deep, token);
419
420	if (err != B_OK)
421		return err;
422
423	return fArchive->AddInt32(name, token);
424}
425
426
427status_t
428BArchiver::GetTokenForArchivable(BArchivable* archivable,
429	bool deep, int32& _token)
430{
431	return fManager->ArchiveObject(archivable, deep, _token);
432}
433
434
435bool
436BArchiver::IsArchived(BArchivable* archivable)
437{
438	return fManager->IsArchived(archivable);
439}
440
441
442status_t
443BArchiver::Finish(status_t err)
444{
445	if (fFinished)
446		debugger("Finish() called multiple times on same BArchiver.");
447
448	fFinished = true;
449
450	return fManager->ArchiverLeaving(this, err);
451}
452
453
454BMessage*
455BArchiver::ArchiveMessage() const
456{
457	return fArchive;
458}
459
460
461void
462BArchiver::RegisterArchivable(const BArchivable* archivable)
463{
464	fManager->RegisterArchivable(archivable);
465}
466
467
468// #pragma mark - BUnarchiver
469
470
471BUnarchiver::BUnarchiver(const BMessage* archive)
472	:
473	fManager(BManagerBase::UnarchiveManager(archive)),
474	fArchive(archive),
475	fFinished(false)
476{
477}
478
479
480BUnarchiver::~BUnarchiver()
481{
482	if (!fFinished && fManager)
483		fManager->UnarchiverLeaving(this, B_OK);
484}
485
486
487template<>
488status_t
489BUnarchiver::GetObject<BArchivable>(int32 token,
490	ownership_policy owning, BArchivable*& object)
491{
492	_CallDebuggerIfManagerNull();
493	return fManager->GetArchivableForToken(token, owning, object);
494}
495
496
497template<>
498status_t
499BUnarchiver::FindObject<BArchivable>(const char* name,
500	int32 index, ownership_policy owning, BArchivable*& archivable)
501{
502	archivable = NULL;
503	int32 token;
504	status_t err = fArchive->FindInt32(name, index, &token);
505	if (err != B_OK)
506		return err;
507
508	return GetObject(token, owning, archivable);
509}
510
511
512bool
513BUnarchiver::IsInstantiated(int32 token)
514{
515	_CallDebuggerIfManagerNull();
516	return fManager->IsInstantiated(token);
517}
518
519
520bool
521BUnarchiver::IsInstantiated(const char* field, int32 index)
522{
523	int32 token;
524	if (fArchive->FindInt32(field, index, &token) == B_OK)
525		return IsInstantiated(token);
526
527	return false;
528}
529
530
531status_t
532BUnarchiver::Finish(status_t err)
533{
534	if (fFinished)
535		debugger("Finish() called multiple times on same BArchiver.");
536
537	fFinished = true;
538	if (fManager)
539		return fManager->UnarchiverLeaving(this, err);
540	else
541		return B_OK;
542}
543
544
545const BMessage*
546BUnarchiver::ArchiveMessage() const
547{
548	return fArchive;
549}
550
551
552void
553BUnarchiver::AssumeOwnership(BArchivable* archivable)
554{
555	_CallDebuggerIfManagerNull();
556	fManager->AssumeOwnership(archivable);
557}
558
559
560void
561BUnarchiver::RelinquishOwnership(BArchivable* archivable)
562{
563	_CallDebuggerIfManagerNull();
564	fManager->RelinquishOwnership(archivable);
565}
566
567
568bool
569BUnarchiver::IsArchiveManaged(const BMessage* archive)
570{
571	// managed child archives will return here
572	if (BManagerBase::ManagerPointer(archive))
573		return true;
574
575	if (archive == NULL)
576		return false;
577
578	// managed top level archives return here
579	bool dummy;
580	if (archive->FindBool(kManagedField, &dummy) == B_OK)
581		return true;
582
583	return false;
584}
585
586
587template<>
588status_t
589BUnarchiver::InstantiateObject<BArchivable>(BMessage* from,
590	BArchivable* &object)
591{
592	BUnarchiver unarchiver(BUnarchiver::PrepareArchive(from));
593	object = instantiate_object(from);
594	return unarchiver.Finish();
595}
596
597
598BMessage*
599BUnarchiver::PrepareArchive(BMessage* &archive)
600{
601	// this check allows PrepareArchive to be
602	// called on new or old-style archives
603	if (BUnarchiver::IsArchiveManaged(archive)) {
604		BUnarchiveManager* manager = BManagerBase::UnarchiveManager(archive);
605		if (!manager)
606			manager = new BUnarchiveManager(archive);
607
608		manager->Acquire();
609	}
610
611	return archive;
612}
613
614
615void
616BUnarchiver::RegisterArchivable(BArchivable* archivable)
617{
618	_CallDebuggerIfManagerNull();
619	fManager->RegisterArchivable(archivable);
620}
621
622
623void
624BUnarchiver::_CallDebuggerIfManagerNull()
625{
626	if (!fManager)
627		debugger("BUnarchiver used with legacy or unprepared archive.");
628}
629
630
631// #pragma mark -
632
633
634BArchivable*
635instantiate_object(BMessage* archive, image_id* _id)
636{
637	status_t statusBuffer;
638	status_t* status = &statusBuffer;
639	if (_id != NULL)
640		status = _id;
641
642	// Check our params
643	if (archive == NULL) {
644		syslog(LOG_ERR, "instantiate_object failed: NULL BMessage argument");
645		*status = B_BAD_VALUE;
646		return NULL;
647	}
648
649	// Get class name from archive
650	const char* className = NULL;
651	status_t err = archive->FindString(B_CLASS_FIELD, &className);
652	if (err) {
653		syslog(LOG_ERR, "instantiate_object failed: Failed to find an entry "
654			"defining the class name (%s).", strerror(err));
655		*status = B_BAD_VALUE;
656		return NULL;
657	}
658
659	// Get sig from archive
660	const char* signature = NULL;
661	bool hasSignature = archive->FindString(B_ADD_ON_FIELD, &signature) == B_OK;
662
663	instantiation_func instantiationFunc = BPrivate::find_instantiation_func(
664		className, signature, _id);
665
666	// if find_instantiation_func() can't locate Class::Instantiate()
667	// and a signature was specified
668	if (!instantiationFunc && hasSignature) {
669		// use BRoster::FindApp() to locate an app or add-on with the symbol
670		BRoster Roster;
671		entry_ref ref;
672		err = Roster.FindApp(signature, &ref);
673
674		// if an entry_ref is obtained
675		BEntry entry;
676		if (err == B_OK)
677			err = entry.SetTo(&ref);
678
679		BPath path;
680		if (err == B_OK)
681			err = entry.GetPath(&path);
682
683		if (err != B_OK) {
684			syslog(LOG_ERR, "instantiate_object failed: Error finding app "
685				"with signature \"%s\" (%s)", signature, strerror(err));
686			*status = err;
687			return NULL;
688		}
689
690		// load the app/add-on
691		image_id addOn = load_add_on(path.Path());
692		if (addOn < B_OK) {
693			syslog(LOG_ERR, "instantiate_object failed: Could not load "
694				"add-on %s: %s.", path.Path(), strerror(addOn));
695			*status = addOn;
696			return NULL;
697		}
698
699		// Save the image_id
700		if (_id != NULL)
701			*_id = addOn;
702
703		BString name = className;
704		for (int32 pass = 0; pass < 2; pass++) {
705			BString funcName;
706			build_function_name(name, funcName);
707
708			instantiationFunc = find_function_in_image(funcName, addOn, err);
709			if (instantiationFunc != NULL)
710				break;
711
712			// Check if we have a private class, and add the BPrivate namespace
713			// (for backwards compatibility)
714			if (!add_private_namespace(name))
715				break;
716		}
717
718		if (instantiationFunc == NULL) {
719			syslog(LOG_ERR, "instantiate_object failed: Failed to find exported "
720				"Instantiate static function for class %s.", className);
721			*status = B_NAME_NOT_FOUND;
722			return NULL;
723		}
724	} else if (instantiationFunc == NULL) {
725		syslog(LOG_ERR, "instantiate_object failed: No signature specified "
726			"in archive, looking for class \"%s\".", className);
727		*status = B_NAME_NOT_FOUND;
728		return NULL;
729	}
730
731	// if Class::Instantiate(BMessage*) was found
732	if (instantiationFunc != NULL) {
733		// use to create and return an object instance
734		return instantiationFunc(archive);
735	}
736
737	return NULL;
738}
739
740
741BArchivable*
742instantiate_object(BMessage* from)
743{
744	return instantiate_object(from, NULL);
745}
746
747
748//	#pragma mark - support_globals
749
750
751bool
752validate_instantiation(BMessage* from, const char* className)
753{
754	// Make sure our params are kosher -- original skimped here =P
755	if (!from) {
756		errno = B_BAD_VALUE;
757		return false;
758	}
759
760	BString name = className;
761	for (int32 pass = 0; pass < 2; pass++) {
762		const char* archiveClassName;
763		for (int32 index = 0; from->FindString(B_CLASS_FIELD, index,
764				&archiveClassName) == B_OK; ++index) {
765			if (name == archiveClassName) {
766				errno = B_OK;
767				return true;
768			}
769		}
770
771		if (!add_private_namespace(name))
772			break;
773	}
774
775	errno = B_MISMATCHED_VALUES;
776	syslog(LOG_ERR, "validate_instantiation failed on class %s.", className);
777
778	return false;
779}
780
781
782instantiation_func
783find_instantiation_func(const char* className, const char* signature)
784{
785	return BPrivate::find_instantiation_func(className, signature, NULL);
786}
787
788
789instantiation_func
790find_instantiation_func(const char* className)
791{
792	return find_instantiation_func(className, NULL);
793}
794
795
796instantiation_func
797find_instantiation_func(BMessage* archive)
798{
799	if (archive == NULL) {
800		errno = B_BAD_VALUE;
801		return NULL;
802	}
803
804	const char* name = NULL;
805	const char* signature = NULL;
806	if (archive->FindString(B_CLASS_FIELD, &name) != B_OK
807		|| archive->FindString(B_ADD_ON_FIELD, &signature)) {
808		errno = B_BAD_VALUE;
809		return NULL;
810	}
811
812	return find_instantiation_func(name, signature);
813}
814
815
816//	#pragma mark - BArchivable binary compatibility
817
818
819#if __GNUC__ == 2
820
821extern "C" status_t
822_ReservedArchivable1__11BArchivable(BArchivable* archivable,
823	const BMessage* archive)
824{
825	// AllUnarchived
826	perform_data_all_unarchived performData;
827	performData.archive = archive;
828
829	archivable->Perform(PERFORM_CODE_ALL_UNARCHIVED, &performData);
830	return performData.return_value;
831}
832
833
834extern "C" status_t
835_ReservedArchivable2__11BArchivable(BArchivable* archivable,
836	BMessage* archive)
837{
838	// AllArchived
839	perform_data_all_archived performData;
840	performData.archive = archive;
841
842	archivable->Perform(PERFORM_CODE_ALL_ARCHIVED, &performData);
843	return performData.return_value;
844}
845
846
847#elif __GNUC__ > 2
848
849extern "C" status_t
850_ZN11BArchivable20_ReservedArchivable1Ev(BArchivable* archivable,
851	const BMessage* archive)
852{
853	// AllUnarchived
854	perform_data_all_unarchived performData;
855	performData.archive = archive;
856
857	archivable->Perform(PERFORM_CODE_ALL_UNARCHIVED, &performData);
858	return performData.return_value;
859}
860
861
862extern "C" status_t
863_ZN11BArchivable20_ReservedArchivable2Ev(BArchivable* archivable,
864	BMessage* archive)
865{
866	// AllArchived
867	perform_data_all_archived performData;
868	performData.archive = archive;
869
870	archivable->Perform(PERFORM_CODE_ALL_ARCHIVED, &performData);
871	return performData.return_value;
872}
873
874#endif // _GNUC__ > 2
875
876
877void BArchivable::_ReservedArchivable3() {}
878