364c875b5aa3f2ae9253409bcf5a2b61830500c1
[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         switch(msg->m_type) {
429
430                 case CONNECT:
431                         jsonObjectSetKey(json, "type", jsonNewObject("CONNECT"));
432                         break;
433
434                 case DISCONNECT:
435                         jsonObjectSetKey(json, "type", jsonNewObject("DISCONNECT"));
436                         break;
437
438                 case STATUS:
439                         jsonObjectSetKey(json, "type", jsonNewObject("STATUS"));
440                         payload = jsonNewObject(NULL);
441                         jsonObjectSetClass(payload, msg->status_name);
442                         jsonObjectSetKey(payload, "status", jsonNewObject(msg->status_text));
443                         snprintf(sc, sizeof(sc), "%d", msg->status_code);
444                         jsonObjectSetKey(payload, "statusCode", jsonNewObject(sc));
445                         jsonObjectSetKey(json, "payload", payload);
446                         break;
447
448                 case REQUEST:
449                         jsonObjectSetKey(json, "type", jsonNewObject("REQUEST"));
450                         payload = jsonNewObject(NULL);
451                         jsonObjectSetClass(payload, "osrfMethod");
452                         jsonObjectSetKey(payload, "method", jsonNewObject(msg->method_name));
453                         jsonObjectSetKey( payload, "params", jsonObjectDecodeClass( msg->_params ) );
454                         jsonObjectSetKey(json, "payload", payload);
455
456                         break;
457
458                 case RESULT:
459                         jsonObjectSetKey(json, "type", jsonNewObject("RESULT"));
460                         payload = jsonNewObject(NULL);
461                         jsonObjectSetClass(payload,"osrfResult");
462                         jsonObjectSetKey(payload, "status", jsonNewObject(msg->status_text));
463                         snprintf(sc, sizeof(sc), "%d", msg->status_code);
464                         jsonObjectSetKey(payload, "statusCode", jsonNewObject(sc));
465                         jsonObjectSetKey(payload, "content", jsonObjectDecodeClass( msg->_result_content ));
466                         jsonObjectSetKey(json, "payload", payload);
467                         break;
468         }
469
470         return json;
471 }
472
473 /**
474         @brief Translate a JSON array into an osrfList of osrfMessages.
475         @param string The JSON string to be translated.
476         @param list Pointer to an osrfList of osrfMessages (may be NULL)
477         @return Pointer to an osrfList containing pointers to osrfMessages.
478
479         The JSON string is expected to be a JSON array, with each element encoding an osrfMessage.
480
481         Translate each element of the JSON array into an osrfMessage, and store a pointer to the
482         osrfMessage in an osrfList.
483
484         If the @a list parameter is NULL, create a new osrfList (with osrfMessageFree() as the
485         callback function for freeing items), populate it, and return a pointer to it.  Otherwise
486         clear the osrfList provided and reuse it.
487
488         When calling osrfMessageDeserialize repeatedly, a reasonable strategy is to pass a NULL
489         for the @a list parameter on the first call, and pass the value returned from the first
490         call on subsequent calls.
491
492         The calling code is responsible for eventually freeing the returned osrfList by calling
493         osrfListFree().
494  */
495 osrfList* osrfMessageDeserialize( const char* string, osrfList* list ) {
496
497         if( list )
498                 osrfListClear( list );
499
500         if( ! string  || ! *string ) {
501                 if( ! list ) {
502                         list = osrfNewList( 1 );
503                         list->freeItem = (void(*)(void*)) osrfMessageFree;
504                 }
505                 return list;                   // No string?  Return empty list.
506         }
507         
508         // Parse the JSON
509         jsonObject* json = jsonParse(string);
510         if(!json) {
511                 osrfLogWarning( OSRF_LOG_MARK,
512                                 "osrfMessageDeserialize() unable to parse data: \n%s\n", string);
513                 if( ! list ) {
514                         list = osrfNewList( 1 );
515                         list->freeItem = (void(*)(void*)) osrfMessageFree;
516                 }
517                 return list;                   // Bad JSON?  Return empty list.
518         }
519
520         const unsigned int count = (int) json->size;
521         if( ! list ) {
522                 // Create a right-sized osrfList
523                 list = osrfNewList( count );
524                 list->freeItem = (void(*)(void*)) osrfMessageFree;
525         }
526
527         // Traverse the JSON_ARRAY, turning each element into an osrfMessage
528         int i;
529         for( i = 0; i < count; ++i ) {
530
531                 const jsonObject* message = jsonObjectGetIndex( json, i );
532                 if( message && message->type != JSON_NULL &&
533                                   message->classname && !strcmp(message->classname, "osrfMessage" )) {
534                         osrfListPush( list, deserialize_one_message( message ) );
535                 }
536         }
537
538         jsonObjectFree( json );
539         return list;
540 }
541
542 /**
543         @brief Translate a JSON array into an array of osrfMessages.
544         @param string The JSON string to be translated.
545         @param msgs Pointer to an array of pointers to osrfMessage, to receive the results.
546         @param count How many slots are available in the @a msgs array.
547         @return The number of osrfMessages created.
548
549         The JSON string is expected to be a JSON array, with each element encoding an osrfMessage.
550
551         If there are too many messages in the JSON array to fit into the pointer array, we
552         silently ignore the excess.
553 */
554 int osrf_message_deserialize(const char* string, osrfMessage* msgs[], int count) {
555
556         if(!string || !msgs || count <= 0) return 0;
557         int numparsed = 0;
558
559         // Parse the JSON
560         jsonObject* json = jsonParse(string);
561
562         if(!json) {
563                 osrfLogWarning( OSRF_LOG_MARK,
564                         "osrf_message_deserialize() unable to parse data: \n%s\n", string);
565                 return 0;
566         }
567
568         // Traverse the JSON_ARRAY, turning each element into an osrfMessage
569         int x;
570         for( x = 0; x < json->size && x < count; x++ ) {
571
572                 const jsonObject* message = jsonObjectGetIndex( json, x );
573
574                 if( message && message->type != JSON_NULL &&
575                         message->classname && !strcmp(message->classname, "osrfMessage" )) {
576                         msgs[numparsed++] = deserialize_one_message( message );
577                 }
578         }
579
580         jsonObjectFree( json );
581         return numparsed;
582 }
583
584
585 /**
586         @brief Translate a jsonObject into a single osrfMessage.
587         @param obj Pointer to the jsonObject to be translated.
588         @return Pointer to a newly created osrfMessage.
589
590         It is assumed that @a obj is non-NULL and points to a valid representation of a message.
591         For a description of the expected structure of this representations, see osrfMessageToJSON().
592
593         The calling code is responsible for freeing the osrfMessage by calling osrfMessageFree().
594 */
595 static osrfMessage* deserialize_one_message( const jsonObject* obj ) {
596
597         // Get the message type.  If it isn't present, default to CONNECT.
598         const jsonObject* tmp = jsonObjectGetKeyConst( obj, "type" );
599
600         enum M_TYPE type = CONNECT;
601         const char* t = jsonObjectGetString( tmp );
602         if( t ) {
603
604                 if(      !strcmp( t, "CONNECT"    ))   type = CONNECT;
605                 else if( !strcmp( t, "DISCONNECT" ))   type = DISCONNECT;
606                 else if( !strcmp( t, "STATUS"     ))   type = STATUS;
607                 else if( !strcmp( t, "REQUEST"    ))   type = REQUEST;
608                 else if( !strcmp( t, "RESULT"     ))   type = RESULT;
609         }
610
611         // Get the thread trace, defaulting to zero.
612         int trace = 0;
613         tmp = jsonObjectGetKeyConst( obj, "threadTrace" );
614         if( tmp ) {
615                 const char* tt = jsonObjectGetString( tmp );
616                 if( tt ) {
617                         trace = atoi( tt );
618                 }
619         }
620
621         // Get the protocol, defaulting to zero.
622         int protocol = 0;
623         tmp = jsonObjectGetKeyConst( obj, "protocol" );
624         if(tmp) {
625                 const char* proto = jsonObjectGetString(tmp);
626                 if( proto ) {
627                         protocol = atoi( proto );
628                 }
629         }
630
631         // Now that we have the essentials, create an osrfMessage
632         osrfMessage* msg = osrf_message_init( type, trace, protocol );
633
634         // Update current_locale with the locale of the message
635         // (or set it to NULL if not specified)
636         tmp = jsonObjectGetKeyConst( obj, "locale" );
637         if(tmp && ( msg->sender_locale = jsonObjectToSimpleString(tmp))) {
638                 if ( current_locale ) {
639                         if( strcmp( current_locale, msg->sender_locale ) ) {
640                                 free( current_locale );
641                                 current_locale = strdup( msg->sender_locale );
642                         } // else they're the same already, so don't replace one with the other
643                 } else
644                         current_locale = strdup( msg->sender_locale );
645         } else {
646                 if ( current_locale ) {
647                         free( current_locale );
648                         current_locale = NULL;
649                 }
650         }
651
652         tmp = jsonObjectGetKeyConst(obj, "ingress");
653         if (tmp) {
654                 osrfMessageSetIngress(msg, jsonObjectGetString(tmp));
655         }
656
657         tmp = jsonObjectGetKeyConst( obj, "payload" );
658         if(tmp) {
659                 // Get method name and parameters for a REQUEST
660                 const jsonObject* tmp0 = jsonObjectGetKeyConst(tmp,"method");
661                 const char* tmp_str = jsonObjectGetString(tmp0);
662                 if(tmp_str)
663                         msg->method_name = strdup(tmp_str);
664
665                 tmp0 = jsonObjectGetKeyConst(tmp,"params");
666                 if(tmp0) {
667                         // Note that we use jsonObjectDecodeClass() instead of
668                         // jsonObjectClone().  The classnames are already decoded,
669                         // but jsonObjectDecodeClass removes the decoded classnames.
670                         msg->_params = jsonObjectDecodeClass( tmp0 );
671                         if(msg->_params && msg->_params->type == JSON_NULL)
672                                 msg->_params->type = JSON_ARRAY;
673                 }
674
675                 // Get status fields for a RESULT or STATUS
676                 if(tmp->classname)
677                         msg->status_name = strdup(tmp->classname);
678
679                 tmp0 = jsonObjectGetKeyConst(tmp,"status");
680                 tmp_str = jsonObjectGetString(tmp0);
681                 if(tmp_str)
682                         msg->status_text = strdup(tmp_str);
683
684                 tmp0 = jsonObjectGetKeyConst(tmp,"statusCode");
685                 if(tmp0) {
686                         tmp_str = jsonObjectGetString(tmp0);
687                         if(tmp_str)
688                                 msg->status_code = atoi(tmp_str);
689                         if(tmp0->type == JSON_NUMBER)
690                                 msg->status_code = (int) jsonObjectGetNumber(tmp0);
691                 }
692
693                 // Get the content for a RESULT
694                 tmp0 = jsonObjectGetKeyConst(tmp,"content");
695                 if(tmp0) {
696                         // Note that we use jsonObjectDecodeClass() instead of
697                         // jsonObjectClone().  The classnames are already decoded,
698                         // but jsonObjectDecodeClass removes the decoded classnames.
699                         msg->_result_content = jsonObjectDecodeClass( tmp0 );
700                 }
701
702         }
703
704         return msg;
705 }
706
707
708 /**
709         @brief Return a pointer to the result content of an osrfMessage.
710         @param msg Pointer to the osrfMessage whose result content is to be returned.
711         @return Pointer to the result content (or NULL if there is no such content, or if @a msg is
712         NULL).
713
714         The returned pointer points into the innards of the osrfMessage.  The calling code should
715         @em not call jsonObjectFree() on it, because the osrfMessage still owns it.
716 */
717 const jsonObject* osrfMessageGetResult( osrfMessage* msg ) {
718         if(msg) return msg->_result_content;
719         return NULL;
720 }