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;
206 @brief Destroy a transport_session, and close its socket.
207 @param session Pointer to the transport_session to be destroyed.
208 @return 1 if successful, or 0 if not.
210 The only error condition is a NULL pointer argument.
212 int session_free( transport_session* session ) {
213 if( ! session ) { return 0; }
215 if( session->sock_id )
216 session_disconnect( session );
218 if(session->sock_mgr)
219 socket_manager_free(session->sock_mgr);
221 if( session->state_machine ) free( session->state_machine );
222 if( session->parser_ctxt) {
223 xmlFreeDoc( session->parser_ctxt->myDoc );
224 xmlFreeParserCtxt(session->parser_ctxt);
227 xmlCleanupCharEncodingHandlers();
231 buffer_free(session->body_buffer);
232 buffer_free(session->subject_buffer);
233 buffer_free(session->thread_buffer);
234 buffer_free(session->from_buffer);
235 buffer_free(session->recipient_buffer);
236 buffer_free(session->status_buffer);
237 buffer_free(session->message_error_type);
238 buffer_free(session->router_to_buffer);
239 buffer_free(session->router_from_buffer);
240 buffer_free(session->osrf_xid_buffer);
241 buffer_free(session->router_class_buffer);
242 buffer_free(session->router_command_buffer);
243 buffer_free(session->session_id);
245 free(session->server);
246 free(session->unix_path);
253 @brief Determine whether a transport_session is connected.
254 @param session Pointer to the transport_session to be tested.
255 @return 1 if connected, or 0 if not.
257 int session_connected( transport_session* session ) {
258 return session ? session->state_machine->connected : 0;
262 @brief Wait on the client socket connected to Jabber, and process any resulting input.
263 @param session Pointer to the transport_session.
264 @param timeout How seconds to wait before timing out (see notes).
265 @return 0 if successful, or -1 if a timeout or other error occurs, or if the server
266 closes the connection at the other end.
268 If @a timeout is -1, wait indefinitely for input activity to appear. If @a timeout is
269 zero, don't wait at all. If @a timeout is positive, wait that number of seconds
270 before timing out. If @a timeout has a negative value other than -1, the results are not
273 Read all available input from the socket and pass it through grab_incoming() (a
274 callback function previously installed in the socket_manager).
276 There is no guarantee that we will get a complete message from a single call. As a
277 result, the calling code should call this function in a loop until it gets a complete
278 message, or until an error occurs.
280 int session_wait( transport_session* session, int timeout ) {
281 if( ! session || ! session->sock_mgr ) {
285 int ret = socket_wait( session->sock_mgr, timeout, session->sock_id );
288 osrfLogDebug(OSRF_LOG_MARK, "socket_wait returned error code %d", ret);
289 session->state_machine->connected = 0;
295 @brief Wrap a message in XML and send it to Jabber.
296 @param session Pointer to the transport_session.
297 @param msg Pointer to a transport_message enclosing the message.
298 @return 0 if successful, or -1 upon error.
300 int session_send_msg(
301 transport_session* session, transport_message* msg ) {
303 if( ! session ) { return -1; }
305 if( ! session->state_machine->connected ) {
306 osrfLogWarning(OSRF_LOG_MARK, "State machine is not connected in send_msg()");
310 message_prepare_xml( msg );
311 return socket_send( session->sock_id, msg->msg_xml );
317 @brief Connect to the Jabber server as a client and open a Jabber session.
318 @param session Pointer to a transport_session.
319 @param username Jabber user name.
320 @param password Jabber password.
321 @param resource name of Jabber resource.
322 @param connect_timeout Timeout interval, in seconds, for receiving data (see notes).
323 @param auth_type An enum: either AUTH_PLAIN or AUTH_DIGEST (see notes).
324 @return 1 if successful, or 0 upon error.
326 If @a connect_timeout is -1, wait indefinitely for input activity to appear. If
327 @a connect_timeout is zero, don't wait at all. If @a timeout is positive, wait that
328 number of seconds before timing out. If @a connect_timeout has a negative value other
329 than -1, the results are not well defined.
331 If we connect as a Jabber component, we send the password as an SHA1 hash. Otherwise
332 we look at the @a auth_type. If it's AUTH_PLAIN, we send the password as plaintext; if
333 it's AUTH_DIGEST, we send it as a hash.
335 At this writing, we only use AUTH_DIGEST.
337 int session_connect( transport_session* session,
338 const char* username, const char* password,
339 const char* resource, int connect_timeout, enum TRANSPORT_AUTH_TYPE auth_type ) {
343 osrfLogWarning(OSRF_LOG_MARK, "session is null in session_connect()" );
347 if( session->sock_id != 0 ) {
348 osrfLogWarning(OSRF_LOG_MARK, "transport session is already open, on socket %d",
353 // Open a client socket connecting to the Jabber server
354 if(session->port > 0) { // use TCP
355 session->sock_id = socket_open_tcp_client(
356 session->sock_mgr, session->port, session->server );
357 if( session->sock_id <= 0 ) {
358 session->sock_id = 0;
361 } else if(session->unix_path != NULL) { // use UNIX domain
362 session->sock_id = socket_open_unix_client( session->sock_mgr, session->unix_path );
363 if( session->sock_id <= 0 ) {
364 session->sock_id = 0;
369 osrfLogWarning( OSRF_LOG_MARK, "Can't open session: no port or unix path" );
373 const char* server = session->server;
378 We establish the session in two stages.
380 First we establish an XMPP stream with the Jabber server by sending an opening tag of
381 stream:stream. This is not a complete XML document. We don't send the corresponding
382 closing tag until we close the session.
384 If the Jabber server responds by sending an opening stream:stream tag of its own, we can
385 proceed to the second stage by sending a second stanza to log in. This stanza is an XML
386 element with the tag <handshake> (if we're a Jabber component) or <iq> (if we're not),
387 enclosing the username, password, and resource.
389 If all goes well, the Jabber server responds with a <handshake> or <iq> stanza of its own,
392 If authentication fails, the Jabber server returns a <stream:error> (if we used a <handshake>
393 or an <iq> of type "error" (if we used an <iq>).
395 if( session->component ) {
397 /* the first Jabber connect stanza */
398 char our_hostname[HOST_NAME_MAX + 1] = "";
399 gethostname(our_hostname, sizeof(our_hostname) );
400 our_hostname[HOST_NAME_MAX] = '\0';
401 size1 = 150 + strlen( username ) + strlen( our_hostname );
402 char stanza1[ size1 ];
403 snprintf( stanza1, sizeof(stanza1),
404 "<stream:stream version='1.0' xmlns:stream='http://etherx.jabber.org/streams' "
405 "xmlns='jabber:component:accept' to='%s' from='%s' xml:lang='en'>",
406 username, our_hostname );
408 /* send the first stanze */
409 session->state_machine->connecting = CONNECTING_1;
411 if( socket_send( session->sock_id, stanza1 ) ) {
412 osrfLogWarning(OSRF_LOG_MARK, "error sending");
413 socket_disconnect( session->sock_mgr, session->sock_id );
414 session->sock_id = 0;
419 socket_wait(session->sock_mgr, connect_timeout, session->sock_id);
421 /* server acknowledges our existence, now see if we can login */
422 if( session->state_machine->connecting == CONNECTING_2 ) {
424 int ss = buffer_length( session->session_id ) + strlen( password ) + 5;
426 snprintf( hashstuff, sizeof(hashstuff), "%s%s",
427 OSRF_BUFFER_C_STR( session->session_id ), password );
429 char* hash = shahash( hashstuff );
430 size2 = 100 + strlen( hash );
431 char stanza2[ size2 ];
432 snprintf( stanza2, sizeof(stanza2), "<handshake>%s</handshake>", hash );
434 if( socket_send( session->sock_id, stanza2 ) ) {
435 osrfLogWarning(OSRF_LOG_MARK, "error sending");
436 socket_disconnect( session->sock_mgr, session->sock_id );
437 session->sock_id = 0;
442 } else { /* we're not a component */
444 /* the first Jabber connect stanza */
445 size1 = 100 + strlen( server );
446 char stanza1[ size1 ];
447 snprintf( stanza1, sizeof(stanza1),
448 "<stream:stream to='%s' xmlns='jabber:client' "
449 "xmlns:stream='http://etherx.jabber.org/streams'>",
452 /* send the first stanze */
453 session->state_machine->connecting = CONNECTING_1;
454 if( socket_send( session->sock_id, stanza1 ) ) {
455 osrfLogWarning(OSRF_LOG_MARK, "error sending");
456 socket_disconnect( session->sock_mgr, session->sock_id );
457 session->sock_id = 0;
463 socket_wait( session->sock_mgr, connect_timeout, session->sock_id ); /* make the timeout smarter XXX */
465 if( auth_type == AUTH_PLAIN ) {
467 /* the second jabber connect stanza including login info*/
468 size2 = 150 + strlen( username ) + strlen( password ) + strlen( resource );
469 char stanza2[ size2 ];
470 snprintf( stanza2, sizeof(stanza2),
471 "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
472 "<username>%s</username><password>%s</password><resource>%s</resource></query></iq>",
473 username, password, resource );
475 /* server acknowledges our existence, now see if we can login */
476 if( session->state_machine->connecting == CONNECTING_2 ) {
477 if( socket_send( session->sock_id, stanza2 ) ) {
478 osrfLogWarning(OSRF_LOG_MARK, "error sending");
479 socket_disconnect( session->sock_mgr, session->sock_id );
480 session->sock_id = 0;
485 } else if( auth_type == AUTH_DIGEST ) {
487 int ss = buffer_length( session->session_id ) + strlen( password ) + 5;
489 snprintf( hashstuff, sizeof(hashstuff), "%s%s", OSRF_BUFFER_C_STR( session->session_id ), password );
491 char* hash = shahash( hashstuff );
493 /* the second jabber connect stanza including login info */
494 size2 = 150 + strlen( username ) + strlen( hash ) + strlen(resource);
495 char stanza2[ size2 ];
496 snprintf( stanza2, sizeof(stanza2),
497 "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
498 "<username>%s</username><digest>%s</digest><resource>%s</resource></query></iq>",
499 username, hash, resource );
501 /* server acknowledges our existence, now see if we can login */
502 if( session->state_machine->connecting == CONNECTING_2 ) {
503 if( socket_send( session->sock_id, stanza2 ) ) {
504 osrfLogWarning(OSRF_LOG_MARK, "error sending");
505 socket_disconnect( session->sock_mgr, session->sock_id );
506 session->sock_id = 0;
512 osrfLogWarning(OSRF_LOG_MARK, "Invalid auth_type parameter: %d",
514 socket_disconnect( session->sock_mgr, session->sock_id );
515 session->sock_id = 0;
522 /* wait for reply to login request */
523 socket_wait( session->sock_mgr, connect_timeout, session->sock_id );
525 if( session->state_machine->connected ) {
529 socket_disconnect( session->sock_mgr, session->sock_id );
530 session->sock_id = 0;
536 @brief Callback function: push a buffer of XML into an XML parser.
537 @param blob Void pointer pointing to the transport_session.
538 @param mgr Pointer to the socket_manager (not used).
539 @param sockid Socket file descriptor (not used)
540 @param data Pointer to a buffer of received data, as a nul-terminated string.
541 @param parent Not applicable.
543 The socket_manager calls this function when it reads a buffer's worth of data from
544 the Jabber socket. The XML parser calls other callback functions when it sees various
547 static void grab_incoming(void* blob, socket_manager* mgr, int sockid, char* data, int parent) {
548 transport_session* ses = (transport_session*) blob;
549 if( ! ses ) { return; }
550 xmlParseChunk(ses->parser_ctxt, data, strlen(data), 0);
555 @brief Respond to the beginning of an XML element.
556 @param session Pointer to the transport_session, cast to a void pointer.
557 @param name Name of the XML element.
558 @param atts Pointer to a ragged array containing attributes and values.
560 The XML parser calls this when it sees the beginning of an XML element. We note what
561 element it is by setting the corresponding switch in the state machine, and grab whatever
562 attributes we expect to find.
564 static void startElementHandler(
565 void *session, const xmlChar *name, const xmlChar **atts) {
567 transport_session* ses = (transport_session*) session;
568 if( ! ses ) { return; }
571 if( strcmp( (char*) name, "message" ) == 0 ) {
572 ses->state_machine->in_message = 1;
573 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
574 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
575 buffer_add( ses->router_from_buffer, get_xml_attr( atts, "router_from" ) );
576 buffer_add( ses->osrf_xid_buffer, get_xml_attr( atts, "osrf_xid" ) );
577 buffer_add( ses->router_to_buffer, get_xml_attr( atts, "router_to" ) );
578 buffer_add( ses->router_class_buffer, get_xml_attr( atts, "router_class" ) );
579 buffer_add( ses->router_command_buffer, get_xml_attr( atts, "router_command" ) );
580 const char* broadcast = get_xml_attr( atts, "broadcast" );
582 ses->router_broadcast = atoi( broadcast );
587 if( ses->state_machine->in_message ) {
589 if( strcmp( (char*) name, "body" ) == 0 ) {
590 ses->state_machine->in_message_body = 1;
594 if( strcmp( (char*) name, "subject" ) == 0 ) {
595 ses->state_machine->in_subject = 1;
599 if( strcmp( (char*) name, "thread" ) == 0 ) {
600 ses->state_machine->in_thread = 1;
606 if( strcmp( (char*) name, "presence" ) == 0 ) {
607 ses->state_machine->in_presence = 1;
608 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
609 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
613 if( strcmp( (char*) name, "status" ) == 0 ) {
614 ses->state_machine->in_status = 1;
619 if( strcmp( (char*) name, "stream:error" ) == 0 ) {
620 ses->state_machine->in_error = 1;
621 ses->state_machine->connected = 0;
622 osrfLogWarning( OSRF_LOG_MARK, "Received <stream:error> message from Jabber server" );
627 /* first server response from a connect attempt */
628 if( strcmp( (char*) name, "stream:stream" ) == 0 ) {
629 if( ses->state_machine->connecting == CONNECTING_1 ) {
630 ses->state_machine->connecting = CONNECTING_2;
631 buffer_add( ses->session_id, get_xml_attr(atts, "id") );
636 if( strcmp( (char*) name, "handshake" ) == 0 ) {
637 ses->state_machine->connected = 1;
638 ses->state_machine->connecting = 0;
643 if( strcmp( (char*) name, "error" ) == 0 ) {
644 ses->state_machine->in_message_error = 1;
645 buffer_add( ses->message_error_type, get_xml_attr( atts, "type" ) );
646 ses->message_error_code = atoi( get_xml_attr( atts, "code" ) );
647 osrfLogInfo( OSRF_LOG_MARK, "Received <error> message with type %s and code %d",
648 OSRF_BUFFER_C_STR( ses->message_error_type ), ses->message_error_code );
652 if( strcmp( (char*) name, "iq" ) == 0 ) {
653 ses->state_machine->in_iq = 1;
655 const char* type = get_xml_attr(atts, "type");
657 if( strcmp( type, "result") == 0
658 && ses->state_machine->connecting == CONNECTING_2 ) {
659 ses->state_machine->connected = 1;
660 ses->state_machine->connecting = 0;
664 if( strcmp( type, "error") == 0 ) {
665 osrfLogWarning( OSRF_LOG_MARK, "Error connecting to jabber" );
672 @brief Return the value of a given XML attribute.
673 @param atts Pointer to a NULL terminated array of strings.
674 @param attr_name Name of the attribute you're looking for.
675 @return The value of the attribute if found, or NULL if not.
677 In the array to which @a atts points, the zeroth entry is an attribute name, and the
678 one after that is its value. Subsequent entries alternate between names and values.
679 The last entry is NULL to terminate the list.
681 static const char* get_xml_attr( const xmlChar** atts, const char* attr_name ) {
684 for(i = 0;(atts[i] != NULL);i++) {
685 if( strcmp( (const char*) atts[i++], attr_name ) == 0 ) {
686 if( atts[i] != NULL ) {
687 return (const char*) atts[i];
697 @brief React to the closing of an XML tag.
698 @param session Pointer to a transport_session, cast to a void pointer.
699 @param name Pointer to the name of the tag that is closing.
701 See what kind of tag is closing, and respond accordingly.
703 static void endElementHandler( void *session, const xmlChar *name) {
704 transport_session* ses = (transport_session*) session;
705 if( ! ses ) { return; }
707 // Bypass a level of indirection, since we'll examine the machine repeatedly:
708 jabber_machine* machine = ses->state_machine;
710 if( machine->in_message && strcmp( (char*) name, "message" ) == 0 ) {
712 /* pass off the message info the callback */
713 if( ses->message_callback ) {
715 transport_message* msg = message_init(
716 OSRF_BUFFER_C_STR( ses->body_buffer ),
717 OSRF_BUFFER_C_STR( ses->subject_buffer ),
718 OSRF_BUFFER_C_STR( ses->thread_buffer ),
719 OSRF_BUFFER_C_STR( ses->recipient_buffer ),
720 OSRF_BUFFER_C_STR( ses->from_buffer ) );
722 message_set_router_info( msg,
723 ses->router_from_buffer->buf,
724 ses->router_to_buffer->buf,
725 ses->router_class_buffer->buf,
726 ses->router_command_buffer->buf,
727 ses->router_broadcast );
729 message_set_osrf_xid( msg, ses->osrf_xid_buffer->buf );
731 if( ses->message_error_type->n_used > 0 ) {
732 set_msg_error( msg, ses->message_error_type->buf, ses->message_error_code );
735 if( msg == NULL ) { return; }
736 ses->message_callback( ses->user_data, msg );
739 machine->in_message = 0;
740 reset_session_buffers( session );
744 if( machine->in_message_body && strcmp( (const char*) name, "body" ) == 0 ) {
745 machine->in_message_body = 0;
749 if( machine->in_subject && strcmp( (const char*) name, "subject" ) == 0 ) {
750 machine->in_subject = 0;
754 if( machine->in_thread && strcmp( (const char*) name, "thread" ) == 0 ) {
755 machine->in_thread = 0;
759 if( machine->in_iq && strcmp( (const char*) name, "iq" ) == 0 ) {
761 if( ses->message_error_code > 0 ) {
762 if( 401 == ses->message_error_code )
763 osrfLogWarning( OSRF_LOG_MARK, "Error 401 in IQ packet: not authorized" );
765 osrfLogWarning( OSRF_LOG_MARK, "Error in IQ packet: code %d",
766 ses->message_error_code );
768 reset_session_buffers( session );
772 if( machine->in_presence && strcmp( (const char*) name, "presence" ) == 0 ) {
773 machine->in_presence = 0;
775 if( ses->presence_callback ) {
776 // call the callback with the status, etc.
779 reset_session_buffers( session );
783 if( machine->in_status && strcmp( (const char*) name, "status" ) == 0 ) {
784 machine->in_status = 0;
788 if( machine->in_message_error && strcmp( (const char*) name, "error" ) == 0 ) {
789 machine->in_message_error = 0;
793 if( machine->in_error && strcmp( (const char*) name, "stream:error" ) == 0 ) {
794 machine->in_error = 0;
800 @brief Clear all the buffers of a transport_session.
801 @param ses Pointer to the transport_session whose buffers are to be cleared.
803 static void reset_session_buffers( transport_session* ses ) {
804 OSRF_BUFFER_RESET( ses->body_buffer );
805 OSRF_BUFFER_RESET( ses->subject_buffer );
806 OSRF_BUFFER_RESET( ses->thread_buffer );
807 OSRF_BUFFER_RESET( ses->from_buffer );
808 OSRF_BUFFER_RESET( ses->recipient_buffer );
809 OSRF_BUFFER_RESET( ses->router_from_buffer );
810 OSRF_BUFFER_RESET( ses->osrf_xid_buffer );
811 OSRF_BUFFER_RESET( ses->router_to_buffer );
812 OSRF_BUFFER_RESET( ses->router_class_buffer );
813 OSRF_BUFFER_RESET( ses->router_command_buffer );
814 OSRF_BUFFER_RESET( ses->message_error_type );
815 OSRF_BUFFER_RESET( ses->session_id );
816 OSRF_BUFFER_RESET( ses->status_buffer );
819 // ------------------------------------------------------------------
820 // takes data out of the body of the message and pushes it into
821 // the appropriate buffer
822 // ------------------------------------------------------------------
824 @brief Copy XML text (outside of tags) into the appropriate buffer.
825 @param session Pointer to the transport_session.
826 @param ch Pointer to the text to be copied.
827 @param len How many characters to be copied.
829 The XML parser calls this as a callback when it finds text outside of a tag, We check
830 the state machine to figure out what kind of text it is, and then append it to the
831 corresponding buffer.
833 static void characterHandler(
834 void *session, const xmlChar *ch, int len) {
836 const char* p = (const char*) ch;
838 transport_session* ses = (transport_session*) session;
839 if( ! ses ) { return; }
841 jabber_machine* machine = ses->state_machine;
843 /* set the various message parts */
844 if( machine->in_message ) {
846 if( machine->in_message_body ) {
847 buffer_add_n( ses->body_buffer, p, len );
850 if( machine->in_subject ) {
851 buffer_add_n( ses->subject_buffer, p, len );
854 if( machine->in_thread ) {
855 buffer_add_n( ses->thread_buffer, p, len );
859 /* set the presence status */
860 if( machine->in_presence && ses->state_machine->in_status ) {
861 buffer_add_n( ses->status_buffer, p, len );
864 if( machine->in_error ) {
866 osrfLogWarning( OSRF_LOG_MARK, "ERROR XML fragment: %s\n", ch );
872 @brief Log a warning from the XML parser.
873 @param session Pointer to a transport_session, cast to a void pointer (not used).
874 @param msg Pointer to a printf-style format string. Subsequent messages, if any, are
875 formatted and inserted into the expanded string.
877 The XML parser calls this function when it wants to warn about something in the XML.
879 static void parseWarningHandler( void *session, const char* msg, ... ) {
880 VA_LIST_TO_STRING(msg);
881 osrfLogWarning( OSRF_LOG_MARK, VA_BUF );
885 @brief Log an error from the XML parser.
886 @param session Pointer to a transport_session, cast to a void pointer (not used).
887 @param msg Pointer to a printf-style format string. Subsequent messages, if any, are
888 formatted and inserted into the expanded string.
890 The XML parser calls this function when it finds an error in the XML.
892 static void parseErrorHandler( void *session, const char* msg, ... ){
893 VA_LIST_TO_STRING(msg);
894 osrfLogError( OSRF_LOG_MARK, VA_BUF );
898 @brief Disconnect from Jabber, and close the socket.
899 @param session Pointer to the transport_session to be disconnected.
900 @return 0 in all cases.
902 int session_disconnect( transport_session* session ) {
903 if( session && session->sock_id != 0 ) {
904 socket_send(session->sock_id, "</stream:stream>");
905 socket_disconnect(session->sock_mgr, session->sock_id);
906 session->sock_id = 0;