1 #include <opensrf/transport_session.h>
4 @file transport_session.c
5 @brief Routines to manage a connection to a Jabber server.
7 In all cases, a transport_session acts as a client with regard to Jabber.
10 #define CONNECTING_1 1 /**< just starting the connection to Jabber */
11 #define CONNECTING_2 2 /**< XML stream opened but not yet logged in */
14 /* Note. these are growing buffers, so all that's necessary is a sane starting point */
15 #define JABBER_BODY_BUFSIZE 4096 /**< buffer size for message body */
16 #define JABBER_SUBJECT_BUFSIZE 64 /**< buffer size for message subject */
17 #define JABBER_THREAD_BUFSIZE 64 /**< buffer size for message thread */
18 #define JABBER_JID_BUFSIZE 64 /**< buffer size for various ids */
19 #define JABBER_STATUS_BUFSIZE 16 /**< buffer size for status code */
21 // ---------------------------------------------------------------------------------
22 // Callback for handling the startElement event. Much of the jabber logic occurs
23 // in this and the characterHandler callbacks.
24 // Here we check for the various top level jabber elements: body, iq, etc.
25 // ---------------------------------------------------------------------------------
26 static void startElementHandler(
27 void *session, const xmlChar *name, const xmlChar **atts);
29 // ---------------------------------------------------------------------------------
30 // Callback for handling the endElement event. Updates the Jabber state machine
31 // to let us know the element is over.
32 // ---------------------------------------------------------------------------------
33 static void endElementHandler( void *session, const xmlChar *name);
35 // ---------------------------------------------------------------------------------
36 // This is where we extract XML text content. In particular, this is useful for
37 // extracting Jabber message bodies.
38 // ---------------------------------------------------------------------------------
39 static void characterHandler(
40 void *session, const xmlChar *ch, int len);
42 static void parseWarningHandler( void *session, const char* msg, ... );
43 static void parseErrorHandler( void *session, const char* msg, ... );
45 // ---------------------------------------------------------------------------------
46 // Tells the SAX parser which functions will be used as event callbacks
47 // ---------------------------------------------------------------------------------
48 static xmlSAXHandler SAXHandlerStruct = {
49 NULL, /* internalSubset */
50 NULL, /* isStandalone */
51 NULL, /* hasInternalSubset */
52 NULL, /* hasExternalSubset */
53 NULL, /* resolveEntity */
55 NULL, /* entityDecl */
56 NULL, /* notationDecl */
57 NULL, /* attributeDecl */
58 NULL, /* elementDecl */
59 NULL, /* unparsedEntityDecl */
60 NULL, /* setDocumentLocator */
61 NULL, /* startDocument */
62 NULL, /* endDocument */
63 startElementHandler, /* startElement */
64 endElementHandler, /* endElement */
66 characterHandler, /* characters */
67 NULL, /* ignorableWhitespace */
68 NULL, /* processingInstruction */
70 parseWarningHandler, /* xmlParserWarning */
71 parseErrorHandler, /* xmlParserError */
72 NULL, /* xmlParserFatalError : unused */
73 NULL, /* getParameterEntity */
74 NULL, /* cdataBlock; */
75 NULL, /* externalSubset; */
78 NULL, /* startElementNs */
79 NULL, /* endElementNs */
80 NULL /* xmlStructuredErrorFunc */
83 // ---------------------------------------------------------------------------------
84 // Our SAX handler pointer.
85 // ---------------------------------------------------------------------------------
86 static const xmlSAXHandlerPtr SAXHandler = &SAXHandlerStruct;
89 #define HOST_NAME_MAX 256
92 static void grab_incoming(void* blob, socket_manager* mgr, int sockid, char* data, int parent);
93 static void reset_session_buffers( transport_session* session );
94 static const char* get_xml_attr( const xmlChar** atts, const char* attr_name );
97 @brief Allocate and initialize a transport_session.
98 @param server Hostname or IP address where the Jabber server resides.
99 @param port Port used for connecting to Jabber (0 if using UNIX domain socket).
100 @param unix_path Name of Jabber's socket in file system (if using UNIX domain socket).
101 @param user_data An opaque pointer stored on behalf of the calling code.
102 @param component Boolean; true if we're a component.
103 @return Pointer to a newly allocated transport_session.
105 This function initializes memory but does not open any sockets or otherwise access
108 If @a port is greater than zero, we will use TCP to connect to Jabber, and ignore
109 @a unix_path. Otherwise we will open a UNIX domain socket using @a unix_path.
111 The calling code is responsible for freeing the transport_session by calling
114 transport_session* init_transport( const char* server,
115 int port, const char* unix_path, void* user_data, int component ) {
120 /* create the session struct */
121 transport_session* session =
122 (transport_session*) safe_malloc( sizeof(transport_session) );
124 session->user_data = user_data;
126 session->component = component;
128 /* initialize the data buffers */
129 session->body_buffer = buffer_init( JABBER_BODY_BUFSIZE );
130 session->subject_buffer = buffer_init( JABBER_SUBJECT_BUFSIZE );
131 session->thread_buffer = buffer_init( JABBER_THREAD_BUFSIZE );
132 session->from_buffer = buffer_init( JABBER_JID_BUFSIZE );
133 session->status_buffer = buffer_init( JABBER_STATUS_BUFSIZE );
134 session->recipient_buffer = buffer_init( JABBER_JID_BUFSIZE );
135 session->message_error_type = buffer_init( JABBER_JID_BUFSIZE );
136 session->session_id = buffer_init( 64 );
138 session->message_error_code = 0;
140 /* for OpenSRF extensions */
141 session->router_to_buffer = buffer_init( JABBER_JID_BUFSIZE );
142 session->router_from_buffer = buffer_init( JABBER_JID_BUFSIZE );
143 session->osrf_xid_buffer = buffer_init( JABBER_JID_BUFSIZE );
144 session->router_class_buffer = buffer_init( JABBER_JID_BUFSIZE );
145 session->router_command_buffer = buffer_init( JABBER_JID_BUFSIZE );
147 session->router_broadcast = 0;
149 /* initialize the jabber state machine */
150 session->state_machine = (jabber_machine*) safe_malloc( sizeof(jabber_machine) );
151 session->state_machine->connected = 0;
152 session->state_machine->connecting = 0;
153 session->state_machine->in_message = 0;
154 session->state_machine->in_message_body = 0;
155 session->state_machine->in_thread = 0;
156 session->state_machine->in_subject = 0;
157 session->state_machine->in_error = 0;
158 session->state_machine->in_message_error = 0;
159 session->state_machine->in_iq = 0;
160 session->state_machine->in_presence = 0;
161 session->state_machine->in_status = 0;
163 /* initialize the sax push parser */
164 session->parser_ctxt = xmlCreatePushParserCtxt(SAXHandler, session, "", 0, NULL);
166 /* initialize the socket_manager structure */
167 session->sock_mgr = (socket_manager*) safe_malloc( sizeof(socket_manager) );
169 session->sock_mgr->data_received = &grab_incoming;
170 session->sock_mgr->on_socket_closed = NULL;
171 session->sock_mgr->socket = NULL;
172 session->sock_mgr->blob = session;
174 session->port = port;
175 session->server = strdup(server);
177 session->unix_path = strdup(unix_path);
178 else session->unix_path = NULL;
180 session->sock_id = 0;
181 session->message_callback = NULL;
187 @brief Disconnect from Jabber, destroy a transport_session, and close its socket.
188 @param session Pointer to the transport_session to be destroyed.
189 @return 1 if successful, or 0 if not.
191 The only error condition is a NULL pointer argument.
193 int session_free( transport_session* session ) {
197 session_disconnect( session );
198 return session_discard( session );
203 @brief Destroy a transport_session and close its socket, without disconnecting from Jabber.
204 @param session Pointer to the transport_session to be destroyed.
205 @return 1 if successful, or 0 if not.
207 This function may be called from a child process in order to free resources associated
208 with the parent's transport_session, but without sending a disconnect to Jabber (since
209 that would disconnect the parent).
211 The only error condition is a NULL pointer argument.
213 int session_discard( transport_session* session ) {
217 if(session->sock_mgr)
218 socket_manager_free(session->sock_mgr);
220 if( session->state_machine ) free( session->state_machine );
221 if( session->parser_ctxt) {
222 xmlFreeDoc( session->parser_ctxt->myDoc );
223 xmlFreeParserCtxt(session->parser_ctxt);
226 xmlCleanupCharEncodingHandlers();
230 buffer_free(session->body_buffer);
231 buffer_free(session->subject_buffer);
232 buffer_free(session->thread_buffer);
233 buffer_free(session->from_buffer);
234 buffer_free(session->recipient_buffer);
235 buffer_free(session->status_buffer);
236 buffer_free(session->message_error_type);
237 buffer_free(session->router_to_buffer);
238 buffer_free(session->router_from_buffer);
239 buffer_free(session->osrf_xid_buffer);
240 buffer_free(session->router_class_buffer);
241 buffer_free(session->router_command_buffer);
242 buffer_free(session->session_id);
244 free(session->server);
245 free(session->unix_path);
252 @brief Determine whether a transport_session is connected.
253 @param session Pointer to the transport_session to be tested.
254 @return 1 if connected, or 0 if not.
256 int session_connected( transport_session* session ) {
257 return session ? session->state_machine->connected : 0;
261 @brief Wait on the client socket connected to Jabber, and process any resulting input.
262 @param session Pointer to the transport_session.
263 @param timeout How seconds to wait before timing out (see notes).
264 @return 0 if successful, or -1 if a timeout or other error occurs, or if the server
265 closes the connection at the other end.
267 If @a timeout is -1, wait indefinitely for input activity to appear. If @a timeout is
268 zero, don't wait at all. If @a timeout is positive, wait that number of seconds
269 before timing out. If @a timeout has a negative value other than -1, the results are not
272 Read all available input from the socket and pass it through grab_incoming() (a
273 callback function previously installed in the socket_manager).
275 There is no guarantee that we will get a complete message from a single call. As a
276 result, the calling code should call this function in a loop until it gets a complete
277 message, or until an error occurs.
279 int session_wait( transport_session* session, int timeout ) {
280 if( ! session || ! session->sock_mgr ) {
284 int ret = socket_wait( session->sock_mgr, timeout, session->sock_id );
287 osrfLogDebug(OSRF_LOG_MARK, "socket_wait returned error code %d", ret);
293 @brief Convert a transport_message to XML and send it to Jabber.
294 @param session Pointer to the transport_session.
295 @param msg Pointer to a transport_message enclosing the message.
296 @return 0 if successful, or -1 upon error.
298 int session_send_msg(
299 transport_session* session, transport_message* msg ) {
301 if( ! session ) { return -1; }
303 if( ! session->state_machine->connected ) {
304 osrfLogWarning(OSRF_LOG_MARK, "State machine is not connected in send_msg()");
308 message_prepare_xml( msg );
309 return socket_send( session->sock_id, msg->msg_xml );
315 @brief Connect to the Jabber server as a client and open a Jabber session.
316 @param session Pointer to a transport_session.
317 @param username Jabber user name.
318 @param password Jabber password.
319 @param resource name of Jabber resource.
320 @param connect_timeout Timeout interval, in seconds, for receiving data (see notes).
321 @param auth_type An enum: either AUTH_PLAIN or AUTH_DIGEST (see notes).
322 @return 1 if successful, or 0 upon error.
324 If @a connect_timeout is -1, wait indefinitely for the Jabber server to respond. If
325 @a connect_timeout is zero, don't wait at all. If @a timeout is positive, wait that
326 number of seconds before timing out. If @a connect_timeout has a negative value other
327 than -1, the results are not well defined.
329 The value of @a connect_timeout applies to each of two stages in the logon procedure.
330 Hence the logon may take up to twice the amount of time indicated.
332 If we connect as a Jabber component, we send the password as an SHA1 hash. Otherwise
333 we look at the @a auth_type. If it's AUTH_PLAIN, we send the password as plaintext; if
334 it's AUTH_DIGEST, we send it as a hash.
336 At this writing, we only use AUTH_DIGEST.
338 int session_connect( transport_session* session,
339 const char* username, const char* password,
340 const char* resource, int connect_timeout, enum TRANSPORT_AUTH_TYPE auth_type ) {
344 osrfLogWarning(OSRF_LOG_MARK, "session is null in session_connect()" );
348 if( session->sock_id != 0 ) {
349 osrfLogWarning(OSRF_LOG_MARK, "transport session is already open, on socket %d",
354 // Open a client socket connecting to the Jabber server
355 if(session->port > 0) { // use TCP
356 session->sock_id = socket_open_tcp_client(
357 session->sock_mgr, session->port, session->server );
358 if( session->sock_id <= 0 ) {
359 session->sock_id = 0;
362 } else if(session->unix_path != NULL) { // use UNIX domain
363 session->sock_id = socket_open_unix_client( session->sock_mgr, session->unix_path );
364 if( session->sock_id <= 0 ) {
365 session->sock_id = 0;
370 osrfLogWarning( OSRF_LOG_MARK, "Can't open session: no port or unix path" );
374 const char* server = session->server;
379 We establish the session in two stages.
381 First we establish an XMPP stream with the Jabber server by sending an opening tag of
382 stream:stream. This is not a complete XML document. We don't send the corresponding
383 closing tag until we close the session.
385 If the Jabber server responds by sending an opening stream:stream tag of its own, we can
386 proceed to the second stage by sending a second stanza to log in. This stanza is an XML
387 element with the tag <handshake> (if we're a Jabber component) or <iq> (if we're not),
388 enclosing the username, password, and resource.
390 If all goes well, the Jabber server responds with a <handshake> or <iq> stanza of its own,
393 If authentication fails, the Jabber server returns a <stream:error> (if we used a <handshake>
394 or an <iq> of type "error" (if we used an <iq>).
396 if( session->component ) {
398 /* the first Jabber connect stanza */
399 char our_hostname[HOST_NAME_MAX + 1] = "";
400 gethostname(our_hostname, sizeof(our_hostname) );
401 our_hostname[HOST_NAME_MAX] = '\0';
402 size1 = 150 + strlen( username ) + strlen( our_hostname );
403 char stanza1[ size1 ];
404 snprintf( stanza1, sizeof(stanza1),
405 "<stream:stream version='1.0' xmlns:stream='http://etherx.jabber.org/streams' "
406 "xmlns='jabber:component:accept' to='%s' from='%s' xml:lang='en'>",
407 username, our_hostname );
409 /* send the first stanze */
410 session->state_machine->connecting = CONNECTING_1;
412 if( socket_send( session->sock_id, stanza1 ) ) {
413 osrfLogWarning(OSRF_LOG_MARK, "error sending");
414 socket_disconnect( session->sock_mgr, session->sock_id );
415 session->sock_id = 0;
420 socket_wait(session->sock_mgr, connect_timeout, session->sock_id);
422 /* server acknowledges our existence, now see if we can login */
423 if( session->state_machine->connecting == CONNECTING_2 ) {
425 int ss = buffer_length( session->session_id ) + strlen( password ) + 5;
427 snprintf( hashstuff, sizeof(hashstuff), "%s%s",
428 OSRF_BUFFER_C_STR( session->session_id ), password );
430 char* hash = shahash( hashstuff );
431 size2 = 100 + strlen( hash );
432 char stanza2[ size2 ];
433 snprintf( stanza2, sizeof(stanza2), "<handshake>%s</handshake>", hash );
435 if( socket_send( session->sock_id, stanza2 ) ) {
436 osrfLogWarning(OSRF_LOG_MARK, "error sending");
437 socket_disconnect( session->sock_mgr, session->sock_id );
438 session->sock_id = 0;
443 } else { /* we're not a component */
445 /* the first Jabber connect stanza */
446 size1 = 100 + strlen( server );
447 char stanza1[ size1 ];
448 snprintf( stanza1, sizeof(stanza1),
449 "<stream:stream to='%s' xmlns='jabber:client' "
450 "xmlns:stream='http://etherx.jabber.org/streams'>",
453 /* send the first stanze */
454 session->state_machine->connecting = CONNECTING_1;
455 if( socket_send( session->sock_id, stanza1 ) ) {
456 osrfLogWarning(OSRF_LOG_MARK, "error sending");
457 socket_disconnect( session->sock_mgr, session->sock_id );
458 session->sock_id = 0;
464 socket_wait( session->sock_mgr, connect_timeout, session->sock_id ); /* make the timeout smarter XXX */
466 if( auth_type == AUTH_PLAIN ) {
468 /* the second jabber connect stanza including login info*/
469 size2 = 150 + strlen( username ) + strlen( password ) + strlen( resource );
470 char stanza2[ size2 ];
471 snprintf( stanza2, sizeof(stanza2),
472 "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
473 "<username>%s</username><password>%s</password><resource>%s</resource></query></iq>",
474 username, password, resource );
476 /* server acknowledges our existence, now see if we can login */
477 if( session->state_machine->connecting == CONNECTING_2 ) {
478 if( socket_send( session->sock_id, stanza2 ) ) {
479 osrfLogWarning(OSRF_LOG_MARK, "error sending");
480 socket_disconnect( session->sock_mgr, session->sock_id );
481 session->sock_id = 0;
486 } else if( auth_type == AUTH_DIGEST ) {
488 int ss = buffer_length( session->session_id ) + strlen( password ) + 5;
490 snprintf( hashstuff, sizeof(hashstuff), "%s%s", OSRF_BUFFER_C_STR( session->session_id ), password );
492 char* hash = shahash( hashstuff );
494 /* the second jabber connect stanza including login info */
495 size2 = 150 + strlen( username ) + strlen( hash ) + strlen(resource);
496 char stanza2[ size2 ];
497 snprintf( stanza2, sizeof(stanza2),
498 "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
499 "<username>%s</username><digest>%s</digest><resource>%s</resource></query></iq>",
500 username, hash, resource );
502 /* server acknowledges our existence, now see if we can login */
503 if( session->state_machine->connecting == CONNECTING_2 ) {
504 if( socket_send( session->sock_id, stanza2 ) ) {
505 osrfLogWarning(OSRF_LOG_MARK, "error sending");
506 socket_disconnect( session->sock_mgr, session->sock_id );
507 session->sock_id = 0;
513 osrfLogWarning(OSRF_LOG_MARK, "Invalid auth_type parameter: %d",
515 socket_disconnect( session->sock_mgr, session->sock_id );
516 session->sock_id = 0;
523 /* wait for reply to login request */
524 socket_wait( session->sock_mgr, connect_timeout, session->sock_id );
526 if( session->state_machine->connected ) {
530 socket_disconnect( session->sock_mgr, session->sock_id );
531 session->sock_id = 0;
537 @brief Callback function: push a buffer of XML into an XML parser.
538 @param blob Void pointer pointing to the transport_session.
539 @param mgr Pointer to the socket_manager (not used).
540 @param sockid Socket file descriptor (not used)
541 @param data Pointer to a buffer of received data, as a nul-terminated string.
542 @param parent Not applicable.
544 The socket_manager calls this function when it reads a buffer's worth of data from
545 the Jabber socket. The XML parser calls other callback functions when it sees various
548 static void grab_incoming(void* blob, socket_manager* mgr, int sockid, char* data, int parent) {
549 transport_session* ses = (transport_session*) blob;
550 if( ! ses ) { return; }
551 xmlParseChunk(ses->parser_ctxt, data, strlen(data), 0);
556 @brief Respond to the beginning of an XML element.
557 @param session Pointer to the transport_session, cast to a void pointer.
558 @param name Name of the XML element.
559 @param atts Pointer to a ragged array containing attributes and values.
561 The XML parser calls this when it sees the beginning of an XML element. We note what
562 element it is by setting the corresponding switch in the state machine, and grab whatever
563 attributes we expect to find.
565 static void startElementHandler(
566 void *session, const xmlChar *name, const xmlChar **atts) {
568 transport_session* ses = (transport_session*) session;
569 if( ! ses ) { return; }
572 if( strcmp( (char*) name, "message" ) == 0 ) {
573 ses->state_machine->in_message = 1;
574 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
575 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
580 if( ses->state_machine->in_message ) {
582 if( strcmp( (char*) name, "opensrf" ) == 0 ) {
583 buffer_add( ses->router_from_buffer, get_xml_attr( atts, "router_from" ) );
584 buffer_add( ses->osrf_xid_buffer, get_xml_attr( atts, "osrf_xid" ) );
585 buffer_add( ses->router_to_buffer, get_xml_attr( atts, "router_to" ) );
586 buffer_add( ses->router_class_buffer, get_xml_attr( atts, "router_class" ) );
587 buffer_add( ses->router_command_buffer, get_xml_attr( atts, "router_command" ) );
588 const char* broadcast = get_xml_attr( atts, "broadcast" );
590 ses->router_broadcast = atoi( broadcast );
595 if( strcmp( (char*) name, "body" ) == 0 ) {
596 ses->state_machine->in_message_body = 1;
600 if( strcmp( (char*) name, "subject" ) == 0 ) {
601 ses->state_machine->in_subject = 1;
605 if( strcmp( (char*) name, "thread" ) == 0 ) {
606 ses->state_machine->in_thread = 1;
612 if( strcmp( (char*) name, "presence" ) == 0 ) {
613 ses->state_machine->in_presence = 1;
614 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
615 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
619 if( strcmp( (char*) name, "status" ) == 0 ) {
620 ses->state_machine->in_status = 1;
625 if( strcmp( (char*) name, "stream:error" ) == 0 ) {
626 ses->state_machine->in_error = 1;
627 ses->state_machine->connected = 0;
628 osrfLogWarning( OSRF_LOG_MARK, "Received <stream:error> message from Jabber server" );
633 /* first server response from a connect attempt */
634 if( strcmp( (char*) name, "stream:stream" ) == 0 ) {
635 if( ses->state_machine->connecting == CONNECTING_1 ) {
636 ses->state_machine->connecting = CONNECTING_2;
637 buffer_add( ses->session_id, get_xml_attr(atts, "id") );
642 if( strcmp( (char*) name, "handshake" ) == 0 ) {
643 ses->state_machine->connected = 1;
644 ses->state_machine->connecting = 0;
649 if( strcmp( (char*) name, "error" ) == 0 ) {
650 ses->state_machine->in_message_error = 1;
651 buffer_add( ses->message_error_type, get_xml_attr( atts, "type" ) );
652 ses->message_error_code = atoi( get_xml_attr( atts, "code" ) );
653 osrfLogInfo( OSRF_LOG_MARK, "Received <error> message with type %s and code %d",
654 OSRF_BUFFER_C_STR( ses->message_error_type ), ses->message_error_code );
658 if( strcmp( (char*) name, "iq" ) == 0 ) {
659 ses->state_machine->in_iq = 1;
661 const char* type = get_xml_attr(atts, "type");
663 if( strcmp( type, "result") == 0
664 && ses->state_machine->connecting == CONNECTING_2 ) {
665 ses->state_machine->connected = 1;
666 ses->state_machine->connecting = 0;
670 if( strcmp( type, "error") == 0 ) {
671 osrfLogWarning( OSRF_LOG_MARK, "Error connecting to jabber" );
678 @brief Return the value of a given XML attribute.
679 @param atts Pointer to a NULL terminated array of strings.
680 @param attr_name Name of the attribute you're looking for.
681 @return The value of the attribute if found, or NULL if not.
683 In the array to which @a atts points, the zeroth entry is an attribute name, and the
684 one after that is its value. Subsequent entries alternate between names and values.
685 The last entry is NULL to terminate the list.
687 static const char* get_xml_attr( const xmlChar** atts, const char* attr_name ) {
690 for(i = 0;(atts[i] != NULL);i++) {
691 if( strcmp( (const char*) atts[i++], attr_name ) == 0 ) {
692 if( atts[i] != NULL ) {
693 return (const char*) atts[i];
703 @brief React to the closing of an XML tag.
704 @param session Pointer to a transport_session, cast to a void pointer.
705 @param name Pointer to the name of the tag that is closing.
707 See what kind of tag is closing, and respond accordingly.
709 static void endElementHandler( void *session, const xmlChar *name) {
710 transport_session* ses = (transport_session*) session;
711 if( ! ses ) { return; }
713 // Bypass a level of indirection, since we'll examine the machine repeatedly:
714 jabber_machine* machine = ses->state_machine;
716 if( machine->in_message && strcmp( (char*) name, "message" ) == 0 ) {
718 /* pass off the message info the callback */
719 if( ses->message_callback ) {
721 transport_message* msg = message_init(
722 OSRF_BUFFER_C_STR( ses->body_buffer ),
723 OSRF_BUFFER_C_STR( ses->subject_buffer ),
724 OSRF_BUFFER_C_STR( ses->thread_buffer ),
725 OSRF_BUFFER_C_STR( ses->recipient_buffer ),
726 OSRF_BUFFER_C_STR( ses->from_buffer ) );
728 message_set_router_info( msg,
729 ses->router_from_buffer->buf,
730 ses->router_to_buffer->buf,
731 ses->router_class_buffer->buf,
732 ses->router_command_buffer->buf,
733 ses->router_broadcast );
735 message_set_osrf_xid( msg, ses->osrf_xid_buffer->buf );
737 if( ses->message_error_type->n_used > 0 ) {
738 set_msg_error( msg, ses->message_error_type->buf, ses->message_error_code );
741 if( msg == NULL ) { return; }
742 ses->message_callback( ses->user_data, msg );
745 machine->in_message = 0;
746 reset_session_buffers( session );
750 if( machine->in_message_body && strcmp( (const char*) name, "body" ) == 0 ) {
751 machine->in_message_body = 0;
755 if( machine->in_subject && strcmp( (const char*) name, "subject" ) == 0 ) {
756 machine->in_subject = 0;
760 if( machine->in_thread && strcmp( (const char*) name, "thread" ) == 0 ) {
761 machine->in_thread = 0;
765 if( machine->in_iq && strcmp( (const char*) name, "iq" ) == 0 ) {
767 if( ses->message_error_code > 0 ) {
768 if( 401 == ses->message_error_code )
769 osrfLogWarning( OSRF_LOG_MARK, "Error 401 in IQ packet: not authorized" );
771 osrfLogWarning( OSRF_LOG_MARK, "Error in IQ packet: code %d",
772 ses->message_error_code );
774 reset_session_buffers( session );
778 if( machine->in_presence && strcmp( (const char*) name, "presence" ) == 0 ) {
779 machine->in_presence = 0;
781 if( ses->presence_callback ) {
782 // call the callback with the status, etc.
785 reset_session_buffers( session );
789 if( machine->in_status && strcmp( (const char*) name, "status" ) == 0 ) {
790 machine->in_status = 0;
794 if( machine->in_message_error && strcmp( (const char*) name, "error" ) == 0 ) {
795 machine->in_message_error = 0;
799 if( machine->in_error && strcmp( (const char*) name, "stream:error" ) == 0 ) {
800 machine->in_error = 0;
806 @brief Clear all the buffers of a transport_session.
807 @param ses Pointer to the transport_session whose buffers are to be cleared.
809 static void reset_session_buffers( transport_session* ses ) {
810 OSRF_BUFFER_RESET( ses->body_buffer );
811 OSRF_BUFFER_RESET( ses->subject_buffer );
812 OSRF_BUFFER_RESET( ses->thread_buffer );
813 OSRF_BUFFER_RESET( ses->from_buffer );
814 OSRF_BUFFER_RESET( ses->recipient_buffer );
815 OSRF_BUFFER_RESET( ses->router_from_buffer );
816 OSRF_BUFFER_RESET( ses->osrf_xid_buffer );
817 OSRF_BUFFER_RESET( ses->router_to_buffer );
818 OSRF_BUFFER_RESET( ses->router_class_buffer );
819 OSRF_BUFFER_RESET( ses->router_command_buffer );
820 OSRF_BUFFER_RESET( ses->message_error_type );
821 OSRF_BUFFER_RESET( ses->session_id );
822 OSRF_BUFFER_RESET( ses->status_buffer );
825 // ------------------------------------------------------------------
826 // takes data out of the body of the message and pushes it into
827 // the appropriate buffer
828 // ------------------------------------------------------------------
830 @brief Copy XML text (outside of tags) into the appropriate buffer.
831 @param session Pointer to the transport_session.
832 @param ch Pointer to the text to be copied.
833 @param len How many characters to be copied.
835 The XML parser calls this as a callback when it finds text outside of a tag, We check
836 the state machine to figure out what kind of text it is, and then append it to the
837 corresponding buffer.
839 static void characterHandler(
840 void *session, const xmlChar *ch, int len) {
842 const char* p = (const char*) ch;
844 transport_session* ses = (transport_session*) session;
845 if( ! ses ) { return; }
847 jabber_machine* machine = ses->state_machine;
849 /* set the various message parts */
850 if( machine->in_message ) {
852 if( machine->in_message_body ) {
853 buffer_add_n( ses->body_buffer, p, len );
856 if( machine->in_subject ) {
857 buffer_add_n( ses->subject_buffer, p, len );
860 if( machine->in_thread ) {
861 buffer_add_n( ses->thread_buffer, p, len );
865 /* set the presence status */
866 if( machine->in_presence && ses->state_machine->in_status ) {
867 buffer_add_n( ses->status_buffer, p, len );
870 if( machine->in_error ) {
872 strncpy( msg, p, len );
874 osrfLogWarning( OSRF_LOG_MARK,
875 "Text of error message received from Jabber: %s", msg );
880 @brief Log a warning from the XML parser.
881 @param session Pointer to a transport_session, cast to a void pointer (not used).
882 @param msg Pointer to a printf-style format string. Subsequent messages, if any, are
883 formatted and inserted into the expanded string.
885 The XML parser calls this function when it wants to warn about something in the XML.
887 static void parseWarningHandler( void *session, const char* msg, ... ) {
888 VA_LIST_TO_STRING(msg);
889 osrfLogWarning( OSRF_LOG_MARK, VA_BUF );
893 @brief Log an error from the XML parser.
894 @param session Pointer to a transport_session, cast to a void pointer (not used).
895 @param msg Pointer to a printf-style format string. Subsequent messages, if any, are
896 formatted and inserted into the expanded string.
898 The XML parser calls this function when it finds an error in the XML.
900 static void parseErrorHandler( void *session, const char* msg, ... ){
901 VA_LIST_TO_STRING(msg);
902 osrfLogError( OSRF_LOG_MARK, VA_BUF );
906 @brief Disconnect from Jabber, and close the socket.
907 @param session Pointer to the transport_session to be disconnected.
908 @return 0 in all cases.
910 int session_disconnect( transport_session* session ) {
911 if( session && session->sock_id != 0 ) {
912 socket_send(session->sock_id, "</stream:stream>");
913 socket_disconnect(session->sock_mgr, session->sock_id);
914 session->sock_id = 0;