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 = xmlGetProp( root, BAD_CAST "router_from" );
124 xmlChar* router_to = xmlGetProp( root, BAD_CAST "router_to" );
125 xmlChar* router_class = xmlGetProp( root, BAD_CAST "router_class" );
126 xmlChar* broadcast = xmlGetProp( root, BAD_CAST "broadcast" );
127 xmlChar* osrf_xid = xmlGetProp( root, BAD_CAST "osrf_xid" );
130 message_set_osrf_xid( new_msg, (char*) osrf_xid);
135 new_msg->sender = strdup((const char*)router_from);
138 new_msg->sender = strdup((const char*)sender);
144 new_msg->recipient = strdup((const char*)recipient);
149 new_msg->subject = strdup((const char*)subject);
154 new_msg->thread = strdup((const char*)thread);
159 new_msg->router_from = strdup((const char*)router_from);
160 xmlFree(router_from);
164 new_msg->router_to = strdup((const char*)router_to);
169 new_msg->router_class = strdup((const char*)router_class);
170 xmlFree(router_class);
174 if(strcmp((const char*) broadcast,"0") )
175 new_msg->broadcast = 1;
179 /* Within the message element, find the child nodes for "thread", "subject", and */
180 /* "body". Extract their textual content into the corresponding members. */
181 xmlNodePtr search_node = root->children;
182 while( search_node != NULL ) {
184 if( ! strcmp( (const char*) search_node->name, "thread" ) ) {
185 if( search_node->children && search_node->children->content )
186 new_msg->thread = strdup( (const char*) search_node->children->content );
189 if( ! strcmp( (const char*) search_node->name, "subject" ) ) {
190 if( search_node->children && search_node->children->content )
191 new_msg->subject = strdup( (const char*) search_node->children->content );
194 if( ! strcmp( (const char*) search_node->name, "body" ) ) {
195 if( search_node->children && search_node->children->content )
196 new_msg->body = strdup((const char*) search_node->children->content );
199 search_node = search_node->next;
202 if( new_msg->thread == NULL )
203 new_msg->thread = strdup("");
204 if( new_msg->subject == NULL )
205 new_msg->subject = strdup("");
206 if( new_msg->body == NULL )
207 new_msg->body = strdup("");
209 /* Convert the XML document back into a string, and store it. */
210 new_msg->msg_xml = xmlDocToString(msg_doc, 0);
218 @brief Populate the osrf_xid (an OSRF extension) of a transport_message.
219 @param msg Pointer to the transport_message.
220 @param osrf_xid Value of the "osrf_xid" attribute of a message stanza.
222 If @a osrf_xid is NULL, populate with an empty string.
224 See also message_set_router_info().
226 void message_set_osrf_xid( transport_message* msg, const char* osrf_xid ) {
228 if( msg->osrf_xid ) free( msg->osrf_xid );
229 msg->osrf_xid = strdup( osrf_xid ? osrf_xid : "" );
234 @brief Populate some OSRF extensions to XMPP in a transport_message.
235 @param msg Pointer to the transport_message to be populated.
236 @param router_from Value of "router_from" attribute in message stanza
237 @param router_to Value of "router_to" attribute in message stanza
238 @param router_class Value of "router_class" attribute in message stanza
239 @param router_command Value of "router_command" attribute in message stanza
240 @param broadcast_enabled Value of "broadcast" attribute in message stanza
242 If any of the pointer pararmeters is NULL (other than @a msg), populate the
243 corresponding member with an empty string.
245 See also osrf_set_xid().
247 void message_set_router_info( transport_message* msg, const char* router_from,
248 const char* router_to, const char* router_class, const char* router_command,
249 int broadcast_enabled ) {
253 /* free old values, if any */
254 if( msg->router_from ) free( msg->router_from );
255 if( msg->router_to ) free( msg->router_to );
256 if( msg->router_class ) free( msg->router_class );
257 if( msg->router_command ) free( msg->router_command );
259 /* install new values */
260 msg->router_from = strdup( router_from ? router_from : "" );
261 msg->router_to = strdup( router_to ? router_to : "" );
262 msg->router_class = strdup( router_class ? router_class : "" );
263 msg->router_command = strdup( router_command ? router_command : "" );
264 msg->broadcast = broadcast_enabled;
266 if( msg->router_from == NULL || msg->router_to == NULL ||
267 msg->router_class == NULL || msg->router_command == NULL )
268 osrfLogError(OSRF_LOG_MARK, "message_set_router_info(): Out of Memory" );
274 @brief Free a transport_message and all the memory it owns.
275 @param msg Pointer to the transport_message to be destroyed.
276 @return 1 if successful, or 0 upon error. The only error condition is if @a msg is NULL.
278 int message_free( transport_message* msg ){
279 if( msg == NULL ) { return 0; }
284 free(msg->recipient);
286 free(msg->router_from);
287 free(msg->router_to);
288 free(msg->router_class);
289 free(msg->router_command);
291 if( msg->error_type != NULL ) free(msg->error_type);
292 if( msg->msg_xml != NULL ) free(msg->msg_xml);
299 @brief Build a <message> element and store it as a string in the msg_xml member.
300 @param msg Pointer to a transport_message.
301 @return 1 if successful, or 0 if not. The only error condition is if @a msg is NULL.
303 If msg_xml is already populated, keep it, and return immediately.
305 The contents of the <message> element come from various members of the
306 transport_message. Store the resulting string as the msg_xml member.
308 To build the XML we first build a DOM structure, and then export it to a string. That
309 way we can let the XML library worry about replacing certain characters with
310 character entity references -- not a trivial task when UTF-8 characters may be present.
312 int message_prepare_xml( transport_message* msg ) {
315 if( msg->msg_xml ) return 1; /* already done */
317 xmlNodePtr message_node;
318 xmlNodePtr body_node;
319 xmlNodePtr thread_node;
320 xmlNodePtr subject_node;
321 xmlNodePtr error_node;
325 xmlKeepBlanksDefault(0);
327 doc = xmlReadDoc( BAD_CAST "<message/>", NULL, NULL, XML_PARSE_NSCLEAN );
328 message_node = xmlDocGetRootElement(doc);
330 if( msg->is_error ) {
331 error_node = xmlNewChild(message_node, NULL, BAD_CAST "error" , NULL );
332 xmlAddChild( message_node, error_node );
333 xmlNewProp( error_node, BAD_CAST "type", BAD_CAST msg->error_type );
335 osrf_clearbuf( code_buf, sizeof(code_buf));
336 sprintf(code_buf, "%d", msg->error_code );
337 xmlNewProp( error_node, BAD_CAST "code", BAD_CAST code_buf );
340 /* set from and to */
341 xmlNewProp( message_node, BAD_CAST "to", BAD_CAST msg->recipient );
342 xmlNewProp( message_node, BAD_CAST "from", BAD_CAST msg->sender );
343 xmlNewProp( message_node, BAD_CAST "router_from", BAD_CAST msg->router_from );
344 xmlNewProp( message_node, BAD_CAST "router_to", BAD_CAST msg->router_to );
345 xmlNewProp( message_node, BAD_CAST "router_class", BAD_CAST msg->router_class );
346 xmlNewProp( message_node, BAD_CAST "router_command", BAD_CAST msg->router_command );
347 xmlNewProp( message_node, BAD_CAST "osrf_xid", BAD_CAST msg->osrf_xid );
350 xmlNewProp( message_node, BAD_CAST "broadcast", BAD_CAST "1" );
352 /* Now add nodes where appropriate */
353 char* body = msg->body;
354 char* subject = msg->subject;
355 char* thread = msg->thread;
357 if( thread && *thread ) {
358 thread_node = xmlNewChild(message_node, NULL, (xmlChar*) "thread", NULL );
359 xmlNodePtr txt = xmlNewText((xmlChar*) thread);
360 xmlAddChild(thread_node, txt);
361 xmlAddChild(message_node, thread_node);
364 if( subject && *subject ) {
365 subject_node = xmlNewChild(message_node, NULL, (xmlChar*) "subject", NULL );
366 xmlNodePtr txt = xmlNewText((xmlChar*) subject);
367 xmlAddChild(subject_node, txt);
368 xmlAddChild( message_node, subject_node );
371 if( body && *body ) {
372 body_node = xmlNewChild(message_node, NULL, (xmlChar*) "body", NULL);
373 xmlNodePtr txt = xmlNewText((xmlChar*) body);
374 xmlAddChild(body_node, txt);
375 xmlAddChild( message_node, body_node );
378 // Export the xmlDoc to a string
379 xmlBufferPtr xmlbuf = xmlBufferCreate();
380 xmlNodeDump( xmlbuf, doc, xmlDocGetRootElement(doc), 0, 0);
381 msg->msg_xml = strdup((const char*) (xmlBufferContent(xmlbuf)));
383 xmlBufferFree(xmlbuf);
392 @brief Extract the username from a Jabber ID.
393 @param jid Pointer to the Jabber ID.
394 @param buf Pointer to a receiving buffer supplied by the caller.
395 @param size Maximum number of characters to copy, not including the terminal nul.
397 A jabber ID is of the form "username@domain/resource", where the resource is optional.
398 Here we copy the username portion into the supplied buffer, plus a terminal nul. If there
399 is no "@" character, leave the buffer as an empty string.
401 void jid_get_username( const char* jid, char buf[], int size ) {
403 if( jid == NULL || buf == NULL || size <= 0 ) { return; }
407 /* find the @ and return whatever is in front of it */
408 int len = strlen( jid );
410 for( i = 0; i != len; i++ ) {
411 if( jid[i] == '@' ) {
412 if(i > size) i = size;
413 memcpy( buf, jid, i );
422 @brief Extract the resource from a Jabber ID.
423 @param jid Pointer to the Jabber ID.
424 @param buf Pointer to a receiving buffer supplied by the caller.
425 @param size Maximum number of characters to copy, not including the terminal nul.
427 A jabber ID is of the form "username@domain/resource", where the resource is optional.
428 Here we copy the resource portion, if present into the supplied buffer, plus a terminal nul.
429 If there is no resource, leave the buffer as an empty string.
431 void jid_get_resource( const char* jid, char buf[], int size) {
432 if( jid == NULL || buf == NULL || size <= 0 ) { return; }
434 // Find the last slash, if any
435 const char* start = strrchr( jid, '/' );
438 // Copy the text beyond the slash, up to a maximum size
439 size_t len = strlen( ++start );
440 if( len > size ) len = size;
441 memcpy( buf, start, len );
449 @brief Extract the domain from a Jabber ID.
450 @param jid Pointer to the Jabber ID.
451 @param buf Pointer to a receiving buffer supplied by the caller.
452 @param size Maximum number of characters to copy, not including the terminal nul.
454 A jabber ID is of the form "username@domain/resource", where the resource is optional.
455 Here we copy the domain portion into the supplied buffer, plus a terminal nul. If the
456 Jabber ID is ill-formed, the results may be ill-formed or empty.
458 void jid_get_domain( const char* jid, char buf[], int size ) {
460 if(jid == NULL) return;
462 int len = strlen(jid);
467 for( i = 0; i!= len; i++ ) {
470 else if(jid[i] == '/' && index1 != 0)
474 if( index1 > 0 && index2 > 0 && index2 > index1 ) {
475 int dlen = index2 - index1;
476 if(dlen > size) dlen = size;
477 memcpy( buf, jid + index1, dlen );
485 @brief Turn a transport_message into an error message.
486 @param msg Pointer to the transport_message.
487 @param type Pointer to a short string denoting the type of error.
488 @param err_code The error code.
490 The @a type and @a err_code parameters correspond to the "type" and "code" attributes of
491 a Jabber error element.
493 void set_msg_error( transport_message* msg, const char* type, int err_code ) {
497 if( type != NULL && *type ) {
498 msg->error_type = safe_malloc( strlen(type)+1);
499 strcpy( msg->error_type, type );
500 msg->error_code = err_code;