Various cleanups in transport_session.c:
[OpenSRF.git] / src / libopensrf / transport_session.c
1 #include <opensrf/transport_session.h>
2
3 /**
4         @file transport_session.c
5         @brief Routines to manage a connection to a Jabber server.
6
7         In all cases, a transport_session acts as a client with regard to Jabber.
8 */
9
10 // ---------------------------------------------------------------------------------
11 // Callback for handling the startElement event.  Much of the jabber logic occurs
12 // in this and the characterHandler callbacks.
13 // Here we check for the various top level jabber elements: body, iq, etc.
14 // ---------------------------------------------------------------------------------
15 static void startElementHandler(
16                 void *session, const xmlChar *name, const xmlChar **atts);
17
18 // ---------------------------------------------------------------------------------
19 // Callback for handling the endElement event.  Updates the Jabber state machine
20 // to let us know the element is over.
21 // ---------------------------------------------------------------------------------
22 static void endElementHandler( void *session, const xmlChar *name);
23
24 // ---------------------------------------------------------------------------------
25 // This is where we extract XML text content.  In particular, this is useful for
26 // extracting Jabber message bodies.
27 // ---------------------------------------------------------------------------------
28 static void characterHandler(
29                 void *session, const xmlChar *ch, int len);
30
31 static void parseWarningHandler( void *session, const char* msg, ... );
32 static void parseErrorHandler( void *session, const char* msg, ... );
33
34 // ---------------------------------------------------------------------------------
35 // Tells the SAX parser which functions will be used as event callbacks
36 // ---------------------------------------------------------------------------------
37 static xmlSAXHandler SAXHandlerStruct = {
38    NULL,                                                        /* internalSubset */
39    NULL,                                                        /* isStandalone */
40    NULL,                                                        /* hasInternalSubset */
41    NULL,                                                        /* hasExternalSubset */
42    NULL,                                                        /* resolveEntity */
43    NULL,                                                        /* getEntity */
44    NULL,                                                        /* entityDecl */
45    NULL,                                                        /* notationDecl */
46    NULL,                                                        /* attributeDecl */
47    NULL,                                                        /* elementDecl */
48    NULL,                                                        /* unparsedEntityDecl */
49    NULL,                                                        /* setDocumentLocator */
50    NULL,                                                        /* startDocument */
51    NULL,                                                        /* endDocument */
52    startElementHandler,         /* startElement */
53    endElementHandler,           /* endElement */
54    NULL,                                                        /* reference */
55    characterHandler,                    /* characters */
56    NULL,                                                        /* ignorableWhitespace */
57    NULL,                                                        /* processingInstruction */
58    NULL,                                                        /* comment */
59    parseWarningHandler,         /* xmlParserWarning */
60    parseErrorHandler,           /* xmlParserError */
61    NULL,                                                        /* xmlParserFatalError : unused */
62    NULL,                                                        /* getParameterEntity */
63    NULL,                                                        /* cdataBlock; */
64    NULL,                                                        /* externalSubset; */
65    1,
66    NULL,
67    NULL,                                                        /* startElementNs */
68    NULL,                                                        /* endElementNs */
69    NULL                                                 /* xmlStructuredErrorFunc */
70 };
71
72 // ---------------------------------------------------------------------------------
73 // Our SAX handler pointer.
74 // ---------------------------------------------------------------------------------
75 static const xmlSAXHandlerPtr SAXHandler = &SAXHandlerStruct;
76
77 #ifndef HOST_NAME_MAX
78 #define HOST_NAME_MAX 256
79 #endif
80
81 static void grab_incoming(void* blob, socket_manager* mgr, int sockid, char* data, int parent);
82 static int reset_session_buffers( transport_session* session );
83 static char* get_xml_attr( const xmlChar** atts, const char* attr_name );
84
85 /**
86         @brief Allocate and initialize a transport_session.
87         @param server Hostname or IP address where the Jabber server resides.
88         @param port Port used for connecting to Jabber (0 if using UNIX domain socket).
89         @param unix_path Name of Jabber's socket in file system (if using UNIX domain socket).
90         @param user_data An opaque pointer stored on behalf of the calling code.
91         @param component Boolean; true if we're a component.
92         @return Pointer to a newly allocated transport_session.
93
94         This function initializes memory but does not open any sockets or otherwise access
95         the network.
96
97         If @a port is greater than zero, we will use TCP to connect to Jabber, and ignore
98         @a unix_path.  Otherwise we will open a UNIX domain socket using @a unix_path.
99
100         The calling code is responsible for freeing the transport_session by calling
101         session_free().
102 */
103 transport_session* init_transport( const char* server,
104         int port, const char* unix_path, void* user_data, int component ) {
105
106         if( ! server )
107                 server = "";
108
109         /* create the session struct */
110         transport_session* session =
111                 (transport_session*) safe_malloc( sizeof(transport_session) );
112
113         session->user_data = user_data;
114
115         session->component = component;
116
117         /* initialize the data buffers */
118         session->body_buffer            = buffer_init( JABBER_BODY_BUFSIZE );
119         session->subject_buffer         = buffer_init( JABBER_SUBJECT_BUFSIZE );
120         session->thread_buffer          = buffer_init( JABBER_THREAD_BUFSIZE );
121         session->from_buffer            = buffer_init( JABBER_JID_BUFSIZE );
122         session->status_buffer          = buffer_init( JABBER_STATUS_BUFSIZE );
123         session->recipient_buffer       = buffer_init( JABBER_JID_BUFSIZE );
124         session->message_error_type = buffer_init( JABBER_JID_BUFSIZE );
125         session->session_id                     = buffer_init( 64 );
126
127         session->message_error_code = 0;
128
129         /* for OpenSRF extensions */
130         session->router_to_buffer       = buffer_init( JABBER_JID_BUFSIZE );
131         session->router_from_buffer     = buffer_init( JABBER_JID_BUFSIZE );
132         session->osrf_xid_buffer        = buffer_init( JABBER_JID_BUFSIZE );
133         session->router_class_buffer    = buffer_init( JABBER_JID_BUFSIZE );
134         session->router_command_buffer  = buffer_init( JABBER_JID_BUFSIZE );
135
136         session->router_broadcast   = 0;
137
138         /* initialize the jabber state machine */
139         session->state_machine = (jabber_machine*) safe_malloc( sizeof(jabber_machine) );
140         session->state_machine->connected        = 0;
141         session->state_machine->connecting       = 0;
142         session->state_machine->in_message       = 0;
143         session->state_machine->in_message_body  = 0;
144         session->state_machine->in_thread        = 0;
145         session->state_machine->in_subject       = 0;
146         session->state_machine->in_error         = 0;
147         session->state_machine->in_message_error = 0;
148         session->state_machine->in_iq            = 0;
149         session->state_machine->in_presence      = 0;
150         session->state_machine->in_status        = 0;
151
152         /* initialize the sax push parser */
153         session->parser_ctxt = xmlCreatePushParserCtxt(SAXHandler, session, "", 0, NULL);
154
155         /* initialize the socket_manager structure */
156         session->sock_mgr = (socket_manager*) safe_malloc( sizeof(socket_manager) );
157
158         session->sock_mgr->data_received = &grab_incoming;
159         session->sock_mgr->on_socket_closed = NULL;
160         session->sock_mgr->socket = NULL;
161         session->sock_mgr->blob = session;
162
163         session->port = port;
164         session->server = strdup(server);
165         if(unix_path)
166                 session->unix_path = strdup(unix_path);
167         else session->unix_path = NULL;
168
169         session->sock_id = 0;
170         session->message_callback = NULL;
171
172         return session;
173 }
174
175
176 /**
177         @brief Destroy a transport_session, and close its socket.
178         @param session Pointer to the transport_session to be destroyed.
179         @return 1 if successful, or 0 if not.
180
181         The only error condition is a NULL pointer argument.
182 */
183 int session_free( transport_session* session ) {
184         if( ! session ) { return 0; }
185
186         if( session->sock_id )
187                 session_disconnect( session );
188
189         if(session->sock_mgr)
190                 socket_manager_free(session->sock_mgr);
191
192         if( session->state_machine ) free( session->state_machine );
193         if( session->parser_ctxt) {
194                 xmlFreeDoc( session->parser_ctxt->myDoc );
195                 xmlFreeParserCtxt(session->parser_ctxt);
196         }
197
198         xmlCleanupCharEncodingHandlers();
199         xmlDictCleanup();
200         xmlCleanupParser();
201
202         buffer_free(session->body_buffer);
203         buffer_free(session->subject_buffer);
204         buffer_free(session->thread_buffer);
205         buffer_free(session->from_buffer);
206         buffer_free(session->recipient_buffer);
207         buffer_free(session->status_buffer);
208         buffer_free(session->message_error_type);
209         buffer_free(session->router_to_buffer);
210         buffer_free(session->router_from_buffer);
211         buffer_free(session->osrf_xid_buffer);
212         buffer_free(session->router_class_buffer);
213         buffer_free(session->router_command_buffer);
214         buffer_free(session->session_id);
215
216         free(session->server);
217         free(session->unix_path);
218
219         free( session );
220         return 1;
221 }
222
223
224 /**
225         @brief Wait on the client socket connected to Jabber, and process any resulting input.
226         @param session Pointer to the transport_session.
227         @param timeout How seconds to wait before timing out (see notes).
228         @return 0 if successful, or -1 if a timeout or other error occurs, or if the server
229                 closes the connection at the other end.
230
231         If @a timeout is -1, wait indefinitely for input activity to appear.  If @a timeout is
232         zero, don't wait at all.  If @a timeout is positive, wait that number of seconds
233         before timing out.  If @a timeout has a negative value other than -1, the results are not
234         well defined.
235
236         Read all available input from the socket and pass it through grab_incoming() (a previously
237         designated callback function).  There is no guarantee that we will get a complete message
238         from a single call.
239 */
240 int session_wait( transport_session* session, int timeout ) {
241         if( ! session || ! session->sock_mgr ) {
242                 return 0;
243         }
244
245         int ret =  socket_wait( session->sock_mgr, timeout, session->sock_id );
246
247         if( ret ) {
248                 osrfLogDebug(OSRF_LOG_MARK, "socket_wait returned error code %d", ret);
249                 session->state_machine->connected = 0;
250         }
251         return ret;
252 }
253
254 /**
255         @brief Wrap a message in XML and send it to Jabber.
256         @param session Pointer to the transport_session.
257         @param msg Pointer to a transport_message enclosing the message.
258         @return 0 if successful, or -1 upon error.
259 */
260 int session_send_msg(
261                 transport_session* session, transport_message* msg ) {
262
263         if( ! session ) { return -1; }
264
265         if( ! session->state_machine->connected ) {
266                 osrfLogWarning(OSRF_LOG_MARK, "State machine is not connected in send_msg()");
267                 return -1;
268         }
269
270         message_prepare_xml( msg );
271         return socket_send( session->sock_id, msg->msg_xml );
272
273 }
274
275
276 /**
277         @brief Connect to the Jabber server as a client and open a Jabber session.
278         @param session Pointer to a transport_session.
279         @param username Jabber user name.
280         @param password Jabber password.
281         @param resource name of Jabber resource.
282         @param connect_timeout Timeout interval, in seconds, for receiving data (see notes).
283         @param auth_type An enum: either AUTH_PLAIN or AUTH_DIGEST (see notes).
284         @return 1 if successful, or 0 upon error.
285
286         If @a connect_timeout is -1, wait indefinitely for input activity to appear.  If
287         @a connect_timeout is zero, don't wait at all.  If @a timeout is positive, wait that
288         number of seconds before timing out.  If @a connect_timeout has a negative value other
289         than -1, the results are not well defined.
290
291         If we connect as a Jabber component, we send the password as an SHA1 hash.  Otherwise
292         we look at the @a auth_type.  If it's AUTH_PLAIN, we send the password as plaintext; if
293         it's AUTH_DIGEST, we send it as a hash.
294
295         At this writing, we only use AUTH_DIGEST.
296 */
297 int session_connect( transport_session* session,
298                 const char* username, const char* password,
299                 const char* resource, int connect_timeout, enum TRANSPORT_AUTH_TYPE auth_type ) {
300
301         int size1 = 0;
302         int size2 = 0;
303
304         if( ! session ) {
305                 osrfLogWarning(OSRF_LOG_MARK, "session is null in session_connect()" );
306                 return 0;
307         }
308
309         if( session->sock_id != 0 ) {
310                 osrfLogWarning(OSRF_LOG_MARK, "transport session is already open, on socket %d",
311                         session->sock_id );
312                 return 0;
313         }
314
315         // Open a client socket connecting to the Jabber server
316         if(session->port > 0) {   // use TCP
317                 session->sock_id = socket_open_tcp_client( 
318                                 session->sock_mgr, session->port, session->server );
319                 if( session->sock_id <= 0 ) {
320                         session->sock_id = 0;
321                         return 0;
322                 }
323         } else if(session->unix_path != NULL) {  // use UNIX domain
324                 session->sock_id = socket_open_unix_client( session->sock_mgr, session->unix_path );
325                 if( session->sock_id <= 0 ) {
326                         session->sock_id = 0;
327                         return 0;
328                 }
329         }
330         else {
331                 osrfLogWarning( OSRF_LOG_MARK, "Can't open session: no port or unix path" );
332                 return 0;
333         }
334
335         const char* server = session->server;
336
337         if( session->component ) {
338
339                 /* the first Jabber connect stanza */
340                 char our_hostname[HOST_NAME_MAX + 1] = "";
341                 gethostname(our_hostname, sizeof(our_hostname) );
342                 our_hostname[HOST_NAME_MAX] = '\0';
343                 size1 = 150 + strlen( username ) + strlen( our_hostname );
344                 char stanza1[ size1 ];
345                 snprintf( stanza1, sizeof(stanza1),
346                                 "<stream:stream version='1.0' xmlns:stream='http://etherx.jabber.org/streams' "
347                                 "xmlns='jabber:component:accept' to='%s' from='%s' xml:lang='en'>",
348                                 username, our_hostname );
349
350                 /* send the first stanze */
351                 session->state_machine->connecting = CONNECTING_1;
352
353                 if( socket_send( session->sock_id, stanza1 ) ) {
354                         osrfLogWarning(OSRF_LOG_MARK, "error sending");
355                         socket_disconnect( session->sock_mgr, session->sock_id );
356                         session->sock_id = 0;
357                         return 0;
358                 }
359
360                 /* wait for reply */
361                 socket_wait(session->sock_mgr, connect_timeout, session->sock_id);
362
363                 /* server acknowledges our existence, now see if we can login */
364                 if( session->state_machine->connecting == CONNECTING_2 ) {
365
366                         int ss = session->session_id->n_used + strlen(password) + 5;
367                         char hashstuff[ss];
368                         snprintf( hashstuff, sizeof(hashstuff), "%s%s", session->session_id->buf, password );
369
370                         char* hash = shahash( hashstuff );
371                         size2 = 100 + strlen( hash );
372                         char stanza2[ size2 ];
373                         snprintf( stanza2, sizeof(stanza2), "<handshake>%s</handshake>", hash );
374
375                         if( socket_send( session->sock_id, stanza2 )  ) {
376                                 osrfLogWarning(OSRF_LOG_MARK, "error sending");
377                                 socket_disconnect( session->sock_mgr, session->sock_id );
378                                 session->sock_id = 0;
379                                 return 0;
380                         }
381                 }
382
383         } else { /* we're not a component */
384
385                 /* the first Jabber connect stanza */
386                 size1 = 100 + strlen( server );
387                 char stanza1[ size1 ];
388                 snprintf( stanza1, sizeof(stanza1),
389                                 "<stream:stream to='%s' xmlns='jabber:client' "
390                                 "xmlns:stream='http://etherx.jabber.org/streams'>",
391                         server );
392
393                 /* send the first stanze */
394                 session->state_machine->connecting = CONNECTING_1;
395                 if( socket_send( session->sock_id, stanza1 ) ) {
396                         osrfLogWarning(OSRF_LOG_MARK, "error sending");
397                         socket_disconnect( session->sock_mgr, session->sock_id );
398                         session->sock_id = 0;
399                         return 0;
400                 }
401
402
403                 /* wait for reply */
404                 socket_wait( session->sock_mgr, connect_timeout, session->sock_id ); /* make the timeout smarter XXX */
405
406                 if( auth_type == AUTH_PLAIN ) {
407
408                         /* the second jabber connect stanza including login info*/
409                         size2 = 150 + strlen( username ) + strlen( password ) + strlen( resource );
410                         char stanza2[ size2 ];
411                         snprintf( stanza2, sizeof(stanza2),
412                                         "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
413                                         "<username>%s</username><password>%s</password><resource>%s</resource></query></iq>",
414                                         username, password, resource );
415
416                         /* server acknowledges our existence, now see if we can login */
417                         if( session->state_machine->connecting == CONNECTING_2 ) {
418                                 if( socket_send( session->sock_id, stanza2 )  ) {
419                                         osrfLogWarning(OSRF_LOG_MARK, "error sending");
420                                         socket_disconnect( session->sock_mgr, session->sock_id );
421                                         session->sock_id = 0;
422                                         return 0;
423                                 }
424                         }
425
426                 } else if( auth_type == AUTH_DIGEST ) {
427
428                         int ss = session->session_id->n_used + strlen(password) + 5;
429                         char hashstuff[ss];
430                         snprintf( hashstuff, sizeof(hashstuff), "%s%s", session->session_id->buf, password );
431
432                         char* hash = shahash( hashstuff );
433
434                         /* the second jabber connect stanza including login info*/
435                         size2 = 150 + strlen( username ) + strlen( hash ) + strlen(resource);
436                         char stanza2[ size2 ];
437                         snprintf( stanza2, sizeof(stanza2),
438                                         "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
439                                         "<username>%s</username><digest>%s</digest><resource>%s</resource></query></iq>",
440                                         username, hash, resource );
441
442                         /* server acknowledges our existence, now see if we can login */
443                         if( session->state_machine->connecting == CONNECTING_2 ) {
444                                 if( socket_send( session->sock_id, stanza2 )  ) {
445                                         osrfLogWarning(OSRF_LOG_MARK, "error sending");
446                                         socket_disconnect( session->sock_mgr, session->sock_id );
447                                         session->sock_id = 0;
448                                         return 0;
449                                 }
450                         }
451
452                 } else {
453                         osrfLogWarning(OSRF_LOG_MARK, "Invalid auth_type parameter: %d",
454                                         (int) auth_type );
455                         socket_disconnect( session->sock_mgr, session->sock_id );
456                         session->sock_id = 0;
457                         return 0;
458                 }
459
460         } // not component
461
462
463         /* wait for reply to login request */
464         socket_wait( session->sock_mgr, connect_timeout, session->sock_id );
465
466         if( session->state_machine->connected ) {
467                 /* yar! */
468                 return 1;
469         } else {
470                 socket_disconnect( session->sock_mgr, session->sock_id );
471                 session->sock_id = 0;
472                 return 0;
473         }
474 }
475
476 // ---------------------------------------------------------------------------------
477 // TCP data callback.  Takes data from the socket handler and pushes it directly
478 // into the push parser
479 // ---------------------------------------------------------------------------------
480 static void grab_incoming(void* blob, socket_manager* mgr, int sockid, char* data, int parent) {
481         transport_session* ses = (transport_session*) blob;
482         if( ! ses ) { return; }
483         xmlParseChunk(ses->parser_ctxt, data, strlen(data), 0);
484 }
485
486
487 static void startElementHandler(
488         void *session, const xmlChar *name, const xmlChar **atts) {
489
490         transport_session* ses = (transport_session*) session;
491         if( ! ses ) { return; }
492
493
494         if( strcmp( (char*) name, "message" ) == 0 ) {
495                 ses->state_machine->in_message = 1;
496                 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
497                 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
498                 buffer_add( ses->router_from_buffer, get_xml_attr( atts, "router_from" ) );
499                 buffer_add( ses->osrf_xid_buffer, get_xml_attr( atts, "osrf_xid" ) );
500                 buffer_add( ses->router_to_buffer, get_xml_attr( atts, "router_to" ) );
501                 buffer_add( ses->router_class_buffer, get_xml_attr( atts, "router_class" ) );
502                 buffer_add( ses->router_command_buffer, get_xml_attr( atts, "router_command" ) );
503                 char* broadcast = get_xml_attr( atts, "broadcast" );
504                 if( broadcast )
505                         ses->router_broadcast = atoi( broadcast );
506
507                 return;
508         }
509
510         if( ses->state_machine->in_message ) {
511
512                 if( strcmp( (char*) name, "body" ) == 0 ) {
513                         ses->state_machine->in_message_body = 1;
514                         return;
515                 }
516
517                 if( strcmp( (char*) name, "subject" ) == 0 ) {
518                         ses->state_machine->in_subject = 1;
519                         return;
520                 }
521
522                 if( strcmp( (char*) name, "thread" ) == 0 ) {
523                         ses->state_machine->in_thread = 1;
524                         return;
525                 }
526
527         }
528
529         if( strcmp( (char*) name, "presence" ) == 0 ) {
530                 ses->state_machine->in_presence = 1;
531                 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
532                 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
533                 return;
534         }
535
536         if( strcmp( (char*) name, "status" ) == 0 ) {
537                 ses->state_machine->in_status = 1;
538                 return;
539         }
540
541
542         if( strcmp( (char*) name, "stream:error" ) == 0 ) {
543                 ses->state_machine->in_error = 1;
544                 ses->state_machine->connected = 0;
545                 osrfLogWarning(  OSRF_LOG_MARK, "Received <stream:error> message from Jabber server" );
546                 return;
547         }
548
549
550         /* first server response from a connect attempt */
551         if( strcmp( (char*) name, "stream:stream" ) == 0 ) {
552                 if( ses->state_machine->connecting == CONNECTING_1 ) {
553                         ses->state_machine->connecting = CONNECTING_2;
554                         buffer_add( ses->session_id, get_xml_attr(atts, "id") );
555                 }
556         }
557
558         if( strcmp( (char*) name, "handshake" ) == 0 ) {
559                 ses->state_machine->connected = 1;
560                 ses->state_machine->connecting = 0;
561                 return;
562         }
563
564
565         if( strcmp( (char*) name, "error" ) == 0 ) {
566                 ses->state_machine->in_message_error = 1;
567                 buffer_add( ses->message_error_type, get_xml_attr( atts, "type" ) );
568                 ses->message_error_code = atoi( get_xml_attr( atts, "code" ) );
569                 osrfLogInfo( OSRF_LOG_MARK, "Received <error> message with type %s and code %s",
570                         get_xml_attr( atts, "type"), get_xml_attr( atts, "code") );
571                 return;
572         }
573
574         if( strcmp( (char*) name, "iq" ) == 0 ) {
575                 ses->state_machine->in_iq = 1;
576
577                 if( strcmp( get_xml_attr(atts, "type"), "result") == 0
578                                 && ses->state_machine->connecting == CONNECTING_2 ) {
579                         ses->state_machine->connected = 1;
580                         ses->state_machine->connecting = 0;
581                         return;
582                 }
583
584                 if( strcmp( get_xml_attr(atts, "type"), "error") == 0 ) {
585                         osrfLogWarning( OSRF_LOG_MARK,  "Error connecting to jabber" );
586                         return;
587                 }
588         }
589 }
590
591 // ------------------------------------------------------------------
592 // Returns the value of the given XML attribute
593 // The xmlChar** construct is commonly returned from SAX event
594 // handlers.  Pass that in with the name of the attribute you want
595 // to retrieve.
596 // ------------------------------------------------------------------
597 static char* get_xml_attr( const xmlChar** atts, const char* attr_name ) {
598         int i;
599         if (atts != NULL) {
600                 for(i = 0;(atts[i] != NULL);i++) {
601                         if( strcmp( (char*) atts[i++], attr_name ) == 0 ) {
602                                 if( atts[i] != NULL ) {
603                                         return (char*) atts[i];
604                                 }
605                         }
606                 }
607         }
608         return NULL;
609 }
610
611
612 // ------------------------------------------------------------------
613 // See which tags are ending
614 // ------------------------------------------------------------------
615 static void endElementHandler( void *session, const xmlChar *name) {
616         transport_session* ses = (transport_session*) session;
617         if( ! ses ) { return; }
618
619         if( strcmp( (char*) name, "message" ) == 0 ) {
620
621
622                 /* pass off the message info the callback */
623                 if( ses->message_callback ) {
624
625                         /* here it's ok to pass in the raw buffers because
626                                 message_init allocates new space for the chars
627                                 passed in */
628                         transport_message* msg =  message_init(
629                                 ses->body_buffer->buf,
630                                 ses->subject_buffer->buf,
631                                 ses->thread_buffer->buf,
632                                 ses->recipient_buffer->buf,
633                                 ses->from_buffer->buf );
634
635                         message_set_router_info( msg,
636                                 ses->router_from_buffer->buf,
637                                 ses->router_to_buffer->buf,
638                                 ses->router_class_buffer->buf,
639                                 ses->router_command_buffer->buf,
640                                 ses->router_broadcast );
641
642          message_set_osrf_xid( msg, ses->osrf_xid_buffer->buf );
643
644                         if( ses->message_error_type->n_used > 0 ) {
645                                 set_msg_error( msg, ses->message_error_type->buf, ses->message_error_code );
646                         }
647
648                         if( msg == NULL ) { return; }
649                         ses->message_callback( ses->user_data, msg );
650                 }
651
652                 ses->state_machine->in_message = 0;
653                 reset_session_buffers( session );
654                 return;
655         }
656
657         if( strcmp( (const char*) name, "body" ) == 0 ) {
658                 ses->state_machine->in_message_body = 0;
659                 return;
660         }
661
662         if( strcmp( (const char*) name, "subject" ) == 0 ) {
663                 ses->state_machine->in_subject = 0;
664                 return;
665         }
666
667         if( strcmp( (const char*) name, "thread" ) == 0 ) {
668                 ses->state_machine->in_thread = 0;
669                 return;
670         }
671
672         if( strcmp( (const char*) name, "iq" ) == 0 ) {
673                 ses->state_machine->in_iq = 0;
674                 if( ses->message_error_code > 0 ) {
675                         osrfLogWarning( OSRF_LOG_MARK,  "Error in IQ packet: code %d",  ses->message_error_code );
676                         osrfLogWarning( OSRF_LOG_MARK,  "Error 401 means not authorized" );
677                 }
678                 reset_session_buffers( session );
679                 return;
680         }
681
682         if( strcmp( (const char*) name, "presence" ) == 0 ) {
683                 ses->state_machine->in_presence = 0;
684                 /*
685                 if( ses->presence_callback ) {
686                         // call the callback with the status, etc.
687                 }
688                 */
689                 reset_session_buffers( session );
690                 return;
691         }
692
693         if( strcmp( (const char*) name, "status" ) == 0 ) {
694                 ses->state_machine->in_status = 0;
695                 return;
696         }
697
698         if( strcmp( (const char*) name, "error" ) == 0 ) {
699                 ses->state_machine->in_message_error = 0;
700                 return;
701         }
702
703         if( strcmp( (const char*) name, "error:error" ) == 0 ) {
704                 ses->state_machine->in_error = 0;
705                 return;
706         }
707 }
708
709 static int reset_session_buffers( transport_session* ses ) {
710         buffer_reset( ses->body_buffer );
711         buffer_reset( ses->subject_buffer );
712         buffer_reset( ses->thread_buffer );
713         buffer_reset( ses->from_buffer );
714         buffer_reset( ses->recipient_buffer );
715         buffer_reset( ses->router_from_buffer );
716         buffer_reset( ses->osrf_xid_buffer );
717         buffer_reset( ses->router_to_buffer );
718         buffer_reset( ses->router_class_buffer );
719         buffer_reset( ses->router_command_buffer );
720         buffer_reset( ses->message_error_type );
721         buffer_reset( ses->session_id );
722
723         return 1;
724 }
725
726 // ------------------------------------------------------------------
727 // takes data out of the body of the message and pushes it into
728 // the appropriate buffer
729 // ------------------------------------------------------------------
730 static void characterHandler(
731                 void *session, const xmlChar *ch, int len) {
732
733         const char* p = (const char*) ch;
734
735         transport_session* ses = (transport_session*) session;
736         if( ! ses ) { return; }
737
738         /* set the various message parts */
739         if( ses->state_machine->in_message ) {
740
741                 if( ses->state_machine->in_message_body ) {
742                         buffer_add_n( ses->body_buffer, p, len );
743                 }
744
745                 if( ses->state_machine->in_subject ) {
746                         buffer_add_n( ses->subject_buffer, p, len );
747                 }
748
749                 if( ses->state_machine->in_thread ) {
750                         buffer_add_n( ses->thread_buffer, p, len );
751                 }
752         }
753
754         /* set the presence status */
755         if( ses->state_machine->in_presence && ses->state_machine->in_status ) {
756                 buffer_add_n( ses->status_buffer, p, len );
757         }
758
759         if( ses->state_machine->in_error ) {
760                 /* for now... */
761                 osrfLogWarning( OSRF_LOG_MARK,  "ERROR XML fragment: %s\n", ch );
762         }
763
764 }
765
766 /* XXX change to warning handlers */
767 static void  parseWarningHandler( void *session, const char* msg, ... ) {
768
769         va_list args;
770         va_start(args, msg);
771         fprintf(stdout, "transport_session XML WARNING");
772         vfprintf(stdout, msg, args);
773         va_end(args);
774         fprintf(stderr, "XML WARNING: %s\n", msg );
775 }
776
777 static void  parseErrorHandler( void *session, const char* msg, ... ){
778
779         va_list args;
780         va_start(args, msg);
781         fprintf(stdout, "transport_session XML ERROR");
782         vfprintf(stdout, msg, args);
783         va_end(args);
784         fprintf(stderr, "XML ERROR: %s\n", msg );
785
786 }
787
788 /**
789         @brief Disconnect from Jabber, and close the socket.
790         @param session Pointer to the transport_session to be disconnected.
791         @return 0 in all cases.
792 */
793 int session_disconnect( transport_session* session ) {
794         if( session && session->sock_id != 0 ) {
795                 socket_send(session->sock_id, "</stream:stream>");
796                 socket_disconnect(session->sock_mgr, session->sock_id);
797                 session->sock_id = 0;
798         }
799         return 0;
800 }
801