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 // Jabber state machine. This is how we know where we are in the Jabber
24 // ---------------------------------------------------------------------------------
25 struct jabber_state_machine_struct {
39 // ---------------------------------------------------------------------------------
40 // Callback for handling the startElement event. Much of the jabber logic occurs
41 // in this and the characterHandler callbacks.
42 // Here we check for the various top level jabber elements: body, iq, etc.
43 // ---------------------------------------------------------------------------------
44 static void startElementHandler(
45 void *session, const xmlChar *name, const xmlChar **atts);
47 // ---------------------------------------------------------------------------------
48 // Callback for handling the endElement event. Updates the Jabber state machine
49 // to let us know the element is over.
50 // ---------------------------------------------------------------------------------
51 static void endElementHandler( void *session, const xmlChar *name);
53 // ---------------------------------------------------------------------------------
54 // This is where we extract XML text content. In particular, this is useful for
55 // extracting Jabber message bodies.
56 // ---------------------------------------------------------------------------------
57 static void characterHandler(
58 void *session, const xmlChar *ch, int len);
60 static void parseWarningHandler( void *session, const char* msg, ... );
61 static void parseErrorHandler( void *session, const char* msg, ... );
63 // ---------------------------------------------------------------------------------
64 // Tells the SAX parser which functions will be used as event callbacks
65 // ---------------------------------------------------------------------------------
66 static xmlSAXHandler SAXHandlerStruct = {
67 NULL, /* internalSubset */
68 NULL, /* isStandalone */
69 NULL, /* hasInternalSubset */
70 NULL, /* hasExternalSubset */
71 NULL, /* resolveEntity */
73 NULL, /* entityDecl */
74 NULL, /* notationDecl */
75 NULL, /* attributeDecl */
76 NULL, /* elementDecl */
77 NULL, /* unparsedEntityDecl */
78 NULL, /* setDocumentLocator */
79 NULL, /* startDocument */
80 NULL, /* endDocument */
81 startElementHandler, /* startElement */
82 endElementHandler, /* endElement */
84 characterHandler, /* characters */
85 NULL, /* ignorableWhitespace */
86 NULL, /* processingInstruction */
88 parseWarningHandler, /* xmlParserWarning */
89 parseErrorHandler, /* xmlParserError */
90 NULL, /* xmlParserFatalError : unused */
91 NULL, /* getParameterEntity */
92 NULL, /* cdataBlock; */
93 NULL, /* externalSubset; */
96 NULL, /* startElementNs */
97 NULL, /* endElementNs */
98 NULL /* xmlStructuredErrorFunc */
101 // ---------------------------------------------------------------------------------
102 // Our SAX handler pointer.
103 // ---------------------------------------------------------------------------------
104 static const xmlSAXHandlerPtr SAXHandler = &SAXHandlerStruct;
106 #ifndef HOST_NAME_MAX
107 #define HOST_NAME_MAX 256
110 static void grab_incoming(void* blob, socket_manager* mgr, int sockid, char* data, int parent);
111 static void reset_session_buffers( transport_session* session );
112 static const char* get_xml_attr( const xmlChar** atts, const char* attr_name );
115 @brief Allocate and initialize a transport_session.
116 @param server Hostname or IP address where the Jabber server resides.
117 @param port Port used for connecting to Jabber (0 if using UNIX domain socket).
118 @param unix_path Name of Jabber's socket in file system (if using UNIX domain socket).
119 @param user_data An opaque pointer stored on behalf of the calling code.
120 @param component Boolean; true if we're a component.
121 @return Pointer to a newly allocated transport_session.
123 This function initializes memory but does not open any sockets or otherwise access
126 If @a port is greater than zero, we will use TCP to connect to Jabber, and ignore
127 @a unix_path. Otherwise we will open a UNIX domain socket using @a unix_path.
129 The calling code is responsible for freeing the transport_session by calling
132 transport_session* init_transport( const char* server,
133 int port, const char* unix_path, void* user_data, int component ) {
138 /* create the session struct */
139 transport_session* session =
140 (transport_session*) safe_malloc( sizeof(transport_session) );
142 session->user_data = user_data;
144 session->component = component;
146 /* initialize the data buffers */
147 session->body_buffer = buffer_init( JABBER_BODY_BUFSIZE );
148 session->subject_buffer = buffer_init( JABBER_SUBJECT_BUFSIZE );
149 session->thread_buffer = buffer_init( JABBER_THREAD_BUFSIZE );
150 session->from_buffer = buffer_init( JABBER_JID_BUFSIZE );
151 session->status_buffer = buffer_init( JABBER_STATUS_BUFSIZE );
152 session->recipient_buffer = buffer_init( JABBER_JID_BUFSIZE );
153 session->message_error_type = buffer_init( JABBER_JID_BUFSIZE );
154 session->session_id = buffer_init( 64 );
156 session->message_error_code = 0;
158 /* for OpenSRF extensions */
159 session->router_to_buffer = buffer_init( JABBER_JID_BUFSIZE );
160 session->router_from_buffer = buffer_init( JABBER_JID_BUFSIZE );
161 session->osrf_xid_buffer = buffer_init( JABBER_JID_BUFSIZE );
162 session->router_class_buffer = buffer_init( JABBER_JID_BUFSIZE );
163 session->router_command_buffer = buffer_init( JABBER_JID_BUFSIZE );
165 session->router_broadcast = 0;
167 /* initialize the jabber state machine */
168 session->state_machine = (jabber_machine*) safe_malloc( sizeof(jabber_machine) );
169 session->state_machine->connected = 0;
170 session->state_machine->connecting = 0;
171 session->state_machine->in_message = 0;
172 session->state_machine->in_message_body = 0;
173 session->state_machine->in_thread = 0;
174 session->state_machine->in_subject = 0;
175 session->state_machine->in_error = 0;
176 session->state_machine->in_message_error = 0;
177 session->state_machine->in_iq = 0;
178 session->state_machine->in_presence = 0;
179 session->state_machine->in_status = 0;
181 /* initialize the sax push parser */
182 session->parser_ctxt = xmlCreatePushParserCtxt(SAXHandler, session, "", 0, NULL);
184 /* initialize the socket_manager structure */
185 session->sock_mgr = (socket_manager*) safe_malloc( sizeof(socket_manager) );
187 session->sock_mgr->data_received = &grab_incoming;
188 session->sock_mgr->on_socket_closed = NULL;
189 session->sock_mgr->socket = NULL;
190 session->sock_mgr->blob = session;
192 session->port = port;
193 session->server = strdup(server);
195 session->unix_path = strdup(unix_path);
196 else session->unix_path = NULL;
198 session->sock_id = 0;
199 session->message_callback = NULL;
205 @brief Disconnect from Jabber, destroy a transport_session, and close its socket.
206 @param session Pointer to the transport_session to be destroyed.
207 @return 1 if successful, or 0 if not.
209 The only error condition is a NULL pointer argument.
211 int session_free( transport_session* session ) {
215 session_disconnect( session );
216 return session_discard( session );
221 @brief Destroy a transport_session and close its socket, without disconnecting from Jabber.
222 @param session Pointer to the transport_session to be destroyed.
223 @return 1 if successful, or 0 if not.
225 This function may be called from a child process in order to free resources associated
226 with the parent's transport_session, but without sending a disconnect to Jabber (since
227 that would disconnect the parent).
229 The only error condition is a NULL pointer argument.
231 int session_discard( transport_session* session ) {
235 if(session->sock_mgr)
236 socket_manager_free(session->sock_mgr);
238 if( session->state_machine ) free( session->state_machine );
239 if( session->parser_ctxt) {
240 xmlFreeDoc( session->parser_ctxt->myDoc );
241 xmlFreeParserCtxt(session->parser_ctxt);
244 xmlCleanupCharEncodingHandlers();
248 buffer_free(session->body_buffer);
249 buffer_free(session->subject_buffer);
250 buffer_free(session->thread_buffer);
251 buffer_free(session->from_buffer);
252 buffer_free(session->recipient_buffer);
253 buffer_free(session->status_buffer);
254 buffer_free(session->message_error_type);
255 buffer_free(session->router_to_buffer);
256 buffer_free(session->router_from_buffer);
257 buffer_free(session->osrf_xid_buffer);
258 buffer_free(session->router_class_buffer);
259 buffer_free(session->router_command_buffer);
260 buffer_free(session->session_id);
262 free(session->server);
263 free(session->unix_path);
270 @brief Determine whether a transport_session is connected.
271 @param session Pointer to the transport_session to be tested.
272 @return 1 if connected, or 0 if not.
274 int session_connected( transport_session* session ) {
275 return session ? session->state_machine->connected : 0;
279 @brief Wait on the client socket connected to Jabber, and process any resulting input.
280 @param session Pointer to the transport_session.
281 @param timeout How seconds to wait before timing out (see notes).
282 @return 0 if successful, or -1 if a timeout or other error occurs, or if the server
283 closes the connection at the other end.
285 If @a timeout is -1, wait indefinitely for input activity to appear. If @a timeout is
286 zero, don't wait at all. If @a timeout is positive, wait that number of seconds
287 before timing out. If @a timeout has a negative value other than -1, the results are not
290 Read all available input from the socket and pass it through grab_incoming() (a
291 callback function previously installed in the socket_manager).
293 There is no guarantee that we will get a complete message from a single call. As a
294 result, the calling code should call this function in a loop until it gets a complete
295 message, or until an error occurs.
297 int session_wait( transport_session* session, int timeout ) {
298 if( ! session || ! session->sock_mgr ) {
302 int ret = socket_wait( session->sock_mgr, timeout, session->sock_id );
305 osrfLogDebug(OSRF_LOG_MARK, "socket_wait returned error code %d", ret);
306 session->state_machine->connected = 0;
312 @brief Convert a transport_message to XML and send it to Jabber.
313 @param session Pointer to the transport_session.
314 @param msg Pointer to a transport_message enclosing the message.
315 @return 0 if successful, or -1 upon error.
317 int session_send_msg(
318 transport_session* session, transport_message* msg ) {
320 if( ! session ) { return -1; }
322 if( ! session->state_machine->connected ) {
323 osrfLogWarning(OSRF_LOG_MARK, "State machine is not connected in send_msg()");
327 message_prepare_xml( msg );
328 return socket_send( session->sock_id, msg->msg_xml );
334 @brief Connect to the Jabber server as a client and open a Jabber session.
335 @param session Pointer to a transport_session.
336 @param username Jabber user name.
337 @param password Jabber password.
338 @param resource name of Jabber resource.
339 @param connect_timeout Timeout interval, in seconds, for receiving data (see notes).
340 @param auth_type An enum: either AUTH_PLAIN or AUTH_DIGEST (see notes).
341 @return 1 if successful, or 0 upon error.
343 If @a connect_timeout is -1, wait indefinitely for the Jabber server to respond. If
344 @a connect_timeout is zero, don't wait at all. If @a timeout is positive, wait that
345 number of seconds before timing out. If @a connect_timeout has a negative value other
346 than -1, the results are not well defined.
348 The value of @a connect_timeout applies to each of two stages in the logon procedure.
349 Hence the logon may take up to twice the amount of time indicated.
351 If we connect as a Jabber component, we send the password as an SHA1 hash. Otherwise
352 we look at the @a auth_type. If it's AUTH_PLAIN, we send the password as plaintext; if
353 it's AUTH_DIGEST, we send it as a hash.
355 At this writing, we only use AUTH_DIGEST.
357 int session_connect( transport_session* session,
358 const char* username, const char* password,
359 const char* resource, int connect_timeout, enum TRANSPORT_AUTH_TYPE auth_type ) {
363 osrfLogWarning(OSRF_LOG_MARK, "session is null in session_connect()" );
367 if( session->sock_id != 0 ) {
368 osrfLogWarning(OSRF_LOG_MARK, "transport session is already open, on socket %d",
373 // Open a client socket connecting to the Jabber server
374 if(session->port > 0) { // use TCP
375 session->sock_id = socket_open_tcp_client(
376 session->sock_mgr, session->port, session->server );
377 if( session->sock_id <= 0 ) {
378 session->sock_id = 0;
381 } else if(session->unix_path != NULL) { // use UNIX domain
382 session->sock_id = socket_open_unix_client( session->sock_mgr, session->unix_path );
383 if( session->sock_id <= 0 ) {
384 session->sock_id = 0;
389 osrfLogWarning( OSRF_LOG_MARK, "Can't open session: no port or unix path" );
393 const char* server = session->server;
398 We establish the session in two stages.
400 First we establish an XMPP stream with the Jabber server by sending an opening tag of
401 stream:stream. This is not a complete XML document. We don't send the corresponding
402 closing tag until we close the session.
404 If the Jabber server responds by sending an opening stream:stream tag of its own, we can
405 proceed to the second stage by sending a second stanza to log in. This stanza is an XML
406 element with the tag <handshake> (if we're a Jabber component) or <iq> (if we're not),
407 enclosing the username, password, and resource.
409 If all goes well, the Jabber server responds with a <handshake> or <iq> stanza of its own,
412 If authentication fails, the Jabber server returns a <stream:error> (if we used a <handshake>
413 or an <iq> of type "error" (if we used an <iq>).
415 if( session->component ) {
417 /* the first Jabber connect stanza */
418 char our_hostname[HOST_NAME_MAX + 1] = "";
419 gethostname(our_hostname, sizeof(our_hostname) );
420 our_hostname[HOST_NAME_MAX] = '\0';
421 size1 = 150 + strlen( username ) + strlen( our_hostname );
422 char stanza1[ size1 ];
423 snprintf( stanza1, sizeof(stanza1),
424 "<stream:stream version='1.0' xmlns:stream='http://etherx.jabber.org/streams' "
425 "xmlns='jabber:component:accept' to='%s' from='%s' xml:lang='en'>",
426 username, our_hostname );
428 /* send the first stanze */
429 session->state_machine->connecting = CONNECTING_1;
431 if( socket_send( session->sock_id, stanza1 ) ) {
432 osrfLogWarning(OSRF_LOG_MARK, "error sending");
433 socket_disconnect( session->sock_mgr, session->sock_id );
434 session->sock_id = 0;
439 socket_wait(session->sock_mgr, connect_timeout, session->sock_id);
441 /* server acknowledges our existence, now see if we can login */
442 if( session->state_machine->connecting == CONNECTING_2 ) {
444 int ss = buffer_length( session->session_id ) + strlen( password ) + 5;
446 snprintf( hashstuff, sizeof(hashstuff), "%s%s",
447 OSRF_BUFFER_C_STR( session->session_id ), password );
449 char* hash = shahash( hashstuff );
450 size2 = 100 + strlen( hash );
451 char stanza2[ size2 ];
452 snprintf( stanza2, sizeof(stanza2), "<handshake>%s</handshake>", hash );
454 if( socket_send( session->sock_id, stanza2 ) ) {
455 osrfLogWarning(OSRF_LOG_MARK, "error sending");
456 socket_disconnect( session->sock_mgr, session->sock_id );
457 session->sock_id = 0;
462 } else { /* we're not a component */
464 /* the first Jabber connect stanza */
465 size1 = 100 + strlen( server );
466 char stanza1[ size1 ];
467 snprintf( stanza1, sizeof(stanza1),
468 "<stream:stream to='%s' xmlns='jabber:client' "
469 "xmlns:stream='http://etherx.jabber.org/streams'>",
472 /* send the first stanze */
473 session->state_machine->connecting = CONNECTING_1;
474 if( socket_send( session->sock_id, stanza1 ) ) {
475 osrfLogWarning(OSRF_LOG_MARK, "error sending");
476 socket_disconnect( session->sock_mgr, session->sock_id );
477 session->sock_id = 0;
483 socket_wait( session->sock_mgr, connect_timeout, session->sock_id ); /* make the timeout smarter XXX */
485 if( auth_type == AUTH_PLAIN ) {
487 /* the second jabber connect stanza including login info*/
488 size2 = 150 + strlen( username ) + strlen( password ) + strlen( resource );
489 char stanza2[ size2 ];
490 snprintf( stanza2, sizeof(stanza2),
491 "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
492 "<username>%s</username><password>%s</password><resource>%s</resource></query></iq>",
493 username, password, resource );
495 /* server acknowledges our existence, now see if we can login */
496 if( session->state_machine->connecting == CONNECTING_2 ) {
497 if( socket_send( session->sock_id, stanza2 ) ) {
498 osrfLogWarning(OSRF_LOG_MARK, "error sending");
499 socket_disconnect( session->sock_mgr, session->sock_id );
500 session->sock_id = 0;
505 } else if( auth_type == AUTH_DIGEST ) {
507 int ss = buffer_length( session->session_id ) + strlen( password ) + 5;
509 snprintf( hashstuff, sizeof(hashstuff), "%s%s", OSRF_BUFFER_C_STR( session->session_id ), password );
511 char* hash = shahash( hashstuff );
513 /* the second jabber connect stanza including login info */
514 size2 = 150 + strlen( username ) + strlen( hash ) + strlen(resource);
515 char stanza2[ size2 ];
516 snprintf( stanza2, sizeof(stanza2),
517 "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
518 "<username>%s</username><digest>%s</digest><resource>%s</resource></query></iq>",
519 username, hash, resource );
521 /* server acknowledges our existence, now see if we can login */
522 if( session->state_machine->connecting == CONNECTING_2 ) {
523 if( socket_send( session->sock_id, stanza2 ) ) {
524 osrfLogWarning(OSRF_LOG_MARK, "error sending");
525 socket_disconnect( session->sock_mgr, session->sock_id );
526 session->sock_id = 0;
532 osrfLogWarning(OSRF_LOG_MARK, "Invalid auth_type parameter: %d",
534 socket_disconnect( session->sock_mgr, session->sock_id );
535 session->sock_id = 0;
542 /* wait for reply to login request */
543 socket_wait( session->sock_mgr, connect_timeout, session->sock_id );
545 if( session->state_machine->connected ) {
549 socket_disconnect( session->sock_mgr, session->sock_id );
550 session->sock_id = 0;
556 @brief Callback function: push a buffer of XML into an XML parser.
557 @param blob Void pointer pointing to the transport_session.
558 @param mgr Pointer to the socket_manager (not used).
559 @param sockid Socket file descriptor (not used)
560 @param data Pointer to a buffer of received data, as a nul-terminated string.
561 @param parent Not applicable.
563 The socket_manager calls this function when it reads a buffer's worth of data from
564 the Jabber socket. The XML parser calls other callback functions when it sees various
567 static void grab_incoming(void* blob, socket_manager* mgr, int sockid, char* data, int parent) {
568 transport_session* ses = (transport_session*) blob;
569 if( ! ses ) { return; }
570 xmlParseChunk(ses->parser_ctxt, data, strlen(data), 0);
575 @brief Respond to the beginning of an XML element.
576 @param session Pointer to the transport_session, cast to a void pointer.
577 @param name Name of the XML element.
578 @param atts Pointer to a ragged array containing attributes and values.
580 The XML parser calls this when it sees the beginning of an XML element. We note what
581 element it is by setting the corresponding switch in the state machine, and grab whatever
582 attributes we expect to find.
584 static void startElementHandler(
585 void *session, const xmlChar *name, const xmlChar **atts) {
587 transport_session* ses = (transport_session*) session;
588 if( ! ses ) { return; }
591 if( strcmp( (char*) name, "message" ) == 0 ) {
592 ses->state_machine->in_message = 1;
593 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
594 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
595 buffer_add( ses->router_from_buffer, get_xml_attr( atts, "router_from" ) );
596 buffer_add( ses->osrf_xid_buffer, get_xml_attr( atts, "osrf_xid" ) );
597 buffer_add( ses->router_to_buffer, get_xml_attr( atts, "router_to" ) );
598 buffer_add( ses->router_class_buffer, get_xml_attr( atts, "router_class" ) );
599 buffer_add( ses->router_command_buffer, get_xml_attr( atts, "router_command" ) );
600 const char* broadcast = get_xml_attr( atts, "broadcast" );
602 ses->router_broadcast = atoi( broadcast );
607 if( ses->state_machine->in_message ) {
609 if( strcmp( (char*) name, "body" ) == 0 ) {
610 ses->state_machine->in_message_body = 1;
614 if( strcmp( (char*) name, "subject" ) == 0 ) {
615 ses->state_machine->in_subject = 1;
619 if( strcmp( (char*) name, "thread" ) == 0 ) {
620 ses->state_machine->in_thread = 1;
626 if( strcmp( (char*) name, "presence" ) == 0 ) {
627 ses->state_machine->in_presence = 1;
628 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
629 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
633 if( strcmp( (char*) name, "status" ) == 0 ) {
634 ses->state_machine->in_status = 1;
639 if( strcmp( (char*) name, "stream:error" ) == 0 ) {
640 ses->state_machine->in_error = 1;
641 ses->state_machine->connected = 0;
642 osrfLogWarning( OSRF_LOG_MARK, "Received <stream:error> message from Jabber server" );
647 /* first server response from a connect attempt */
648 if( strcmp( (char*) name, "stream:stream" ) == 0 ) {
649 if( ses->state_machine->connecting == CONNECTING_1 ) {
650 ses->state_machine->connecting = CONNECTING_2;
651 buffer_add( ses->session_id, get_xml_attr(atts, "id") );
656 if( strcmp( (char*) name, "handshake" ) == 0 ) {
657 ses->state_machine->connected = 1;
658 ses->state_machine->connecting = 0;
663 if( strcmp( (char*) name, "error" ) == 0 ) {
664 ses->state_machine->in_message_error = 1;
665 buffer_add( ses->message_error_type, get_xml_attr( atts, "type" ) );
666 ses->message_error_code = atoi( get_xml_attr( atts, "code" ) );
667 osrfLogInfo( OSRF_LOG_MARK, "Received <error> message with type %s and code %d",
668 OSRF_BUFFER_C_STR( ses->message_error_type ), ses->message_error_code );
672 if( strcmp( (char*) name, "iq" ) == 0 ) {
673 ses->state_machine->in_iq = 1;
675 const char* type = get_xml_attr(atts, "type");
677 if( strcmp( type, "result") == 0
678 && ses->state_machine->connecting == CONNECTING_2 ) {
679 ses->state_machine->connected = 1;
680 ses->state_machine->connecting = 0;
684 if( strcmp( type, "error") == 0 ) {
685 osrfLogWarning( OSRF_LOG_MARK, "Error connecting to jabber" );
692 @brief Return the value of a given XML attribute.
693 @param atts Pointer to a NULL terminated array of strings.
694 @param attr_name Name of the attribute you're looking for.
695 @return The value of the attribute if found, or NULL if not.
697 In the array to which @a atts points, the zeroth entry is an attribute name, and the
698 one after that is its value. Subsequent entries alternate between names and values.
699 The last entry is NULL to terminate the list.
701 static const char* get_xml_attr( const xmlChar** atts, const char* attr_name ) {
704 for(i = 0;(atts[i] != NULL);i++) {
705 if( strcmp( (const char*) atts[i++], attr_name ) == 0 ) {
706 if( atts[i] != NULL ) {
707 return (const char*) atts[i];
717 @brief React to the closing of an XML tag.
718 @param session Pointer to a transport_session, cast to a void pointer.
719 @param name Pointer to the name of the tag that is closing.
721 See what kind of tag is closing, and respond accordingly.
723 static void endElementHandler( void *session, const xmlChar *name) {
724 transport_session* ses = (transport_session*) session;
725 if( ! ses ) { return; }
727 // Bypass a level of indirection, since we'll examine the machine repeatedly:
728 jabber_machine* machine = ses->state_machine;
730 if( machine->in_message && strcmp( (char*) name, "message" ) == 0 ) {
732 /* pass off the message info the callback */
733 if( ses->message_callback ) {
735 transport_message* msg = message_init(
736 OSRF_BUFFER_C_STR( ses->body_buffer ),
737 OSRF_BUFFER_C_STR( ses->subject_buffer ),
738 OSRF_BUFFER_C_STR( ses->thread_buffer ),
739 OSRF_BUFFER_C_STR( ses->recipient_buffer ),
740 OSRF_BUFFER_C_STR( ses->from_buffer ) );
742 message_set_router_info( msg,
743 ses->router_from_buffer->buf,
744 ses->router_to_buffer->buf,
745 ses->router_class_buffer->buf,
746 ses->router_command_buffer->buf,
747 ses->router_broadcast );
749 message_set_osrf_xid( msg, ses->osrf_xid_buffer->buf );
751 if( ses->message_error_type->n_used > 0 ) {
752 set_msg_error( msg, ses->message_error_type->buf, ses->message_error_code );
755 if( msg == NULL ) { return; }
756 ses->message_callback( ses->user_data, msg );
759 machine->in_message = 0;
760 reset_session_buffers( session );
764 if( machine->in_message_body && strcmp( (const char*) name, "body" ) == 0 ) {
765 machine->in_message_body = 0;
769 if( machine->in_subject && strcmp( (const char*) name, "subject" ) == 0 ) {
770 machine->in_subject = 0;
774 if( machine->in_thread && strcmp( (const char*) name, "thread" ) == 0 ) {
775 machine->in_thread = 0;
779 if( machine->in_iq && strcmp( (const char*) name, "iq" ) == 0 ) {
781 if( ses->message_error_code > 0 ) {
782 if( 401 == ses->message_error_code )
783 osrfLogWarning( OSRF_LOG_MARK, "Error 401 in IQ packet: not authorized" );
785 osrfLogWarning( OSRF_LOG_MARK, "Error in IQ packet: code %d",
786 ses->message_error_code );
788 reset_session_buffers( session );
792 if( machine->in_presence && strcmp( (const char*) name, "presence" ) == 0 ) {
793 machine->in_presence = 0;
795 if( ses->presence_callback ) {
796 // call the callback with the status, etc.
799 reset_session_buffers( session );
803 if( machine->in_status && strcmp( (const char*) name, "status" ) == 0 ) {
804 machine->in_status = 0;
808 if( machine->in_message_error && strcmp( (const char*) name, "error" ) == 0 ) {
809 machine->in_message_error = 0;
813 if( machine->in_error && strcmp( (const char*) name, "stream:error" ) == 0 ) {
814 machine->in_error = 0;
820 @brief Clear all the buffers of a transport_session.
821 @param ses Pointer to the transport_session whose buffers are to be cleared.
823 static void reset_session_buffers( transport_session* ses ) {
824 OSRF_BUFFER_RESET( ses->body_buffer );
825 OSRF_BUFFER_RESET( ses->subject_buffer );
826 OSRF_BUFFER_RESET( ses->thread_buffer );
827 OSRF_BUFFER_RESET( ses->from_buffer );
828 OSRF_BUFFER_RESET( ses->recipient_buffer );
829 OSRF_BUFFER_RESET( ses->router_from_buffer );
830 OSRF_BUFFER_RESET( ses->osrf_xid_buffer );
831 OSRF_BUFFER_RESET( ses->router_to_buffer );
832 OSRF_BUFFER_RESET( ses->router_class_buffer );
833 OSRF_BUFFER_RESET( ses->router_command_buffer );
834 OSRF_BUFFER_RESET( ses->message_error_type );
835 OSRF_BUFFER_RESET( ses->session_id );
836 OSRF_BUFFER_RESET( ses->status_buffer );
839 // ------------------------------------------------------------------
840 // takes data out of the body of the message and pushes it into
841 // the appropriate buffer
842 // ------------------------------------------------------------------
844 @brief Copy XML text (outside of tags) into the appropriate buffer.
845 @param session Pointer to the transport_session.
846 @param ch Pointer to the text to be copied.
847 @param len How many characters to be copied.
849 The XML parser calls this as a callback when it finds text outside of a tag, We check
850 the state machine to figure out what kind of text it is, and then append it to the
851 corresponding buffer.
853 static void characterHandler(
854 void *session, const xmlChar *ch, int len) {
856 const char* p = (const char*) ch;
858 transport_session* ses = (transport_session*) session;
859 if( ! ses ) { return; }
861 jabber_machine* machine = ses->state_machine;
863 /* set the various message parts */
864 if( machine->in_message ) {
866 if( machine->in_message_body ) {
867 buffer_add_n( ses->body_buffer, p, len );
870 if( machine->in_subject ) {
871 buffer_add_n( ses->subject_buffer, p, len );
874 if( machine->in_thread ) {
875 buffer_add_n( ses->thread_buffer, p, len );
879 /* set the presence status */
880 if( machine->in_presence && ses->state_machine->in_status ) {
881 buffer_add_n( ses->status_buffer, p, len );
884 if( machine->in_error ) {
886 osrfLogWarning( OSRF_LOG_MARK, "ERROR XML fragment: %s\n", ch );
892 @brief Log a warning from the XML parser.
893 @param session Pointer to a transport_session, cast to a void pointer (not used).
894 @param msg Pointer to a printf-style format string. Subsequent messages, if any, are
895 formatted and inserted into the expanded string.
897 The XML parser calls this function when it wants to warn about something in the XML.
899 static void parseWarningHandler( void *session, const char* msg, ... ) {
900 VA_LIST_TO_STRING(msg);
901 osrfLogWarning( OSRF_LOG_MARK, VA_BUF );
905 @brief Log an error from the XML parser.
906 @param session Pointer to a transport_session, cast to a void pointer (not used).
907 @param msg Pointer to a printf-style format string. Subsequent messages, if any, are
908 formatted and inserted into the expanded string.
910 The XML parser calls this function when it finds an error in the XML.
912 static void parseErrorHandler( void *session, const char* msg, ... ){
913 VA_LIST_TO_STRING(msg);
914 osrfLogError( OSRF_LOG_MARK, VA_BUF );
918 @brief Disconnect from Jabber, and close the socket.
919 @param session Pointer to the transport_session to be disconnected.
920 @return 0 in all cases.
922 int session_disconnect( transport_session* session ) {
923 if( session && session->sock_id != 0 ) {
924 socket_send(session->sock_id, "</stream:stream>");
925 socket_disconnect(session->sock_mgr, session->sock_id);
926 session->sock_id = 0;