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