]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/libopensrf/osrf_message.c
Adding comments and tinkering with white space.
[OpenSRF.git] / src / libopensrf / osrf_message.c
1 /**
2         @file osrf_message.c
3         @brief Implementation of osrfMessage.
4 */
5
6 /* libxml stuff for the config reader */
7 #include <libxml/xmlmemory.h>
8 #include <libxml/parser.h>
9 #include <libxml/xpath.h>
10 #include <libxml/xpathInternals.h>
11 #include <libxml/tree.h>
12
13 #include <opensrf/osrf_message.h>
14 #include "opensrf/osrf_stack.h"
15
16 static osrfMessage* deserialize_one_message( const jsonObject* message );
17
18 static char default_locale[17] = "en-US\0\0\0\0\0\0\0\0\0\0\0\0";
19 static char* current_locale = NULL;
20
21 /**
22         @brief Allocate and initialize an osrfMessage.
23         @param type One of CONNECT, REQUEST, RESULT, STATUS, or DISCONNECT.
24         @param thread_trace Thread trace.
25         @param protocol Protocol.
26         @return Pointer to the newly allocated osrfMessage.
27
28         The calling code is responsible for freeing the osrfMessage by calling osrfMessageFree().
29 */
30 osrfMessage* osrf_message_init( enum M_TYPE type, int thread_trace, int protocol ) {
31
32         osrfMessage* msg            = (osrfMessage*) safe_malloc(sizeof(osrfMessage));
33         msg->m_type                 = type;
34         msg->thread_trace           = thread_trace;
35         msg->protocol               = protocol;
36         msg->status_name            = NULL;
37         msg->status_text            = NULL;
38         msg->status_code            = 0;
39         msg->next                   = NULL;
40         msg->is_exception           = 0;
41         msg->_params                = NULL;
42         msg->_result_content        = NULL;
43         msg->method_name            = NULL;
44         msg->sender_locale          = NULL;
45
46         return msg;
47 }
48
49
50 /**
51         @brief Return the previous locale.
52         @return A pointer to the locale specified by the last message to have been deserialized,
53                 or NULL.
54
55         A JSON message may specify a locale string, which is saved as the current locale.  If
56         the message does not specify a locale string, then the current locale becomes NULL.
57
58         This function returns a pointer to an internal buffer.  Since both the address and the
59         contents of this buffer may change from one call to the next, the calling code should
60         either use the result immediately or make a copy of the string for later use.
61 */
62 const char* osrf_message_get_last_locale() {
63         return current_locale;
64 }
65
66 /**
67         @brief Set the locale for a specified osrfMessage.
68         @param msg Pointer to the osrfMessage.
69         @param locale Pointer to the locale string to be installed in the osrfMessage.
70         @return Pointer to the new locale string for the osrfMessage, or NULL if either
71                 parameter is NULL.
72
73         If no locale is specified for an osrfMessage, we use the default locale.
74
75         Used for a REQUEST message.
76 */
77 const char* osrf_message_set_locale( osrfMessage* msg, const char* locale ) {
78         if( msg == NULL || locale == NULL )
79                 return NULL;
80         if( msg->sender_locale )
81                 free( msg->sender_locale );
82         return msg->sender_locale = strdup( locale );
83 }
84
85 /**
86         @brief Change the default locale.
87         @param locale The new default locale.
88         @return A pointer to the new default locale if successful, or NULL if not.
89
90         The function returns NULL if the parameter is NULL, or if the proposed new locale is
91         longer than 16 characters.
92
93         At this writing, nothing calls this function.
94 */
95 const char* osrf_message_set_default_locale( const char* locale ) {
96         if( locale == NULL ) return NULL;
97         if( strlen(locale) > sizeof(default_locale) - 1 ) return NULL;
98
99         strcpy( default_locale, locale );
100         return default_locale;
101 }
102
103 /**
104         @brief Populate the method_name member of an osrfMessage.
105         @param msg Pointer to the osrfMessage.
106         @param method_name The method name.
107
108         Used for a REQUEST message to specify what method to invoke.
109 */
110 void osrf_message_set_method( osrfMessage* msg, const char* method_name ) {
111         if( msg == NULL || method_name == NULL )
112                 return;
113         if( msg->method_name )
114                 free( msg->method_name );
115         msg->method_name = strdup( method_name );
116 }
117
118
119 /**
120         @brief Add a copy of a jsonObject to an osrfMessage as a parameter for a method call.
121         @param msg Pointer to the osrfMessage.
122         @param o Pointer to the jsonObject of which a copy is to be stored.
123
124         Make a copy of the input jsonObject, with all classnames encoded with JSON_CLASS_KEY and
125         JSON_DATA_KEY.  Append it to a JSON_ARRAY stored at msg->_params.
126
127         If there is nothing at msg->_params, create a new JSON_ARRAY for it and add the new object
128         as the first element.
129
130         If the jsonObject is raw (i.e. the class information has not been decoded into classnames),
131         decode it.  If the class information has already been decoded, discard it.
132
133         See also osrf_message_add_param().
134
135         At this writing, nothing calls this function.
136 */
137 void osrf_message_add_object_param( osrfMessage* msg, const jsonObject* o ) {
138         if(!msg|| !o) return;
139         if(!msg->_params)
140                 msg->_params = jsonNewObjectType( JSON_ARRAY );
141         jsonObjectPush(msg->_params, jsonObjectDecodeClass( o ));
142 }
143
144 /**
145         @brief Populate the params member of an osrfMessage.
146         @param msg Pointer to the osrfMessage.
147         @param o Pointer to a jsonObject representing the parameter(s) to a method.
148
149         Make a copy of a jsonObject and install it as the parameter list for a method call.
150
151         If the osrfMessage already has any parameters, discard them.
152
153         The @a o parameter should point to a jsonObject of type JSON_ARRAY, with each element
154         of the array being a parameter.  If @a o points to any other type of jsonObject, create
155         a JSON_ARRAY as a wrapper for it, and install a copy of @a o as its only element.
156
157         Used for a REQUEST message, to pass parameters to a method.  The alternative is to call
158         osrf_message_add_param() or osrf_message_add_object_param() repeatedly as needed to add
159         one parameter at a time.
160 */
161 void osrf_message_set_params( osrfMessage* msg, const jsonObject* o ) {
162         if(!msg || !o) return;
163
164         if(msg->_params)
165                 jsonObjectFree(msg->_params);
166
167         if(o->type == JSON_ARRAY) {
168                 msg->_params = jsonObjectClone(o);
169         } else {
170                 osrfLogDebug( OSRF_LOG_MARK, "passing non-array to osrf_message_set_params(), fixing...");
171                 jsonObject* clone = jsonObjectClone(o);
172                 msg->_params = jsonNewObjectType( JSON_ARRAY );
173                 jsonObjectPush(msg->_params, clone);
174                 return;
175         }
176 }
177
178
179 /**
180         @brief Add a JSON string to an osrfMessage as a parameter for a method call.
181         @param msg Pointer to the osrfMessage.
182         @param param_string A JSON string encoding the parameter to be added.
183
184         Translate the JSON string into a jsonObject, and append it to the parameter list.
185         If the parameter list doesn't already exist, create an empty one and add the new
186         parameter to it.
187
188         Decode any class information in the raw JSON into classnames.
189
190         If the JSON string is not valid JSON, append a new jsonObject of type JSON_NULL.
191
192         Used for a REQUEST message, to pass a parameter to a method,  The alternative is to
193         call osrf_message_set_params() to provide all the parameters at once.  See also
194         osrf_message_add_object_param().
195 */
196 void osrf_message_add_param( osrfMessage* msg, const char* param_string ) {
197         if(msg == NULL || param_string == NULL) return;
198         if(!msg->_params) msg->_params = jsonNewObjectType( JSON_ARRAY );
199         jsonObjectPush(msg->_params, jsonParse(param_string));
200 }
201
202
203 /**
204         @brief Set the status_name, status_text, and status_code members of an osrfMessage.
205         @param msg Pointer to the osrfMessage to be populated.
206         @param status_name Status name (may be NULL).
207         @param status_text Status text (may be NULL).
208         @param status_code Status code.
209
210         If the @a status_name or @a status_text parameter is NULL, the corresponding member
211         is left unchanged.
212
213         Used for a RESULT or STATUS message.
214 */
215 void osrf_message_set_status_info( osrfMessage* msg,
216                 const char* status_name, const char* status_text, int status_code ) {
217         if(!msg) return;
218
219         if( status_name != NULL ) {
220                 if( msg->status_name )
221                         free( msg->status_name );
222                 msg->status_name = strdup( status_name );
223         }
224
225         if( status_text != NULL ) {
226                 if( msg->status_text )
227                         free( msg->status_text );
228                 msg->status_text = strdup( status_text );
229         }
230
231         msg->status_code = status_code;
232 }
233
234
235 /**
236         @brief Populate the _result_content membersof an osrfMessage from a JSON string.
237         @param msg Pointer to the osrfMessage to be populated.
238         @param json_string A JSON string encoding a result.
239
240         Used for a RESULT message to return the results of a remote procedure call.
241 */
242 void osrf_message_set_result_content( osrfMessage* msg, const char* json_string ) {
243         if( msg == NULL || json_string == NULL) return;
244         if( msg->_result_content )
245                 jsonObjectFree( msg->_result_content );
246
247         msg->_result_content = jsonParse(json_string);
248 }
249
250
251 /**
252         @brief Populate the _result_content membersof an osrfMessage from a JSON object.
253         @param msg Pointer to the osrfMessage to be populated.
254         @param obj Pointer to a jsonObject encoding a result.
255
256         Used for a RESULT message to return the results of a remote procedure call.
257 */
258 void osrf_message_set_result( osrfMessage* msg, const jsonObject* obj ) {
259         if( msg == NULL || obj == NULL) return;
260         if( msg->_result_content )
261                 jsonObjectFree( msg->_result_content );
262
263         msg->_result_content = jsonObjectDecodeClass( obj );
264 }
265
266
267 /**
268         @brief Free an osrfMessage and everything it owns.
269         @param msg Pointer to the osrfMessage to be freed.
270 */
271 void osrfMessageFree( osrfMessage* msg ) {
272         if( msg == NULL )
273                 return;
274
275         if( msg->status_name != NULL )
276                 free(msg->status_name);
277
278         if( msg->status_text != NULL )
279                 free(msg->status_text);
280
281         if( msg->_result_content != NULL )
282                 jsonObjectFree( msg->_result_content );
283
284         if( msg->method_name != NULL )
285                 free(msg->method_name);
286
287         if( msg->sender_locale != NULL )
288                 free(msg->sender_locale);
289
290         if( msg->_params != NULL )
291                 jsonObjectFree(msg->_params);
292
293         free(msg);
294 }
295
296
297 /**
298         @brief Turn a collection of osrfMessages into one big JSON string.
299         @param msgs Pointer to an array of osrfMessages.
300         @param count Maximum number of messages to serialize.
301         @return Pointer to the JSON string.
302
303         Traverse the array, adding each osrfMessage in turn to a JSON_ARRAY.  Stop when you have added
304         the maximum number of messages, or when you encounter a NULL pointer in the array.  Then
305         translate the JSON_ARRAY into a JSON string.
306
307         The calling code is responsible for freeing the returned string.
308 */
309 char* osrfMessageSerializeBatch( osrfMessage* msgs [], int count ) {
310         if( !msgs ) return NULL;
311
312         jsonObject* wrapper = jsonNewObjectType(JSON_ARRAY);
313
314         int i = 0;
315         while( (i < count) && msgs[i] ) {
316                 jsonObjectPush(wrapper, osrfMessageToJSON( msgs[i] ));
317                 ++i;
318         }
319
320         char* j = jsonObjectToJSON(wrapper);
321         jsonObjectFree(wrapper);
322
323         return j;
324 }
325
326
327 /**
328         @brief Turn a single osrfMessage into a JSON string.
329         @param msg Pointer to the osrfMessage to be serialized.
330         @return Pointer to the resulting JSON string.
331
332         Translate the osrfMessage into JSON, wrapped in a JSON array.
333
334         This function is equivalent to osrfMessageSerializeBatch() for an array of one pointer.
335
336         The calling code is responsible for freeing the returned string.
337 */
338 char* osrf_message_serialize(const osrfMessage* msg) {
339
340         if( msg == NULL ) return NULL;
341         char* j = NULL;
342
343         jsonObject* json = osrfMessageToJSON( msg );
344
345         if(json) {
346                 jsonObject* wrapper = jsonNewObjectType(JSON_ARRAY);
347                 jsonObjectPush(wrapper, json);
348                 j = jsonObjectToJSON(wrapper);
349                 jsonObjectFree(wrapper);
350         }
351
352         return j;
353 }
354
355
356 /**
357         @brief Translate an osrfMessage into a jsonObject.
358         @param msg Pointer to the osrfMessage to be translated.
359         @return Pointer to a newly created jsonObject.
360
361         The resulting jsonObject is a JSON_HASH with a classname of "osrfMessage", and the following keys:
362         - "threadTrace"
363         - "locale"
364         - "type"
365         - "payload" (only for STATUS, REQUEST, and RESULT messages)
366
367         The data for "payload" is also a JSON_HASH, whose structure depends on the message type:
368
369         For a STATUS message, the payload's classname is msg->status_name.  The keys are "status"
370         (carrying msg->status_text) and "statusCode" (carrying the status code as a string).
371
372         For a REQUEST message, the payload's classname is "osrfMethod".  The keys are "method"
373         (carrying msg->method_name) and "params" (carrying a jsonObject to pass any parameters
374         to the method call).
375
376         For a RESULT message, the payload's classname is "osrfResult".  The keys are "status"
377         (carrying msg->status_text), "statusCode" (carrying the status code as a string), and
378         "content" (carrying a jsonObject to return any results from the method call).
379
380         The calling code is responsible for freeing the returned jsonObject.
381 */
382 jsonObject* osrfMessageToJSON( const osrfMessage* msg ) {
383
384         jsonObject* json = jsonNewObjectType(JSON_HASH);
385         jsonObjectSetClass(json, "osrfMessage");
386         jsonObject* payload;
387         char sc[64];
388         osrf_clearbuf(sc, sizeof(sc));
389
390         INT_TO_STRING(msg->thread_trace);
391         jsonObjectSetKey(json, "threadTrace", jsonNewObject(INTSTR));
392
393         if (msg->sender_locale != NULL) {
394                 jsonObjectSetKey(json, "locale", jsonNewObject(msg->sender_locale));
395         } else if (current_locale != NULL) {
396                 jsonObjectSetKey(json, "locale", jsonNewObject(current_locale));
397         } else {
398                 jsonObjectSetKey(json, "locale", jsonNewObject(default_locale));
399         }
400
401         switch(msg->m_type) {
402
403                 case CONNECT:
404                         jsonObjectSetKey(json, "type", jsonNewObject("CONNECT"));
405                         break;
406
407                 case DISCONNECT:
408                         jsonObjectSetKey(json, "type", jsonNewObject("DISCONNECT"));
409                         break;
410
411                 case STATUS:
412                         jsonObjectSetKey(json, "type", jsonNewObject("STATUS"));
413                         payload = jsonNewObject(NULL);
414                         jsonObjectSetClass(payload, msg->status_name);
415                         jsonObjectSetKey(payload, "status", jsonNewObject(msg->status_text));
416                         snprintf(sc, sizeof(sc), "%d", msg->status_code);
417                         jsonObjectSetKey(payload, "statusCode", jsonNewObject(sc));
418                         jsonObjectSetKey(json, "payload", payload);
419                         break;
420
421                 case REQUEST:
422                         jsonObjectSetKey(json, "type", jsonNewObject("REQUEST"));
423                         payload = jsonNewObject(NULL);
424                         jsonObjectSetClass(payload, "osrfMethod");
425                         jsonObjectSetKey(payload, "method", jsonNewObject(msg->method_name));
426                         jsonObjectSetKey( payload, "params", jsonObjectDecodeClass( msg->_params ) );
427                         jsonObjectSetKey(json, "payload", payload);
428
429                         break;
430
431                 case RESULT:
432                         jsonObjectSetKey(json, "type", jsonNewObject("RESULT"));
433                         payload = jsonNewObject(NULL);
434                         jsonObjectSetClass(payload,"osrfResult");
435                         jsonObjectSetKey(payload, "status", jsonNewObject(msg->status_text));
436                         snprintf(sc, sizeof(sc), "%d", msg->status_code);
437                         jsonObjectSetKey(payload, "statusCode", jsonNewObject(sc));
438                         jsonObjectSetKey(payload, "content", jsonObjectDecodeClass( msg->_result_content ));
439                         jsonObjectSetKey(json, "payload", payload);
440                         break;
441         }
442
443         return json;
444 }
445
446 /**
447         @brief Translate a JSON array into an osrfList of osrfMessages.
448         @param string The JSON string to be translated.
449         @param list Pointer to an osrfList of osrfMessages (may be NULL)
450         @return Pointer to an osrfList containing pointers to osrfMessages.
451
452         The JSON string is expected to be a JSON array, with each element encoding an osrfMessage.
453
454         Translate each element of the JSON array into an osrfMessage, and store a pointer to the
455         osrfMessage in an osrfList.
456
457         If the @a list parameter is NULL, create a new osrfList (with osrfMessageFree() as the
458         callback function for freeing items), populate it, and return a pointer to it.  Otherwise
459         clear the osrfList provided and reuse it.
460
461         When calling osrfMessageDeserialize repeatedly, a reasonable strategy is to pass a NULL
462         for the @a list parameter on the first call, and pass the value returned from the first
463         call on subsequent calls.
464
465         The calling code is responsible for eventually freeing the returned osrfList by calling
466         osrfListFree().
467  */
468 osrfList* osrfMessageDeserialize( const char* string, osrfList* list ) {
469
470         if( list )
471                 osrfListClear( list );
472
473         if( ! string  || ! *string ) {
474                 if( ! list ) {
475                         list = osrfNewList( 1 );
476                         list->freeItem = (void(*)(void*)) osrfMessageFree;
477                 }
478                 return list;                   // No string?  Return empty list.
479         }
480         
481         // Parse the JSON
482         jsonObject* json = jsonParse(string);
483         if(!json) {
484                 osrfLogWarning( OSRF_LOG_MARK,
485                                 "osrfMessageDeserialize() unable to parse data: \n%s\n", string);
486                 if( ! list ) {
487                         list = osrfNewList( 1 );
488                         list->freeItem = (void(*)(void*)) osrfMessageFree;
489                 }
490                 return list;                   // Bad JSON?  Return empty list.
491         }
492
493         const unsigned int count = (int) json->size;
494         if( ! list ) {
495                 // Create a right-sized osrfList
496                 list = osrfNewList( count );
497                 list->freeItem = (void(*)(void*)) osrfMessageFree;
498         }
499
500         // Traverse the JSON_ARRAY, turning each element into an osrfMessage
501         int i;
502         for( i = 0; i < count; ++i ) {
503
504                 const jsonObject* message = jsonObjectGetIndex( json, i );
505                 if( message && message->type != JSON_NULL &&
506                                   message->classname && !strcmp(message->classname, "osrfMessage" )) {
507                         osrfListPush( list, deserialize_one_message( message ) );
508                 }
509         }
510
511         jsonObjectFree( json );
512         return list;
513 }
514
515 /**
516         @brief Translate a JSON array into an array of osrfMessages.
517         @param string The JSON string to be translated.
518         @param msgs Pointer to an array of pointers to osrfMessage, to receive the results.
519         @param count How many slots are available in the @a msgs array.
520         @return The number of osrfMessages created.
521
522         The JSON string is expected to be a JSON array, with each element encoding an osrfMessage.
523
524         If there are too many messages in the JSON array to fit into the pointer array, we
525         silently ignore the excess.
526 */
527 int osrf_message_deserialize(const char* string, osrfMessage* msgs[], int count) {
528
529         if(!string || !msgs || count <= 0) return 0;
530         int numparsed = 0;
531
532         // Parse the JSON
533         jsonObject* json = jsonParse(string);
534
535         if(!json) {
536                 osrfLogWarning( OSRF_LOG_MARK,
537                         "osrf_message_deserialize() unable to parse data: \n%s\n", string);
538                 return 0;
539         }
540
541         // Traverse the JSON_ARRAY, turning each element into an osrfMessage
542         int x;
543         for( x = 0; x < json->size && x < count; x++ ) {
544
545                 const jsonObject* message = jsonObjectGetIndex( json, x );
546
547                 if( message && message->type != JSON_NULL &&
548                         message->classname && !strcmp(message->classname, "osrfMessage" )) {
549                         msgs[numparsed++] = deserialize_one_message( message );
550                 }
551         }
552
553         jsonObjectFree( json );
554         return numparsed;
555 }
556
557
558 /**
559         @brief Translate a jsonObject into a single osrfMessage.
560         @param obj Pointer to the jsonObject to be translated.
561         @return Pointer to a newly created osrfMessage.
562
563         It is assumed that @a obj is non-NULL and points to a valid representation of a message.
564         For a description of the expected structure of this representations, see osrfMessageToJSON().
565
566         The calling code is responsible for freeing the osrfMessage by calling osrfMessageFree().
567 */
568 static osrfMessage* deserialize_one_message( const jsonObject* obj ) {
569
570         // Get the message type.  If it isn't present, default to CONNECT.
571         const jsonObject* tmp = jsonObjectGetKeyConst( obj, "type" );
572
573         enum M_TYPE type = CONNECT;
574         const char* t = jsonObjectGetString( tmp );
575         if( t ) {
576
577                 if(      !strcmp( t, "CONNECT"    ))   type = CONNECT;
578                 else if( !strcmp( t, "DISCONNECT" ))   type = DISCONNECT;
579                 else if( !strcmp( t, "STATUS"     ))   type = STATUS;
580                 else if( !strcmp( t, "REQUEST"    ))   type = REQUEST;
581                 else if( !strcmp( t, "RESULT"     ))   type = RESULT;
582         }
583
584         // Get the thread trace, defaulting to zero.
585         int trace = 0;
586         tmp = jsonObjectGetKeyConst( obj, "threadTrace" );
587         if( tmp ) {
588                 const char* tt = jsonObjectGetString( tmp );
589                 if( tt ) {
590                         trace = atoi( tt );
591                 }
592         }
593
594         // Get the protocol, defaulting to zero.
595         int protocol = 0;
596         tmp = jsonObjectGetKeyConst( obj, "protocol" );
597         if(tmp) {
598                 const char* proto = jsonObjectGetString(tmp);
599                 if( proto ) {
600                         protocol = atoi( proto );
601                 }
602         }
603
604         // Now that we have the essentials, create an osrfMessage
605         osrfMessage* msg = osrf_message_init( type, trace, protocol );
606
607         // Update current_locale with the locale of the message
608         // (or set it to NULL if not specified)
609         tmp = jsonObjectGetKeyConst( obj, "locale" );
610         if(tmp && ( msg->sender_locale = jsonObjectToSimpleString(tmp))) {
611                 if ( current_locale ) {
612                         if( strcmp( current_locale, msg->sender_locale ) ) {
613                                 free( current_locale );
614                                 current_locale = strdup( msg->sender_locale );
615                         } // else they're the same already, so don't replace one with the other
616                 } else
617                         current_locale = strdup( msg->sender_locale );
618         } else {
619                 if ( current_locale ) {
620                         free( current_locale );
621                         current_locale = NULL;
622                 }
623         }
624
625         tmp = jsonObjectGetKeyConst( obj, "payload" );
626         if(tmp) {
627                 // Get method name and parameters for a REQUEST
628                 const jsonObject* tmp0 = jsonObjectGetKeyConst(tmp,"method");
629                 const char* tmp_str = jsonObjectGetString(tmp0);
630                 if(tmp_str)
631                         msg->method_name = strdup(tmp_str);
632
633                 tmp0 = jsonObjectGetKeyConst(tmp,"params");
634                 if(tmp0) {
635                         // Note that we use jsonObjectDecodeClass() instead of
636                         // jsonObjectClone().  The classnames are already decoded,
637                         // but jsonObjectDecodeClass removes the decoded classnames.
638                         msg->_params = jsonObjectDecodeClass( tmp0 );
639                         if(msg->_params && msg->_params->type == JSON_NULL)
640                                 msg->_params->type = JSON_ARRAY;
641                 }
642
643                 // Get status fields for a RESULT or STATUS
644                 if(tmp->classname)
645                         msg->status_name = strdup(tmp->classname);
646
647                 tmp0 = jsonObjectGetKeyConst(tmp,"status");
648                 tmp_str = jsonObjectGetString(tmp0);
649                 if(tmp_str)
650                         msg->status_text = strdup(tmp_str);
651
652                 tmp0 = jsonObjectGetKeyConst(tmp,"statusCode");
653                 if(tmp0) {
654                         tmp_str = jsonObjectGetString(tmp0);
655                         if(tmp_str)
656                                 msg->status_code = atoi(tmp_str);
657                         if(tmp0->type == JSON_NUMBER)
658                                 msg->status_code = (int) jsonObjectGetNumber(tmp0);
659                 }
660
661                 // Get the content for a RESULT
662                 tmp0 = jsonObjectGetKeyConst(tmp,"content");
663                 if(tmp0) {
664                         // Note that we use jsonObjectDecodeClass() instead of
665                         // jsonObjectClone().  The classnames are already decoded,
666                         // but jsonObjectDecodeClass removes the decoded classnames.
667                         msg->_result_content = jsonObjectDecodeClass( tmp0 );
668                 }
669
670         }
671
672         return msg;
673 }
674
675
676 /**
677         @brief Return a pointer to the result content of an osrfMessage.
678         @param msg Pointer to the osrfMessage whose result content is to be returned.
679         @return Pointer to the result content (or NULL if there is no such content, or if @a msg is
680         NULL).
681
682         The returned pointer points into the innards of the osrfMessage.  The calling code should
683         @em not call jsonObjectFree() on it, because the osrfMessage still owns it.
684 */
685 const jsonObject* osrfMessageGetResult( osrfMessage* msg ) {
686         if(msg) return msg->_result_content;
687         return NULL;
688 }