1 #include <opensrf/transport_message.h>
4 @file transport_message.c
5 @brief Collection of routines for managing transport_messages.
7 These routines are largely concerned with the conversion of XML to transport_messages,
12 @brief Allocate and initialize a new transport_message to be send via Jabber.
13 @param body Content of the message.
14 @param subject Subject of the message.
15 @param thread Thread of the message.
16 @param recipient The address of the recipient.
17 @param sender The address of the sender.
18 @return A pointer to a newly-allocated transport_message.
20 This function doesn't populate everything. Typically there are subsequent calls to
21 some combination of message_set_router_info(), message_set_osrf_xid(), and
22 set_msg_error() to populate various additional bits and pieces. Before sending the
23 message anywhere, we call message_prepare_xml() to translate the message into XML,
24 specifically a message stanza for a Jabber XML stream.
26 The calling code is responsible for freeing the transport_message by calling message_free().
28 transport_message* message_init( const char* body, const char* subject,
29 const char* thread, const char* recipient, const char* sender ) {
31 transport_message* msg = safe_malloc( sizeof(transport_message) );
33 if( body == NULL ) { body = ""; }
34 if( thread == NULL ) { thread = ""; }
35 if( subject == NULL ) { subject = ""; }
36 if( sender == NULL ) { sender = ""; }
37 if( recipient == NULL ) { recipient = ""; }
39 msg->body = strdup(body);
40 msg->thread = strdup(thread);
41 msg->subject = strdup(subject);
42 msg->recipient = strdup(recipient);
43 msg->sender = strdup(sender);
45 if( msg->body == NULL || msg->thread == NULL ||
46 msg->subject == NULL || msg->recipient == NULL ||
47 msg->sender == NULL ) {
49 osrfLogError(OSRF_LOG_MARK, "message_init(): Out of Memory" );
53 free( msg->recipient );
59 msg->router_from = NULL;
60 msg->router_to = NULL;
61 msg->router_class = NULL;
62 msg->router_command = NULL;
65 msg->error_type = NULL;
76 @brief Translate an XML string into a transport_message.
77 @param msg_xml Pointer to a <message> element as passed by Jabber.
78 @return Pointer to a newly created transport_message.
80 Do @em not populate the following members:
86 The calling code is responsible for freeing the transport_message by calling message_free().
88 transport_message* new_message_from_xml( const char* msg_xml ) {
90 if( msg_xml == NULL || *msg_xml == '\0' )
93 transport_message* new_msg = safe_malloc( sizeof(transport_message) );
96 new_msg->subject = NULL;
97 new_msg->thread = NULL;
98 new_msg->recipient = NULL;
99 new_msg->sender = NULL;
100 new_msg->router_from = NULL;
101 new_msg->router_to = NULL;
102 new_msg->router_class = NULL;
103 new_msg->router_command = NULL;
104 new_msg->osrf_xid = NULL;
105 new_msg->is_error = 0;
106 new_msg->error_type = NULL;
107 new_msg->error_code = 0;
108 new_msg->broadcast = 0;
109 new_msg->msg_xml = NULL;
110 new_msg->next = NULL;
112 /* Parse the XML document and grab the root */
113 xmlKeepBlanksDefault(0);
114 xmlDocPtr msg_doc = xmlReadDoc( BAD_CAST msg_xml, NULL, NULL, 0 );
115 xmlNodePtr root = xmlDocGetRootElement(msg_doc);
117 /* Get various attributes of the root, */
118 /* and use them to populate the corresponding members */
119 xmlChar* sender = xmlGetProp( root, BAD_CAST "from");
120 xmlChar* recipient = xmlGetProp( root, BAD_CAST "to");
121 xmlChar* subject = xmlGetProp( root, BAD_CAST "subject");
122 xmlChar* thread = xmlGetProp( root, BAD_CAST "thread" );
123 xmlChar* router_from = NULL;
124 xmlChar* router_to = NULL;
125 xmlChar* router_class = NULL;
126 xmlChar* router_command = NULL;
127 xmlChar* broadcast = NULL;
128 xmlChar* osrf_xid = NULL;
131 new_msg->sender = strdup((const char*)sender);
136 new_msg->recipient = strdup((const char*)recipient);
141 new_msg->subject = strdup((const char*)subject);
146 new_msg->thread = strdup((const char*)thread);
150 /* Within the message element, find the child nodes for "thread", "subject" */
151 /* "body", and "opensrf". Extract their textual content into the corresponding members. */
152 xmlNodePtr search_node = root->children;
153 while( search_node != NULL ) {
155 if( ! strcmp( (const char*) search_node->name, "thread" ) ) {
156 if( search_node->children && search_node->children->content )
157 new_msg->thread = strdup( (const char*) search_node->children->content );
160 if( ! strcmp( (const char*) search_node->name, "subject" ) ) {
161 if( search_node->children && search_node->children->content )
162 new_msg->subject = strdup( (const char*) search_node->children->content );
165 if( ! strcmp( (const char*) search_node->name, "opensrf" ) ) {
166 router_from = xmlGetProp( search_node, BAD_CAST "router_from" );
167 router_to = xmlGetProp( search_node, BAD_CAST "router_to" );
168 router_class = xmlGetProp( search_node, BAD_CAST "router_class" );
169 router_command = xmlGetProp( search_node, BAD_CAST "router_command" );
170 broadcast = xmlGetProp( search_node, BAD_CAST "broadcast" );
171 osrf_xid = xmlGetProp( search_node, BAD_CAST "osrf_xid" );
174 message_set_osrf_xid( new_msg, (char*) osrf_xid);
179 if (new_msg->sender) {
180 // Sender value applied above. Clear it and
181 // use the router value instead.
182 free(new_msg->sender);
185 new_msg->sender = strdup((const char*)router_from);
186 new_msg->router_from = strdup((const char*)router_from);
187 xmlFree(router_from);
191 new_msg->router_to = strdup((const char*)router_to);
196 new_msg->router_class = strdup((const char*)router_class);
197 xmlFree(router_class);
201 new_msg->router_command = strdup((const char*)router_command);
202 xmlFree(router_command);
206 if(strcmp((const char*) broadcast,"0") )
207 new_msg->broadcast = 1;
212 if( ! strcmp( (const char*) search_node->name, "body" ) ) {
213 if( search_node->children && search_node->children->content )
214 new_msg->body = strdup((const char*) search_node->children->content );
217 search_node = search_node->next;
220 if( new_msg->thread == NULL )
221 new_msg->thread = strdup("");
222 if( new_msg->subject == NULL )
223 new_msg->subject = strdup("");
224 if( new_msg->body == NULL )
225 new_msg->body = strdup("");
227 /* Convert the XML document back into a string, and store it. */
228 new_msg->msg_xml = xmlDocToString(msg_doc, 0);
236 @brief Populate the osrf_xid (an OSRF extension) of a transport_message.
237 @param msg Pointer to the transport_message.
238 @param osrf_xid Value of the "osrf_xid" attribute of a message stanza.
240 If @a osrf_xid is NULL, populate with an empty string.
242 See also message_set_router_info().
244 void message_set_osrf_xid( transport_message* msg, const char* osrf_xid ) {
246 if( msg->osrf_xid ) free( msg->osrf_xid );
247 msg->osrf_xid = strdup( osrf_xid ? osrf_xid : "" );
252 @brief Populate some OSRF extensions to XMPP in a transport_message.
253 @param msg Pointer to the transport_message to be populated.
254 @param router_from Value of "router_from" attribute in message stanza
255 @param router_to Value of "router_to" attribute in message stanza
256 @param router_class Value of "router_class" attribute in message stanza
257 @param router_command Value of "router_command" attribute in message stanza
258 @param broadcast_enabled Value of "broadcast" attribute in message stanza
260 If any of the pointer pararmeters is NULL (other than @a msg), populate the
261 corresponding member with an empty string.
263 See also osrf_set_xid().
265 void message_set_router_info( transport_message* msg, const char* router_from,
266 const char* router_to, const char* router_class, const char* router_command,
267 int broadcast_enabled ) {
271 /* free old values, if any */
272 if( msg->router_from ) free( msg->router_from );
273 if( msg->router_to ) free( msg->router_to );
274 if( msg->router_class ) free( msg->router_class );
275 if( msg->router_command ) free( msg->router_command );
277 /* install new values */
278 msg->router_from = strdup( router_from ? router_from : "" );
279 msg->router_to = strdup( router_to ? router_to : "" );
280 msg->router_class = strdup( router_class ? router_class : "" );
281 msg->router_command = strdup( router_command ? router_command : "" );
282 msg->broadcast = broadcast_enabled;
284 if( msg->router_from == NULL || msg->router_to == NULL ||
285 msg->router_class == NULL || msg->router_command == NULL )
286 osrfLogError(OSRF_LOG_MARK, "message_set_router_info(): Out of Memory" );
292 @brief Free a transport_message and all the memory it owns.
293 @param msg Pointer to the transport_message to be destroyed.
294 @return 1 if successful, or 0 upon error. The only error condition is if @a msg is NULL.
296 int message_free( transport_message* msg ){
297 if( msg == NULL ) { return 0; }
302 free(msg->recipient);
304 free(msg->router_from);
305 free(msg->router_to);
306 free(msg->router_class);
307 free(msg->router_command);
309 if( msg->error_type != NULL ) free(msg->error_type);
310 if( msg->msg_xml != NULL ) free(msg->msg_xml);
317 @brief Build a <message> element and store it as a string in the msg_xml member.
318 @param msg Pointer to a transport_message.
319 @return 1 if successful, or 0 if not. The only error condition is if @a msg is NULL.
321 If msg_xml is already populated, keep it, and return immediately.
323 The contents of the <message> element come from various members of the
324 transport_message. Store the resulting string as the msg_xml member.
326 To build the XML we first build a DOM structure, and then export it to a string. That
327 way we can let the XML library worry about replacing certain characters with
328 character entity references -- not a trivial task when UTF-8 characters may be present.
330 int message_prepare_xml( transport_message* msg ) {
333 if( msg->msg_xml ) return 1; /* already done */
335 xmlNodePtr message_node;
336 xmlNodePtr body_node;
337 xmlNodePtr thread_node;
338 xmlNodePtr opensrf_node;
339 xmlNodePtr subject_node;
340 xmlNodePtr error_node;
344 xmlKeepBlanksDefault(0);
346 doc = xmlReadDoc( BAD_CAST "<message/>", NULL, NULL, XML_PARSE_NSCLEAN );
347 message_node = xmlDocGetRootElement(doc);
349 if( msg->is_error ) {
350 error_node = xmlNewChild(message_node, NULL, BAD_CAST "error" , NULL );
351 xmlAddChild( message_node, error_node );
352 xmlNewProp( error_node, BAD_CAST "type", BAD_CAST msg->error_type );
354 osrf_clearbuf( code_buf, sizeof(code_buf));
355 sprintf(code_buf, "%d", msg->error_code );
356 xmlNewProp( error_node, BAD_CAST "code", BAD_CAST code_buf );
359 /* set from and to */
360 xmlNewProp( message_node, BAD_CAST "to", BAD_CAST msg->recipient );
361 xmlNewProp( message_node, BAD_CAST "from", BAD_CAST msg->sender );
363 /* set from and to on a new node, also */
364 opensrf_node = xmlNewChild(message_node, NULL, (xmlChar*) "opensrf", NULL );
365 xmlNewProp( opensrf_node, BAD_CAST "router_from", BAD_CAST msg->router_from );
366 xmlNewProp( opensrf_node, BAD_CAST "router_to", BAD_CAST msg->router_to );
367 xmlNewProp( opensrf_node, BAD_CAST "router_class", BAD_CAST msg->router_class );
368 xmlNewProp( opensrf_node, BAD_CAST "router_command", BAD_CAST msg->router_command );
369 xmlNewProp( opensrf_node, BAD_CAST "osrf_xid", BAD_CAST msg->osrf_xid );
371 xmlAddChild(message_node, opensrf_node);
373 if( msg->broadcast ) {
374 xmlNewProp( opensrf_node, BAD_CAST "broadcast", BAD_CAST "1" );
377 /* Now add nodes where appropriate */
378 char* body = msg->body;
379 char* subject = msg->subject;
380 char* thread = msg->thread;
382 if( thread && *thread ) {
383 thread_node = xmlNewChild(message_node, NULL, (xmlChar*) "thread", NULL );
384 xmlNodePtr txt = xmlNewText((xmlChar*) thread);
385 xmlAddChild(thread_node, txt);
386 xmlAddChild(message_node, thread_node);
389 if( subject && *subject ) {
390 subject_node = xmlNewChild(message_node, NULL, (xmlChar*) "subject", NULL );
391 xmlNodePtr txt = xmlNewText((xmlChar*) subject);
392 xmlAddChild(subject_node, txt);
393 xmlAddChild( message_node, subject_node );
396 if( body && *body ) {
397 body_node = xmlNewChild(message_node, NULL, (xmlChar*) "body", NULL);
398 xmlNodePtr txt = xmlNewText((xmlChar*) body);
399 xmlAddChild(body_node, txt);
400 xmlAddChild( message_node, body_node );
403 // Export the xmlDoc to a string
404 xmlBufferPtr xmlbuf = xmlBufferCreate();
405 xmlNodeDump( xmlbuf, doc, xmlDocGetRootElement(doc), 0, 0);
406 msg->msg_xml = strdup((const char*) (xmlBufferContent(xmlbuf)));
408 xmlBufferFree(xmlbuf);
417 @brief Extract the username from a Jabber ID.
418 @param jid Pointer to the Jabber ID.
419 @param buf Pointer to a receiving buffer supplied by the caller.
420 @param size Maximum number of characters to copy, not including the terminal nul.
422 A jabber ID is of the form "username@domain/resource", where the resource is optional.
423 Here we copy the username portion into the supplied buffer, plus a terminal nul. If there
424 is no "@" character, leave the buffer as an empty string.
426 void jid_get_username( const char* jid, char buf[], int size ) {
428 if( jid == NULL || buf == NULL || size <= 0 ) { return; }
432 /* find the @ and return whatever is in front of it */
433 int len = strlen( jid );
435 for( i = 0; i != len; i++ ) {
436 if( jid[i] == '@' ) {
437 if(i > size) i = size;
438 memcpy( buf, jid, i );
447 @brief Extract the resource from a Jabber ID.
448 @param jid Pointer to the Jabber ID.
449 @param buf Pointer to a receiving buffer supplied by the caller.
450 @param size Maximum number of characters to copy, not including the terminal nul.
452 A jabber ID is of the form "username@domain/resource", where the resource is optional.
453 Here we copy the resource portion, if present into the supplied buffer, plus a terminal nul.
454 If there is no resource, leave the buffer as an empty string.
456 void jid_get_resource( const char* jid, char buf[], int size) {
457 if( jid == NULL || buf == NULL || size <= 0 ) { return; }
459 // Find the last slash, if any
460 const char* start = strrchr( jid, '/' );
463 // Copy the text beyond the slash, up to a maximum size
464 size_t len = strlen( ++start );
465 if( len > size ) len = size;
466 memcpy( buf, start, len );
474 @brief Extract the domain from a Jabber ID.
475 @param jid Pointer to the Jabber ID.
476 @param buf Pointer to a receiving buffer supplied by the caller.
477 @param size Maximum number of characters to copy, not including the terminal nul.
479 A jabber ID is of the form "username@domain/resource", where the resource is optional.
480 Here we copy the domain portion into the supplied buffer, plus a terminal nul. If the
481 Jabber ID is ill-formed, the results may be ill-formed or empty.
483 void jid_get_domain( const char* jid, char buf[], int size ) {
485 if(jid == NULL) return;
487 int len = strlen(jid);
492 for( i = 0; i!= len; i++ ) {
495 else if(jid[i] == '/' && index1 != 0)
499 if( index1 > 0 && index2 > 0 && index2 > index1 ) {
500 int dlen = index2 - index1;
501 if(dlen > size) dlen = size;
502 memcpy( buf, jid + index1, dlen );
510 @brief Turn a transport_message into an error message.
511 @param msg Pointer to the transport_message.
512 @param type Pointer to a short string denoting the type of error.
513 @param err_code The error code.
515 The @a type and @a err_code parameters correspond to the "type" and "code" attributes of
516 a Jabber error element.
518 void set_msg_error( transport_message* msg, const char* type, int err_code ) {
522 if( type != NULL && *type ) {
523 if( msg->error_type )
524 free( msg->error_type );
525 msg->error_type = safe_malloc( strlen(type)+1 );
526 strcpy( msg->error_type, type );
527 msg->error_code = err_code;