xref: /haiku/src/kits/support/Archivable.cpp (revision 7120e97489acbf17d86d3f33e3b2e68974fd4b23)
1 //------------------------------------------------------------------------------
2 //	Copyright (c) 2001-2002, OpenBeOS
3 //
4 //	Permission is hereby granted, free of charge, to any person obtaining a
5 //	copy of this software and associated documentation files (the "Software"),
6 //	to deal in the Software without restriction, including without limitation
7 //	the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 //	and/or sell copies of the Software, and to permit persons to whom the
9 //	Software is furnished to do so, subject to the following conditions:
10 //
11 //	The above copyright notice and this permission notice shall be included in
12 //	all copies or substantial portions of the Software.
13 //
14 //	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 //	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 //	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 //	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 //	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 //	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 //	DEALINGS IN THE SOFTWARE.
21 //
22 //	File Name:		Archivable.cpp
23 //	Author:			Erik Jaesler (erik@cgsoftware.com)
24 //	Description:	BArchivable mix-in class defines the archiving
25 //					protocol.  Also some global archiving functions.
26 //------------------------------------------------------------------------------
27 
28 // Standard Includes -----------------------------------------------------------
29 #include <ctype.h>
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string>
34 #include <typeinfo>
35 #include <vector>
36 
37 // System Includes -------------------------------------------------------------
38 #include <AppFileInfo.h>
39 #include <Archivable.h>
40 #include <Entry.h>
41 #include <List.h>
42 #include <OS.h>
43 #include <Path.h>
44 #include <Roster.h>
45 #include <String.h>
46 #include <SupportDefs.h>
47 #include <syslog.h>
48 
49 // Project Includes ------------------------------------------------------------
50 
51 // Local Includes --------------------------------------------------------------
52 
53 // Local Defines ---------------------------------------------------------------
54 
55 // Globals ---------------------------------------------------------------------
56 
57 
58 using std::string;
59 using std::vector;
60 
61 const char* B_CLASS_FIELD = "class";
62 const char* B_ADD_ON_FIELD = "add_on";
63 const int32 FUNC_NAME_LEN = 1024;
64 
65 // TODO: consider moving these
66 //		 to a separate module, and making them more full-featured (e.g., taking
67 //		 NS::ClassName::Function(Param p) instead of just NS::ClassName)
68 static void Demangle(const char *name, BString &out);
69 static void Mangle(const char *name, BString &out);
70 static instantiation_func FindFuncInImage(BString& funcName, image_id id,
71 										  status_t& err);
72 static bool CheckSig(const char* sig, image_info& info);
73 
74 /*
75 // TODO: Where do these get triggered from?
76 Log entries graciously coughed up by the Be implementation:
77 	Nov 28 01:40:45 instantiate_object failed: NULL BMessage argument
78 	Nov 28 01:40:45 instantiate_object failed: Failed to find an entrydefining the class name (Name not found).
79 	Nov 28 01:40:45 instantiate_object failed: No signature specified in archive, looking for class "TInvalidClassName".
80 	Nov 28 01:40:45 instantiate_object failed: Error finding app with signature "application/x-vnd.InvalidSignature" (Application could not be found)
81 	Nov 28 01:40:45 instantiate_object failed: Application could not be found (8000200b)
82 	Nov 28 01:40:45 instantiate_object failed: Failed to find exported Instantiate static function for class TInvalidClassName.
83 	Nov 28 01:40:45 instantiate_object failed: Invalid argument (80000005)
84 	Nov 28 01:40:45 instantiate_object failed: No signature specified in archive, looking for class "TRemoteTestObject".
85 	Nov 28 01:40:45 instantiate_object - couldn't get mime sig for /boot/home/src/projects/OpenBeOS/app_kit/test/lib/support/BArchivable/./BArchivableSystemTester
86 	Nov 28 01:40:45 instantiate_object failed: Error finding app with signature "application/x-vnd.InvalidSignature" (Application could not be found)
87 	Nov 28 01:40:45 instantiate_object failed: Application could not be found (8000200b)
88 	Nov 28 01:40:45 instantiate_object failed: Error finding app with signature "application/x-vnd.InvalidSignature" (Application could not be found)
89 	Nov 28 01:40:45 instantiate_object failed: Application could not be found (8000200b)
90 	Nov 28 01:40:45 instantiate_object failed: Error finding app with signature "application/x-vnd.InvalidSignature" (Application could not be found)
91 	Nov 28 01:40:45 instantiate_object failed: Application could not be found (8000200b)
92 */
93 
94 //------------------------------------------------------------------------------
95 BArchivable::BArchivable()
96 {
97 	;
98 }
99 //------------------------------------------------------------------------------
100 BArchivable::BArchivable(BMessage* from)
101 {
102 	;
103 }
104 //------------------------------------------------------------------------------
105 BArchivable::~BArchivable()
106 {
107 	;
108 }
109 //------------------------------------------------------------------------------
110 status_t BArchivable::Archive(BMessage* into, bool deep) const
111 {
112 	if (!into)
113 	{
114 		// TODO: logging/other error reporting?
115 		return B_BAD_VALUE;
116 	}
117 
118 	BString name;
119 	Demangle(typeid(*this).name(), name);
120 
121 	return into->AddString(B_CLASS_FIELD, name);
122 }
123 //------------------------------------------------------------------------------
124 BArchivable* BArchivable::Instantiate(BMessage* from)
125 {
126 	debugger("Can't create a plain BArchivable object");
127 	return NULL;
128 }
129 //------------------------------------------------------------------------------
130 status_t BArchivable::Perform(perform_code d, void* arg)
131 {
132 	// TODO: Check against original
133 	return B_ERROR;
134 }
135 //------------------------------------------------------------------------------
136 void BArchivable::_ReservedArchivable1()
137 {
138 	;
139 }
140 //------------------------------------------------------------------------------
141 void BArchivable::_ReservedArchivable2()
142 {
143 	;
144 }
145 //------------------------------------------------------------------------------
146 void BArchivable::_ReservedArchivable3()
147 {
148 	;
149 }
150 //------------------------------------------------------------------------------
151 void BuildFuncName(const char* className, BString& funcName)
152 {
153 	funcName = "";
154 
155 	//	This is what we're after:
156 	//		Instantiate__Q28OpenBeOS11BArchivableP8BMessage
157 	Mangle(className, funcName);
158 	funcName.Prepend("Instantiate__");
159 	funcName.Append("P8BMessage");
160 }
161 //------------------------------------------------------------------------------
162 BArchivable* instantiate_object(BMessage* archive, image_id* id)
163 {
164 	errno = B_OK;
165 
166 	// Check our params
167 	if (id)
168 	{
169 		*id = B_BAD_VALUE;
170 	}
171 
172 	if (!archive)
173 	{
174 		// TODO: extended error handling
175 		errno = B_BAD_VALUE;
176 		syslog(LOG_ERR, "instantiate_object failed: NULL BMessage argument");
177 		return NULL;
178 	}
179 
180 	// Get class name from archive
181 	const char* name = NULL;
182 	status_t err = archive->FindString(B_CLASS_FIELD, &name);
183 	if (err)
184 	{
185 		// TODO: extended error handling
186 		syslog(LOG_ERR, "instantiate_object failed: Failed to find an entry "
187 			   "defining the class name (%s).", strerror(err));
188 		return NULL;
189 	}
190 
191 	// Get sig from archive
192 	const char* sig = NULL;
193 	bool hasSig = (archive->FindString(B_ADD_ON_FIELD, &sig) == B_OK);
194 
195 	instantiation_func iFunc = find_instantiation_func(name, sig);
196 
197 	//	if find_instantiation_func() can't locate Class::Instantiate()
198 	//		and a signature was specified
199 	if (!iFunc && hasSig)
200 	{
201 		//	use BRoster::FindApp() to locate an app or add-on with the symbol
202 		BRoster Roster;
203 		entry_ref ref;
204 		err = Roster.FindApp(sig, &ref);
205 
206 		BEntry entry;
207 
208 		//	if an entry_ref is obtained
209 		if (!err)
210 		{
211 			err = entry.SetTo(&ref);
212 		}
213 
214 		if (err)
215 		{
216 			syslog(LOG_ERR, "instantiate_object failed: Error finding app "
217 							"with signature \"%s\" (%s)", sig, strerror(err));
218 		}
219 
220 		if (!err)
221 		{
222 			BPath path;
223 			err = entry.GetPath(&path);
224 			if (!err)
225 			{
226 				//	load the app/add-on
227 				image_id theImage = load_add_on(path.Path());
228 				if (theImage < 0)
229 				{
230 					// TODO: extended error handling
231 					return NULL;
232 				}
233 
234 				// Save the image_id
235 				if (id)
236 				{
237 					*id = theImage;
238 				}
239 
240 				BString funcName;
241 				BuildFuncName(name, funcName);
242 				iFunc = FindFuncInImage(funcName, theImage, err);
243 				if (!iFunc)
244 				{
245 					syslog(LOG_ERR, "instantiate_object failed: Failed to find exported "
246 						   "Instantiate static function for class %s.", name);
247 				}
248 			}
249 		}
250 	}
251 	else if (!iFunc)
252 	{
253 		syslog(LOG_ERR, "instantiate_object failed: No signature specified "
254 			   "in archive, looking for class \"%s\".", name);
255 		errno = B_BAD_VALUE;
256 	}
257 
258 	if (err)
259 	{
260 		// TODO: extended error handling
261 		syslog(LOG_ERR, "instantiate_object failed: %s (%x)",
262 			   strerror(err), err);
263 		errno = err;
264 		return NULL;
265 	}
266 
267 	//	if Class::Instantiate(BMessage*) was found
268 	if (iFunc)
269 	{
270 		//	use to create and return an object instance
271 		return iFunc(archive);
272 	}
273 
274 	return NULL;
275 }
276 //------------------------------------------------------------------------------
277 BArchivable* instantiate_object(BMessage* from)
278 {
279 	return instantiate_object(from, NULL);
280 }
281 //------------------------------------------------------------------------------
282 bool validate_instantiation(BMessage* from, const char* class_name)
283 {
284 	errno = B_OK;
285 
286 	// Make sure our params are kosher -- original skimped here =P
287 	if (!from)
288 	{
289 		// Not standard; Be implementation has a segment
290 		// violation on this error mode
291 		errno = B_BAD_VALUE;
292 
293 		return false;
294 	}
295 
296 	status_t err = B_OK;
297 	const char* data;
298 	for (int32 index = 0; err == B_OK; ++index)
299 	{
300 		err = from->FindString(B_CLASS_FIELD, index, &data);
301 		if (!err && strcmp(data, class_name) == 0)
302 		{
303 			return true;
304 		}
305 	}
306 
307 	errno = B_MISMATCHED_VALUES;
308 	syslog(LOG_ERR, "validate_instantiation failed on class %s.", class_name);
309 
310 	return false;
311 }
312 //------------------------------------------------------------------------------
313 instantiation_func find_instantiation_func(const char* class_name,
314 										   const char* sig)
315 {
316 	errno = B_OK;
317 	if (!class_name)
318 	{
319 		errno = B_BAD_VALUE;
320 		return NULL;
321 	}
322 
323 	instantiation_func theFunc = NULL;
324 	BString funcName;
325 
326 	BuildFuncName(class_name, funcName);
327 
328 	thread_id tid = find_thread(NULL);
329 	thread_info ti;
330 	status_t err = get_thread_info(tid, &ti);
331 	if (!err)
332 	{
333 		//	for each image_id in team_id
334 		image_info info;
335 		int32 cookie = 0;
336 		while (!theFunc && (get_next_image_info(ti.team, &cookie, &info) == B_OK))
337 		{
338 			theFunc = FindFuncInImage(funcName, info.id, err);
339 		}
340 
341 		if (theFunc && !CheckSig(sig, info))
342 		{
343 			// TODO: extended error handling
344 			theFunc = NULL;
345 		}
346 	}
347 
348 	return theFunc;
349 }
350 //------------------------------------------------------------------------------
351 instantiation_func find_instantiation_func(const char* class_name)
352 {
353 	return find_instantiation_func(class_name, NULL);
354 }
355 //------------------------------------------------------------------------------
356 instantiation_func find_instantiation_func(BMessage* archive_data)
357 {
358 	errno = B_OK;
359 
360 	if (!archive_data)
361 	{
362 		// TODO:  extended error handling
363 		errno = B_BAD_VALUE;
364 		return NULL;
365 	}
366 
367 	const char* name = NULL;
368 	const char* sig = NULL;
369 	status_t err;
370 
371 	err = archive_data->FindString(B_CLASS_FIELD, &name);
372 	if (err)
373 	{
374 		// TODO:  extended error handling
375 		return NULL;
376 	}
377 
378 	err = archive_data->FindString(B_ADD_ON_FIELD, &sig);
379 
380 	return find_instantiation_func(name, sig);
381 }
382 //------------------------------------------------------------------------------
383 
384 
385 //------------------------------------------------------------------------------
386 int GetNumber(const char*& name)
387 {
388 	int val = atoi(name);
389 	while (isdigit(*name))
390 	{
391 		++name;
392 	}
393 
394 	return val;
395 }
396 //------------------------------------------------------------------------------
397 void Demangle(const char* name, BString& out)
398 {
399 // TODO: add support for template classes
400 //	_find__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCccUlUl
401 
402 	out = "";
403 
404 	// Are we in a namespace?
405 	if (*name == 'Q')
406 	{
407 		// Yessir, we are; how many deep are we?
408 		int nsCount = 0;
409 		++name;
410 		if (*name == '_')	// more than 10 deep
411 		{
412 			++name;
413 			if (!isdigit(*name))
414 				;	// TODO: error handling
415 
416 			nsCount = GetNumber(name);
417 			if (*name == '_')	// more than 10 deep
418 				++name;
419 			else
420 				;	// this should be an error condition
421 		}
422 		else
423 		{
424 			nsCount = *name - '0';
425 			++name;
426 		}
427 
428 		int nameLen = 0;
429 		for (int i = 0; i < nsCount - 1; ++i)
430 		{
431 			if (!isdigit(*name))
432 				;	// TODO: error handling
433 
434 			nameLen = GetNumber(name);
435 			out.Append(name, nameLen);
436 			out += "::";
437 			name += nameLen;
438 		}
439 	}
440 
441 	out.Append(name, GetNumber(name));
442 }
443 //------------------------------------------------------------------------------
444 void Mangle(const char* name, BString& out)
445 {
446 // TODO: add support for template classes
447 //	_find__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCccUlUl
448 
449 	//	Chop this:
450 	//		testthree::testfour::Testthree::Testfour
451 	//	up into little bite-sized pieces
452 	int count = 0;
453 	string origName(name);
454 	vector<string> spacenames;
455 
456 	string::size_type pos = 0;
457 	string::size_type oldpos = 0;
458 	while (pos != string::npos)
459 	{
460 		pos = origName.find_first_of("::", oldpos);
461 		spacenames.push_back(string(origName, oldpos, pos - oldpos));
462 		pos = origName.find_first_not_of("::", pos);
463 		oldpos = pos;
464 		++count;
465 	}
466 
467 	//	Now mangle it into this:
468 	//		Q49testthree8testfour9Testthree8Testfour
469 	out = "";
470 	if (count > 1)
471 	{
472 		out += 'Q';
473 		if (count > 10)
474 			out += '_';
475 		out << count;
476 		if (count > 10)
477 			out += '_';
478 	}
479 
480 	for (unsigned int i = 0; i < spacenames.size(); ++i)
481 	{
482 		out << (int)spacenames[i].length();
483 		out += spacenames[i].c_str();
484 	}
485 }
486 //------------------------------------------------------------------------------
487 instantiation_func FindFuncInImage(BString& funcName, image_id id,
488 								   status_t& err)
489 {
490 	err = B_OK;
491 	instantiation_func theFunc = NULL;
492 
493 	// Don't need to do it this way ...
494 #if 0
495 	char foundFuncName[FUNC_NAME_LEN];
496 	int32 symbolType;
497 	int32 funcNameLen;
498 
499 	//	for each B_SYMBOL_TYPE_TEXT in image_id
500 	for (int32 i = 0; !err; ++i)
501 	{
502 		funcNameLen = FUNC_NAME_LEN;
503 		err = get_nth_image_symbol(id, i, foundFuncName, &funcNameLen,
504 								   &symbolType, (void**)&theFunc);
505 
506 		if (!err && symbolType == B_SYMBOL_TYPE_TEXT)
507 		{
508 			//	try to match Class::Instantiate(BMessage*) signature
509 			if (funcName.ICompare(foundFuncName, funcNameLen) == 0)
510 				break;
511 			else
512 				theFunc = NULL;
513 		}
514 	}
515 #endif
516 
517 	err = get_image_symbol(id, funcName.String(), B_SYMBOL_TYPE_TEXT,
518 						   (void**)&theFunc);
519 
520 	if (err)
521 	{
522 		// TODO: error handling
523 		theFunc = NULL;
524 	}
525 
526 	return theFunc;
527 }
528 //------------------------------------------------------------------------------
529 bool CheckSig(const char* sig, image_info& info)
530 {
531 	if (!sig)
532 	{
533 		// If it wasn't specified, anything "matches"
534 		return true;
535 	}
536 
537 	status_t err = B_OK;
538 
539 	// Get image signature
540 	BFile ImageFile(info.name, B_READ_ONLY);
541 	err = ImageFile.InitCheck();
542 	if (err)
543 	{
544 		// TODO: extended error handling
545 		return false;
546 	}
547 
548 	char imageSig[B_MIME_TYPE_LENGTH];
549 	BAppFileInfo AFI(&ImageFile);
550 	err = AFI.GetSignature(imageSig);
551 	if (err)
552 	{
553 		// TODO: extended error handling
554 		syslog(LOG_ERR, "instantiate_object - couldn't get mime sig for %s",
555 			   info.name);
556 		return false;
557 	}
558 
559 	return strcmp(sig, imageSig) == 0;
560 }
561 //------------------------------------------------------------------------------
562 
563 
564 /*
565  * $Log $
566  *
567  * $Id  $
568  *
569  */
570 
571