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 /* First <stream> packet sent and <stream> packet received from server */
13 /* Note. these are growing buffers, so all that's necessary is a sane starting point */
14 #define JABBER_BODY_BUFSIZE 4096
15 #define JABBER_SUBJECT_BUFSIZE 64
16 #define JABBER_THREAD_BUFSIZE 64
17 #define JABBER_JID_BUFSIZE 64
18 #define JABBER_STATUS_BUFSIZE 16
20 // ---------------------------------------------------------------------------------
21 // Jabber state machine. This is how we know where we are in the Jabber
23 // ---------------------------------------------------------------------------------
24 struct jabber_state_machine_struct {
38 // ---------------------------------------------------------------------------------
39 // Callback for handling the startElement event. Much of the jabber logic occurs
40 // in this and the characterHandler callbacks.
41 // Here we check for the various top level jabber elements: body, iq, etc.
42 // ---------------------------------------------------------------------------------
43 static void startElementHandler(
44 void *session, const xmlChar *name, const xmlChar **atts);
46 // ---------------------------------------------------------------------------------
47 // Callback for handling the endElement event. Updates the Jabber state machine
48 // to let us know the element is over.
49 // ---------------------------------------------------------------------------------
50 static void endElementHandler( void *session, const xmlChar *name);
52 // ---------------------------------------------------------------------------------
53 // This is where we extract XML text content. In particular, this is useful for
54 // extracting Jabber message bodies.
55 // ---------------------------------------------------------------------------------
56 static void characterHandler(
57 void *session, const xmlChar *ch, int len);
59 static void parseWarningHandler( void *session, const char* msg, ... );
60 static void parseErrorHandler( void *session, const char* msg, ... );
62 // ---------------------------------------------------------------------------------
63 // Tells the SAX parser which functions will be used as event callbacks
64 // ---------------------------------------------------------------------------------
65 static xmlSAXHandler SAXHandlerStruct = {
66 NULL, /* internalSubset */
67 NULL, /* isStandalone */
68 NULL, /* hasInternalSubset */
69 NULL, /* hasExternalSubset */
70 NULL, /* resolveEntity */
72 NULL, /* entityDecl */
73 NULL, /* notationDecl */
74 NULL, /* attributeDecl */
75 NULL, /* elementDecl */
76 NULL, /* unparsedEntityDecl */
77 NULL, /* setDocumentLocator */
78 NULL, /* startDocument */
79 NULL, /* endDocument */
80 startElementHandler, /* startElement */
81 endElementHandler, /* endElement */
83 characterHandler, /* characters */
84 NULL, /* ignorableWhitespace */
85 NULL, /* processingInstruction */
87 parseWarningHandler, /* xmlParserWarning */
88 parseErrorHandler, /* xmlParserError */
89 NULL, /* xmlParserFatalError : unused */
90 NULL, /* getParameterEntity */
91 NULL, /* cdataBlock; */
92 NULL, /* externalSubset; */
95 NULL, /* startElementNs */
96 NULL, /* endElementNs */
97 NULL /* xmlStructuredErrorFunc */
100 // ---------------------------------------------------------------------------------
101 // Our SAX handler pointer.
102 // ---------------------------------------------------------------------------------
103 static const xmlSAXHandlerPtr SAXHandler = &SAXHandlerStruct;
105 #ifndef HOST_NAME_MAX
106 #define HOST_NAME_MAX 256
109 static void grab_incoming(void* blob, socket_manager* mgr, int sockid, char* data, int parent);
110 static void reset_session_buffers( transport_session* session );
111 static const char* get_xml_attr( const xmlChar** atts, const char* attr_name );
114 @brief Allocate and initialize a transport_session.
115 @param server Hostname or IP address where the Jabber server resides.
116 @param port Port used for connecting to Jabber (0 if using UNIX domain socket).
117 @param unix_path Name of Jabber's socket in file system (if using UNIX domain socket).
118 @param user_data An opaque pointer stored on behalf of the calling code.
119 @param component Boolean; true if we're a component.
120 @return Pointer to a newly allocated transport_session.
122 This function initializes memory but does not open any sockets or otherwise access
125 If @a port is greater than zero, we will use TCP to connect to Jabber, and ignore
126 @a unix_path. Otherwise we will open a UNIX domain socket using @a unix_path.
128 The calling code is responsible for freeing the transport_session by calling
131 transport_session* init_transport( const char* server,
132 int port, const char* unix_path, void* user_data, int component ) {
137 /* create the session struct */
138 transport_session* session =
139 (transport_session*) safe_malloc( sizeof(transport_session) );
141 session->user_data = user_data;
143 session->component = component;
145 /* initialize the data buffers */
146 session->body_buffer = buffer_init( JABBER_BODY_BUFSIZE );
147 session->subject_buffer = buffer_init( JABBER_SUBJECT_BUFSIZE );
148 session->thread_buffer = buffer_init( JABBER_THREAD_BUFSIZE );
149 session->from_buffer = buffer_init( JABBER_JID_BUFSIZE );
150 session->status_buffer = buffer_init( JABBER_STATUS_BUFSIZE );
151 session->recipient_buffer = buffer_init( JABBER_JID_BUFSIZE );
152 session->message_error_type = buffer_init( JABBER_JID_BUFSIZE );
153 session->session_id = buffer_init( 64 );
155 session->message_error_code = 0;
157 /* for OpenSRF extensions */
158 session->router_to_buffer = buffer_init( JABBER_JID_BUFSIZE );
159 session->router_from_buffer = buffer_init( JABBER_JID_BUFSIZE );
160 session->osrf_xid_buffer = buffer_init( JABBER_JID_BUFSIZE );
161 session->router_class_buffer = buffer_init( JABBER_JID_BUFSIZE );
162 session->router_command_buffer = buffer_init( JABBER_JID_BUFSIZE );
164 session->router_broadcast = 0;
166 /* initialize the jabber state machine */
167 session->state_machine = (jabber_machine*) safe_malloc( sizeof(jabber_machine) );
168 session->state_machine->connected = 0;
169 session->state_machine->connecting = 0;
170 session->state_machine->in_message = 0;
171 session->state_machine->in_message_body = 0;
172 session->state_machine->in_thread = 0;
173 session->state_machine->in_subject = 0;
174 session->state_machine->in_error = 0;
175 session->state_machine->in_message_error = 0;
176 session->state_machine->in_iq = 0;
177 session->state_machine->in_presence = 0;
178 session->state_machine->in_status = 0;
180 /* initialize the sax push parser */
181 session->parser_ctxt = xmlCreatePushParserCtxt(SAXHandler, session, "", 0, NULL);
183 /* initialize the socket_manager structure */
184 session->sock_mgr = (socket_manager*) safe_malloc( sizeof(socket_manager) );
186 session->sock_mgr->data_received = &grab_incoming;
187 session->sock_mgr->on_socket_closed = NULL;
188 session->sock_mgr->socket = NULL;
189 session->sock_mgr->blob = session;
191 session->port = port;
192 session->server = strdup(server);
194 session->unix_path = strdup(unix_path);
195 else session->unix_path = NULL;
197 session->sock_id = 0;
198 session->message_callback = NULL;
205 @brief 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 ) {
212 if( ! session ) { return 0; }
214 if( session->sock_id )
215 session_disconnect( 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 previously
273 designated callback function). There is no guarantee that we will get a complete message
276 int session_wait( transport_session* session, int timeout ) {
277 if( ! session || ! session->sock_mgr ) {
281 int ret = socket_wait( session->sock_mgr, timeout, session->sock_id );
284 osrfLogDebug(OSRF_LOG_MARK, "socket_wait returned error code %d", ret);
285 session->state_machine->connected = 0;
291 @brief Wrap a message in XML and send it to Jabber.
292 @param session Pointer to the transport_session.
293 @param msg Pointer to a transport_message enclosing the message.
294 @return 0 if successful, or -1 upon error.
296 int session_send_msg(
297 transport_session* session, transport_message* msg ) {
299 if( ! session ) { return -1; }
301 if( ! session->state_machine->connected ) {
302 osrfLogWarning(OSRF_LOG_MARK, "State machine is not connected in send_msg()");
306 message_prepare_xml( msg );
307 return socket_send( session->sock_id, msg->msg_xml );
313 @brief Connect to the Jabber server as a client and open a Jabber session.
314 @param session Pointer to a transport_session.
315 @param username Jabber user name.
316 @param password Jabber password.
317 @param resource name of Jabber resource.
318 @param connect_timeout Timeout interval, in seconds, for receiving data (see notes).
319 @param auth_type An enum: either AUTH_PLAIN or AUTH_DIGEST (see notes).
320 @return 1 if successful, or 0 upon error.
322 If @a connect_timeout is -1, wait indefinitely for input activity to appear. If
323 @a connect_timeout is zero, don't wait at all. If @a timeout is positive, wait that
324 number of seconds before timing out. If @a connect_timeout has a negative value other
325 than -1, the results are not well defined.
327 If we connect as a Jabber component, we send the password as an SHA1 hash. Otherwise
328 we look at the @a auth_type. If it's AUTH_PLAIN, we send the password as plaintext; if
329 it's AUTH_DIGEST, we send it as a hash.
331 At this writing, we only use AUTH_DIGEST.
333 int session_connect( transport_session* session,
334 const char* username, const char* password,
335 const char* resource, int connect_timeout, enum TRANSPORT_AUTH_TYPE auth_type ) {
339 osrfLogWarning(OSRF_LOG_MARK, "session is null in session_connect()" );
343 if( session->sock_id != 0 ) {
344 osrfLogWarning(OSRF_LOG_MARK, "transport session is already open, on socket %d",
349 // Open a client socket connecting to the Jabber server
350 if(session->port > 0) { // use TCP
351 session->sock_id = socket_open_tcp_client(
352 session->sock_mgr, session->port, session->server );
353 if( session->sock_id <= 0 ) {
354 session->sock_id = 0;
357 } else if(session->unix_path != NULL) { // use UNIX domain
358 session->sock_id = socket_open_unix_client( session->sock_mgr, session->unix_path );
359 if( session->sock_id <= 0 ) {
360 session->sock_id = 0;
365 osrfLogWarning( OSRF_LOG_MARK, "Can't open session: no port or unix path" );
369 const char* server = session->server;
374 We establish the session in two stages.
376 First we establish an XMPP stream with the Jabber server by sending an opening tag of
377 stream:stream. This is not a complete XML document. We don't send the corresponding
378 closing tag until we close the session.
380 If the Jabber server responds by sending an opening stream:stream tag of its own, we can
381 proceed to the second stage by sending a second stanza to log in. This stanza is an XML
382 element with the tag <handshake> (if we're a Jabber component) or <iq> (if we're not),
383 enclosing the username, password, and resource.
385 If all goes well, the Jabber server responds with a <handshake> or <iq> stanza of its own,
388 If authentication fails, the Jabber server returns a <stream:error> (if we used a <handshake>
389 or an <iq> of type "error" (if we used an <iq>).
391 if( session->component ) {
393 /* the first Jabber connect stanza */
394 char our_hostname[HOST_NAME_MAX + 1] = "";
395 gethostname(our_hostname, sizeof(our_hostname) );
396 our_hostname[HOST_NAME_MAX] = '\0';
397 size1 = 150 + strlen( username ) + strlen( our_hostname );
398 char stanza1[ size1 ];
399 snprintf( stanza1, sizeof(stanza1),
400 "<stream:stream version='1.0' xmlns:stream='http://etherx.jabber.org/streams' "
401 "xmlns='jabber:component:accept' to='%s' from='%s' xml:lang='en'>",
402 username, our_hostname );
404 /* send the first stanze */
405 session->state_machine->connecting = CONNECTING_1;
407 if( socket_send( session->sock_id, stanza1 ) ) {
408 osrfLogWarning(OSRF_LOG_MARK, "error sending");
409 socket_disconnect( session->sock_mgr, session->sock_id );
410 session->sock_id = 0;
415 socket_wait(session->sock_mgr, connect_timeout, session->sock_id);
417 /* server acknowledges our existence, now see if we can login */
418 if( session->state_machine->connecting == CONNECTING_2 ) {
420 int ss = buffer_length( session->session_id ) + strlen( password ) + 5;
422 snprintf( hashstuff, sizeof(hashstuff), "%s%s", OSRF_BUFFER_C_STR( session->session_id ), password );
424 char* hash = shahash( hashstuff );
425 size2 = 100 + strlen( hash );
426 char stanza2[ size2 ];
427 snprintf( stanza2, sizeof(stanza2), "<handshake>%s</handshake>", hash );
429 if( socket_send( session->sock_id, stanza2 ) ) {
430 osrfLogWarning(OSRF_LOG_MARK, "error sending");
431 socket_disconnect( session->sock_mgr, session->sock_id );
432 session->sock_id = 0;
437 } else { /* we're not a component */
439 /* the first Jabber connect stanza */
440 size1 = 100 + strlen( server );
441 char stanza1[ size1 ];
442 snprintf( stanza1, sizeof(stanza1),
443 "<stream:stream to='%s' xmlns='jabber:client' "
444 "xmlns:stream='http://etherx.jabber.org/streams'>",
447 /* send the first stanze */
448 session->state_machine->connecting = CONNECTING_1;
449 if( socket_send( session->sock_id, stanza1 ) ) {
450 osrfLogWarning(OSRF_LOG_MARK, "error sending");
451 socket_disconnect( session->sock_mgr, session->sock_id );
452 session->sock_id = 0;
458 socket_wait( session->sock_mgr, connect_timeout, session->sock_id ); /* make the timeout smarter XXX */
460 if( auth_type == AUTH_PLAIN ) {
462 /* the second jabber connect stanza including login info*/
463 size2 = 150 + strlen( username ) + strlen( password ) + strlen( resource );
464 char stanza2[ size2 ];
465 snprintf( stanza2, sizeof(stanza2),
466 "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
467 "<username>%s</username><password>%s</password><resource>%s</resource></query></iq>",
468 username, password, resource );
470 /* server acknowledges our existence, now see if we can login */
471 if( session->state_machine->connecting == CONNECTING_2 ) {
472 if( socket_send( session->sock_id, stanza2 ) ) {
473 osrfLogWarning(OSRF_LOG_MARK, "error sending");
474 socket_disconnect( session->sock_mgr, session->sock_id );
475 session->sock_id = 0;
480 } else if( auth_type == AUTH_DIGEST ) {
482 int ss = buffer_length( session->session_id ) + strlen( password ) + 5;
484 snprintf( hashstuff, sizeof(hashstuff), "%s%s", OSRF_BUFFER_C_STR( session->session_id ), password );
486 char* hash = shahash( hashstuff );
488 /* the second jabber connect stanza including login info */
489 size2 = 150 + strlen( username ) + strlen( hash ) + strlen(resource);
490 char stanza2[ size2 ];
491 snprintf( stanza2, sizeof(stanza2),
492 "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
493 "<username>%s</username><digest>%s</digest><resource>%s</resource></query></iq>",
494 username, hash, resource );
496 /* server acknowledges our existence, now see if we can login */
497 if( session->state_machine->connecting == CONNECTING_2 ) {
498 if( socket_send( session->sock_id, stanza2 ) ) {
499 osrfLogWarning(OSRF_LOG_MARK, "error sending");
500 socket_disconnect( session->sock_mgr, session->sock_id );
501 session->sock_id = 0;
507 osrfLogWarning(OSRF_LOG_MARK, "Invalid auth_type parameter: %d",
509 socket_disconnect( session->sock_mgr, session->sock_id );
510 session->sock_id = 0;
517 /* wait for reply to login request */
518 socket_wait( session->sock_mgr, connect_timeout, session->sock_id );
520 if( session->state_machine->connected ) {
524 socket_disconnect( session->sock_mgr, session->sock_id );
525 session->sock_id = 0;
531 @brief Callback function: push a buffer of XML into an XML parser.
532 @param blob Void pointer pointing to the transport_session.
533 @param mgr Pointer to the socket_manager (not used).
534 @param sockid Socket file descriptor (not used)
535 @param data Pointer to a buffer of received data, as a nul-terminated string.
536 @param parent Not applicable.
538 The socket_manager calls this function when it reads a buffer's worth of data from
539 the Jabber socket. The XML parser calls other callback functions when it sees various
542 static void grab_incoming(void* blob, socket_manager* mgr, int sockid, char* data, int parent) {
543 transport_session* ses = (transport_session*) blob;
544 if( ! ses ) { return; }
545 xmlParseChunk(ses->parser_ctxt, data, strlen(data), 0);
550 @brief Respond to the beginning of an XML element.
551 @param session Pointer to the transport_session, cast to a void pointer.
552 @param name Name of the XML element.
553 @param atts Pointer to a ragged array containing attributes and values.
555 The XML parser calls this when it sees the beginning of an XML element. We note what
556 element it is by setting the corresponding switch in the state machine, and grab whatever
557 attributes we expect to find.
559 static void startElementHandler(
560 void *session, const xmlChar *name, const xmlChar **atts) {
562 transport_session* ses = (transport_session*) session;
563 if( ! ses ) { return; }
566 if( strcmp( (char*) name, "message" ) == 0 ) {
567 ses->state_machine->in_message = 1;
568 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
569 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
570 buffer_add( ses->router_from_buffer, get_xml_attr( atts, "router_from" ) );
571 buffer_add( ses->osrf_xid_buffer, get_xml_attr( atts, "osrf_xid" ) );
572 buffer_add( ses->router_to_buffer, get_xml_attr( atts, "router_to" ) );
573 buffer_add( ses->router_class_buffer, get_xml_attr( atts, "router_class" ) );
574 buffer_add( ses->router_command_buffer, get_xml_attr( atts, "router_command" ) );
575 const char* broadcast = get_xml_attr( atts, "broadcast" );
577 ses->router_broadcast = atoi( broadcast );
582 if( ses->state_machine->in_message ) {
584 if( strcmp( (char*) name, "body" ) == 0 ) {
585 ses->state_machine->in_message_body = 1;
589 if( strcmp( (char*) name, "subject" ) == 0 ) {
590 ses->state_machine->in_subject = 1;
594 if( strcmp( (char*) name, "thread" ) == 0 ) {
595 ses->state_machine->in_thread = 1;
601 if( strcmp( (char*) name, "presence" ) == 0 ) {
602 ses->state_machine->in_presence = 1;
603 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
604 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
608 if( strcmp( (char*) name, "status" ) == 0 ) {
609 ses->state_machine->in_status = 1;
614 if( strcmp( (char*) name, "stream:error" ) == 0 ) {
615 ses->state_machine->in_error = 1;
616 ses->state_machine->connected = 0;
617 osrfLogWarning( OSRF_LOG_MARK, "Received <stream:error> message from Jabber server" );
622 /* first server response from a connect attempt */
623 if( strcmp( (char*) name, "stream:stream" ) == 0 ) {
624 if( ses->state_machine->connecting == CONNECTING_1 ) {
625 ses->state_machine->connecting = CONNECTING_2;
626 buffer_add( ses->session_id, get_xml_attr(atts, "id") );
631 if( strcmp( (char*) name, "handshake" ) == 0 ) {
632 ses->state_machine->connected = 1;
633 ses->state_machine->connecting = 0;
638 if( strcmp( (char*) name, "error" ) == 0 ) {
639 ses->state_machine->in_message_error = 1;
640 buffer_add( ses->message_error_type, get_xml_attr( atts, "type" ) );
641 ses->message_error_code = atoi( get_xml_attr( atts, "code" ) );
642 osrfLogInfo( OSRF_LOG_MARK, "Received <error> message with type %s and code %d",
643 OSRF_BUFFER_C_STR( ses->message_error_type ), ses->message_error_code );
647 if( strcmp( (char*) name, "iq" ) == 0 ) {
648 ses->state_machine->in_iq = 1;
650 const char* type = get_xml_attr(atts, "type");
652 if( strcmp( type, "result") == 0
653 && ses->state_machine->connecting == CONNECTING_2 ) {
654 ses->state_machine->connected = 1;
655 ses->state_machine->connecting = 0;
659 if( strcmp( type, "error") == 0 ) {
660 osrfLogWarning( OSRF_LOG_MARK, "Error connecting to jabber" );
667 @brief Return the value of a given XML attribute.
668 @param atts Pointer to a NULL terminated array of strings.
669 @param attr_name Name of the attribute you're looking for.
670 @return The value of the attribute if found, or NULL if not.
672 In the array to which @a atts points, the zeroth entry is an attribute name, and the
673 one after that is its value. Subsequent entries alternate between names and values.
674 The last entry is NULL to terminate the list.
676 static const char* get_xml_attr( const xmlChar** atts, const char* attr_name ) {
679 for(i = 0;(atts[i] != NULL);i++) {
680 if( strcmp( (const char*) atts[i++], attr_name ) == 0 ) {
681 if( atts[i] != NULL ) {
682 return (const char*) atts[i];
692 @brief React to the closing of an XML tag.
693 @param session Pointer to a transport_session, cast to a void pointer.
694 @param name Pointer to the name of the tag that is closing.
696 See what kind of tag is closing, and respond accordingly.
698 static void endElementHandler( void *session, const xmlChar *name) {
699 transport_session* ses = (transport_session*) session;
700 if( ! ses ) { return; }
702 // Bypass a level of indirection, since we'll examine the machine repeatedly:
703 jabber_machine* machine = ses->state_machine;
705 if( machine->in_message && strcmp( (char*) name, "message" ) == 0 ) {
707 /* pass off the message info the callback */
708 if( ses->message_callback ) {
710 transport_message* msg = message_init(
711 OSRF_BUFFER_C_STR( ses->body_buffer ),
712 OSRF_BUFFER_C_STR( ses->subject_buffer ),
713 OSRF_BUFFER_C_STR( ses->thread_buffer ),
714 OSRF_BUFFER_C_STR( ses->recipient_buffer ),
715 OSRF_BUFFER_C_STR( ses->from_buffer ) );
717 message_set_router_info( msg,
718 ses->router_from_buffer->buf,
719 ses->router_to_buffer->buf,
720 ses->router_class_buffer->buf,
721 ses->router_command_buffer->buf,
722 ses->router_broadcast );
724 message_set_osrf_xid( msg, ses->osrf_xid_buffer->buf );
726 if( ses->message_error_type->n_used > 0 ) {
727 set_msg_error( msg, ses->message_error_type->buf, ses->message_error_code );
730 if( msg == NULL ) { return; }
731 ses->message_callback( ses->user_data, msg );
734 machine->in_message = 0;
735 reset_session_buffers( session );
739 if( machine->in_message_body && strcmp( (const char*) name, "body" ) == 0 ) {
740 machine->in_message_body = 0;
744 if( machine->in_subject && strcmp( (const char*) name, "subject" ) == 0 ) {
745 machine->in_subject = 0;
749 if( machine->in_thread && strcmp( (const char*) name, "thread" ) == 0 ) {
750 machine->in_thread = 0;
754 if( machine->in_iq && strcmp( (const char*) name, "iq" ) == 0 ) {
756 if( ses->message_error_code > 0 ) {
757 osrfLogWarning( OSRF_LOG_MARK, "Error in IQ packet: code %d", ses->message_error_code );
758 osrfLogWarning( OSRF_LOG_MARK, "Error 401 means not authorized" );
760 reset_session_buffers( session );
764 if( machine->in_presence && strcmp( (const char*) name, "presence" ) == 0 ) {
765 machine->in_presence = 0;
767 if( ses->presence_callback ) {
768 // call the callback with the status, etc.
771 reset_session_buffers( session );
775 if( machine->in_status && strcmp( (const char*) name, "status" ) == 0 ) {
776 machine->in_status = 0;
780 if( machine->in_message_error && strcmp( (const char*) name, "error" ) == 0 ) {
781 machine->in_message_error = 0;
785 if( machine->in_error && strcmp( (const char*) name, "stream:error" ) == 0 ) {
786 machine->in_error = 0;
792 @brief Clear all the buffers of a transport_session.
793 @param ses Pointer to the transport_session whose buffers are to be cleared.
795 static void reset_session_buffers( transport_session* ses ) {
796 OSRF_BUFFER_RESET( ses->body_buffer );
797 OSRF_BUFFER_RESET( ses->subject_buffer );
798 OSRF_BUFFER_RESET( ses->thread_buffer );
799 OSRF_BUFFER_RESET( ses->from_buffer );
800 OSRF_BUFFER_RESET( ses->recipient_buffer );
801 OSRF_BUFFER_RESET( ses->router_from_buffer );
802 OSRF_BUFFER_RESET( ses->osrf_xid_buffer );
803 OSRF_BUFFER_RESET( ses->router_to_buffer );
804 OSRF_BUFFER_RESET( ses->router_class_buffer );
805 OSRF_BUFFER_RESET( ses->router_command_buffer );
806 OSRF_BUFFER_RESET( ses->message_error_type );
807 OSRF_BUFFER_RESET( ses->session_id );
808 OSRF_BUFFER_RESET( ses->status_buffer );
811 // ------------------------------------------------------------------
812 // takes data out of the body of the message and pushes it into
813 // the appropriate buffer
814 // ------------------------------------------------------------------
816 @brief Copy XML text (outside of tags) into the appropriate buffer.
817 @param session Pointer to the transport_session.
818 @param ch Pointer to the text to be copied.
819 @param len How many characters to be copied.
821 The XML parser calls this as a callback when it finds text outside of a tag, We check
822 the state machine to figure out what kind of text it is, and then append it to the
823 corresponding buffer.
825 static void characterHandler(
826 void *session, const xmlChar *ch, int len) {
828 const char* p = (const char*) ch;
830 transport_session* ses = (transport_session*) session;
831 if( ! ses ) { return; }
833 jabber_machine* machine = ses->state_machine;
835 /* set the various message parts */
836 if( machine->in_message ) {
838 if( machine->in_message_body ) {
839 buffer_add_n( ses->body_buffer, p, len );
842 if( machine->in_subject ) {
843 buffer_add_n( ses->subject_buffer, p, len );
846 if( machine->in_thread ) {
847 buffer_add_n( ses->thread_buffer, p, len );
851 /* set the presence status */
852 if( machine->in_presence && ses->state_machine->in_status ) {
853 buffer_add_n( ses->status_buffer, p, len );
856 if( machine->in_error ) {
858 osrfLogWarning( OSRF_LOG_MARK, "ERROR XML fragment: %s\n", ch );
864 @brief Report a warning from the XML parser.
865 @param session Pointer to a transport_session, cast to a void pointer (not used).
866 @param msg Pointer to a printf-style format string. Subsequent messages, if any, are
867 formatted and inserted into the expanded string.
869 The XML parser calls this function when it wants to warn about something in the XML.
871 static void parseWarningHandler( void *session, const char* msg, ... ) {
875 fprintf(stdout, "transport_session XML WARNING");
876 vfprintf(stdout, msg, args);
878 fprintf(stderr, "XML WARNING: %s\n", msg );
882 @brief Report an error from the XML parser.
883 @param session Pointer to a transport_session, cast to a void pointer (not used).
884 @param msg Pointer to a printf-style format string. Subsequent messages, if any, are
885 formatted and inserted into the expanded string.
887 The XML parser calls this function when it finds an error in the XML.
889 static void parseErrorHandler( void *session, const char* msg, ... ){
893 fprintf(stdout, "transport_session XML ERROR");
894 vfprintf(stdout, msg, args);
896 fprintf(stderr, "XML ERROR: %s\n", msg );
900 @brief Disconnect from Jabber, and close the socket.
901 @param session Pointer to the transport_session to be disconnected.
902 @return 0 in all cases.
904 int session_disconnect( transport_session* session ) {
905 if( session && session->sock_id != 0 ) {
906 socket_send(session->sock_id, "</stream:stream>");
907 socket_disconnect(session->sock_mgr, session->sock_id);
908 session->sock_id = 0;