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