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