xref: /haiku/src/kits/support/Archivable.cpp (revision 0dacda17d52333639ef3874b7e67702b646a5672)
1 /*
2  * Copyright (c) 2001-2008, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Erik Jaesler (erik@cgsoftware.com)
7  */
8 
9 /*!	BArchivable mix-in class defines the archiving protocol.
10 	Also some global archiving functions.
11 */
12 
13 
14 #include <ctype.h>
15 #include <errno.h>
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <string>
19 #include <syslog.h>
20 #include <typeinfo>
21 #include <vector>
22 
23 #include <AppFileInfo.h>
24 #include <Archivable.h>
25 #include <Entry.h>
26 #include <List.h>
27 #include <OS.h>
28 #include <Path.h>
29 #include <Roster.h>
30 #include <String.h>
31 
32 
33 using std::string;
34 using std::vector;
35 
36 const char* B_CLASS_FIELD = "class";
37 const char* B_ADD_ON_FIELD = "add_on";
38 const int32 FUNC_NAME_LEN = 1024;
39 
40 // TODO: consider moving these to a separate module, and making them more
41 //	full-featured (e.g., taking NS::ClassName::Function(Param p) instead
42 //	of just NS::ClassName)
43 
44 
45 static status_t
46 demangle_class_name(const char* name, BString& out)
47 {
48 // TODO: add support for template classes
49 //	_find__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCccUlUl
50 
51 	out = "";
52 
53 	if (name[0] == 'Q') {
54 		// The name is in a namespace
55 		int namespaceCount = 0;
56 		name++;
57 		if (name[0] == '_') {
58 			// more than 10 namespaces deep
59 			if (!isdigit(*++name))
60 				return B_BAD_VALUE;
61 
62 			namespaceCount = strtoul(name, (char**)&name, 10);
63 			if (name[0] != '_')
64 				return B_BAD_VALUE;
65 		} else
66 			namespaceCount = name[0] - '0';
67 
68 		name++;
69 
70 		for (int i = 0; i < namespaceCount - 1; i++) {
71 			if (!isdigit(name[0]))
72 				return B_BAD_VALUE;
73 
74 			int nameLength = strtoul(name, (char**)&name, 10);
75 			out.Append(name, nameLength);
76 			out += "::";
77 			name += nameLength;
78 		}
79 	}
80 
81 	int nameLength = strtoul(name, (char**)&name, 10);
82 	out.Append(name, nameLength);
83 
84 	return B_OK;
85 }
86 
87 
88 static void
89 mangle_class_name(const char* name, BString& out)
90 {
91 // TODO: add support for template classes
92 //	_find__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCccUlUl
93 
94 	//	Chop this:
95 	//		testthree::testfour::Testthree::Testfour
96 	//	up into little bite-sized pieces
97 	int count = 0;
98 	string origName(name);
99 	vector<string> spacenames;
100 
101 	string::size_type pos = 0;
102 	string::size_type oldpos = 0;
103 	while (pos != string::npos) {
104 		pos = origName.find_first_of("::", oldpos);
105 		spacenames.push_back(string(origName, oldpos, pos - oldpos));
106 		pos = origName.find_first_not_of("::", pos);
107 		oldpos = pos;
108 		++count;
109 	}
110 
111 	//	Now mangle it into this:
112 	//		Q49testthree8testfour9Testthree8Testfour
113 	out = "";
114 	if (count > 1) {
115 		out += 'Q';
116 		if (count > 10)
117 			out += '_';
118 		out << count;
119 		if (count > 10)
120 			out += '_';
121 	}
122 
123 	for (unsigned int i = 0; i < spacenames.size(); ++i) {
124 		out << (int)spacenames[i].length();
125 		out += spacenames[i].c_str();
126 	}
127 }
128 
129 
130 static void
131 build_function_name(const BString& className, BString& funcName)
132 {
133 	funcName = "";
134 
135 	//	This is what we're after:
136 	//		Instantiate__Q28OpenBeOS11BArchivableP8BMessage
137 	mangle_class_name(className.String(), funcName);
138 #if __GNUC__ >= 4
139 	funcName.Prepend("_ZN");
140 	funcName.Append("11InstantiateE");
141 #else
142 	funcName.Prepend("Instantiate__");
143 #endif
144 	funcName.Append("P8BMessage");
145 }
146 
147 
148 static bool
149 add_private_namespace(BString& name)
150 {
151 	if (name.Compare("_", 1) != 0)
152 		return false;
153 
154 	name.Prepend("BPrivate::");
155 	return true;
156 }
157 
158 
159 static instantiation_func
160 find_function_in_image(BString& funcName, image_id id, status_t& err)
161 {
162 	instantiation_func instantiationFunc = NULL;
163 	err = get_image_symbol(id, funcName.String(), B_SYMBOL_TYPE_TEXT,
164 		(void**)&instantiationFunc);
165 	if (err != B_OK)
166 		return NULL;
167 
168 	return instantiationFunc;
169 }
170 
171 
172 static status_t
173 check_signature(const char* signature, image_info& info)
174 {
175 	if (signature == NULL) {
176 		// If it wasn't specified, anything "matches"
177 		return B_OK;
178 	}
179 
180 	// Get image signature
181 	BFile file(info.name, B_READ_ONLY);
182 	status_t err = file.InitCheck();
183 	if (err != B_OK)
184 		return err;
185 
186 	char imageSignature[B_MIME_TYPE_LENGTH];
187 	BAppFileInfo appFileInfo(&file);
188 	err = appFileInfo.GetSignature(imageSignature);
189 	if (err != B_OK) {
190 		syslog(LOG_ERR, "instantiate_object - couldn't get mime sig for %s",
191 			info.name);
192 		return err;
193 	}
194 
195 	if (strcmp(signature, imageSignature))
196 		return B_MISMATCHED_VALUES;
197 
198 	return B_OK;
199 }
200 
201 
202 //	#pragma mark -
203 
204 
205 BArchivable::BArchivable()
206 {
207 }
208 
209 
210 BArchivable::BArchivable(BMessage* from)
211 {
212 }
213 
214 
215 BArchivable::~BArchivable()
216 {
217 }
218 
219 
220 status_t
221 BArchivable::Archive(BMessage* into, bool deep) const
222 {
223 	if (!into) {
224 		// TODO: logging/other error reporting?
225 		return B_BAD_VALUE;
226 	}
227 
228 	BString name;
229 	status_t status = demangle_class_name(typeid(*this).name(), name);
230 	if (status != B_OK)
231 		return status;
232 
233 	return into->AddString(B_CLASS_FIELD, name);
234 }
235 
236 
237 BArchivable*
238 BArchivable::Instantiate(BMessage* from)
239 {
240 	debugger("Can't create a plain BArchivable object");
241 	return NULL;
242 }
243 
244 
245 status_t
246 BArchivable::Perform(perform_code d, void* arg)
247 {
248 	// TODO: Check against original
249 	return B_ERROR;
250 }
251 
252 
253 void BArchivable::_ReservedArchivable1() {}
254 void BArchivable::_ReservedArchivable2() {}
255 void BArchivable::_ReservedArchivable3() {}
256 
257 
258 // #pragma mark -
259 
260 
261 BArchivable*
262 instantiate_object(BMessage* archive, image_id* _id)
263 {
264 	status_t statusBuffer;
265 	status_t* status = &statusBuffer;
266 	if (_id != NULL)
267 		status = _id;
268 
269 	// Check our params
270 	if (archive == NULL) {
271 		syslog(LOG_ERR, "instantiate_object failed: NULL BMessage argument");
272 		*status = B_BAD_VALUE;
273 		return NULL;
274 	}
275 
276 	// Get class name from archive
277 	const char* className = NULL;
278 	status_t err = archive->FindString(B_CLASS_FIELD, &className);
279 	if (err) {
280 		syslog(LOG_ERR, "instantiate_object failed: Failed to find an entry "
281 			"defining the class name (%s).", strerror(err));
282 		*status = B_BAD_VALUE;
283 		return NULL;
284 	}
285 
286 	// Get sig from archive
287 	const char* signature = NULL;
288 	bool hasSignature = archive->FindString(B_ADD_ON_FIELD, &signature) == B_OK;
289 
290 	instantiation_func instantiationFunc = find_instantiation_func(className,
291 		signature);
292 
293 	// if find_instantiation_func() can't locate Class::Instantiate()
294 	// and a signature was specified
295 	if (!instantiationFunc && hasSignature) {
296 		// use BRoster::FindApp() to locate an app or add-on with the symbol
297 		BRoster Roster;
298 		entry_ref ref;
299 		err = Roster.FindApp(signature, &ref);
300 
301 		// if an entry_ref is obtained
302 		BEntry entry;
303 		if (err == B_OK)
304 			err = entry.SetTo(&ref);
305 
306 		BPath path;
307 		if (err == B_OK)
308 			err = entry.GetPath(&path);
309 
310 		if (err != B_OK) {
311 			syslog(LOG_ERR, "instantiate_object failed: Error finding app "
312 				"with signature \"%s\" (%s)", signature, strerror(err));
313 			*status = err;
314 			return NULL;
315 		}
316 
317 		// load the app/add-on
318 		image_id addOn = load_add_on(path.Path());
319 		if (addOn < B_OK) {
320 			syslog(LOG_ERR, "instantiate_object failed: Could not load "
321 				"add-on %s: %s.", path.Path(), strerror(addOn));
322 			*status = addOn;
323 			return NULL;
324 		}
325 
326 		// Save the image_id
327 		if (_id != NULL)
328 			*_id = addOn;
329 
330 		BString name = className;
331 		for (int32 pass = 0; pass < 2; pass++) {
332 			BString funcName;
333 			build_function_name(name, funcName);
334 
335 			instantiationFunc = find_function_in_image(funcName, addOn, err);
336 			if (instantiationFunc != NULL)
337 				break;
338 
339 			// Check if we have a private class, and add the BPrivate namespace
340 			// (for backwards compatibility)
341 			if (!add_private_namespace(name))
342 				break;
343 		}
344 
345 		if (instantiationFunc == NULL) {
346 			syslog(LOG_ERR, "instantiate_object failed: Failed to find exported "
347 				"Instantiate static function for class %s.", className);
348 			*status = B_NAME_NOT_FOUND;
349 			return NULL;
350 		}
351 	} else if (instantiationFunc == NULL) {
352 		syslog(LOG_ERR, "instantiate_object failed: No signature specified "
353 			"in archive, looking for class \"%s\".", className);
354 		*status = B_NAME_NOT_FOUND;
355 		return NULL;
356 	}
357 
358 	// if Class::Instantiate(BMessage*) was found
359 	if (instantiationFunc != NULL) {
360 		// use to create and return an object instance
361 		return instantiationFunc(archive);
362 	}
363 
364 	return NULL;
365 }
366 
367 
368 BArchivable*
369 instantiate_object(BMessage* from)
370 {
371 	return instantiate_object(from, NULL);
372 }
373 
374 
375 bool
376 validate_instantiation(BMessage* from, const char* className)
377 {
378 	// Make sure our params are kosher -- original skimped here =P
379 	if (!from) {
380 		errno = B_BAD_VALUE;
381 		return false;
382 	}
383 
384 	BString name = className;
385 	for (int32 pass = 0; pass < 2; pass++) {
386 		const char* archiveClassName;
387 		for (int32 index = 0; from->FindString(B_CLASS_FIELD, index,
388 				&archiveClassName) == B_OK; ++index) {
389 			if (name == archiveClassName)
390 				return true;
391 		}
392 
393 		if (!add_private_namespace(name))
394 			break;
395 	}
396 
397 	errno = B_MISMATCHED_VALUES;
398 	syslog(LOG_ERR, "validate_instantiation failed on class %s.", className);
399 
400 	return false;
401 }
402 
403 
404 instantiation_func
405 find_instantiation_func(const char* className, const char* signature)
406 {
407 	if (className == NULL) {
408 		errno = B_BAD_VALUE;
409 		return NULL;
410 	}
411 
412 	thread_info threadInfo;
413 	status_t err = get_thread_info(find_thread(NULL), &threadInfo);
414 	if (err != B_OK) {
415 		errno = err;
416 		return NULL;
417 	}
418 
419 	instantiation_func instantiationFunc = NULL;
420 	image_info imageInfo;
421 
422 	BString name = className;
423 	for (int32 pass = 0; pass < 2; pass++) {
424 		BString funcName;
425 		build_function_name(name, funcName);
426 
427 		// for each image_id in team_id
428 		int32 cookie = 0;
429 		while (instantiationFunc == NULL
430 			&& get_next_image_info(threadInfo.team, &cookie, &imageInfo)
431 				== B_OK) {
432 			instantiationFunc = find_function_in_image(funcName, imageInfo.id,
433 				err);
434 		}
435 		if (instantiationFunc != NULL)
436 			break;
437 
438 		// Check if we have a private class, and add the BPrivate namespace
439 		// (for backwards compatibility)
440 		if (!add_private_namespace(name))
441 			break;
442 	}
443 
444 	if (instantiationFunc != NULL
445 		&& check_signature(signature, imageInfo) != B_OK)
446 		return NULL;
447 
448 	return instantiationFunc;
449 }
450 
451 
452 instantiation_func
453 find_instantiation_func(const char* className)
454 {
455 	return find_instantiation_func(className, NULL);
456 }
457 
458 
459 instantiation_func
460 find_instantiation_func(BMessage* archive)
461 {
462 	if (archive == NULL) {
463 		errno = B_BAD_VALUE;
464 		return NULL;
465 	}
466 
467 	const char* name = NULL;
468 	const char* signature = NULL;
469 	if (archive->FindString(B_CLASS_FIELD, &name) != B_OK
470 		|| archive->FindString(B_ADD_ON_FIELD, &signature)) {
471 		errno = B_BAD_VALUE;
472 		return NULL;
473 	}
474 
475 	return find_instantiation_func(name, signature);
476 }
477 
478