]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/libopensrf/transport_session.c
f0f70f1cf2b080da03cfe06648d676f42a50e25b
[OpenSRF.git] / src / libopensrf / transport_session.c
1 #include <opensrf/transport_session.h>
2
3 #ifndef HOST_NAME_MAX
4 #define HOST_NAME_MAX 256
5 #endif
6
7 static char* get_xml_attr( const xmlChar** atts, const char* attr_name );
8
9 // ---------------------------------------------------------------------------------
10 // returns a built and allocated transport_session object.
11 // This codes does no network activity, only memory initilization
12 // ---------------------------------------------------------------------------------
13 transport_session* init_transport(  const char* server, 
14         int port, const char* unix_path, void* user_data, int component ) {
15
16         /* create the session struct */
17         transport_session* session = 
18                 (transport_session*) safe_malloc( sizeof(transport_session) );
19
20         session->user_data = user_data;
21
22         session->component = component;
23
24         /* initialize the data buffers */
25         session->body_buffer                    = buffer_init( JABBER_BODY_BUFSIZE );
26         session->subject_buffer         = buffer_init( JABBER_SUBJECT_BUFSIZE );
27         session->thread_buffer          = buffer_init( JABBER_THREAD_BUFSIZE );
28         session->from_buffer                    = buffer_init( JABBER_JID_BUFSIZE );
29         session->status_buffer          = buffer_init( JABBER_STATUS_BUFSIZE );
30         session->recipient_buffer       = buffer_init( JABBER_JID_BUFSIZE );
31         session->message_error_type = buffer_init( JABBER_JID_BUFSIZE );
32         session->session_id                     = buffer_init( 64 );
33
34         session->message_error_code = 0;
35
36         /* for OpenSRF extensions */
37         session->router_to_buffer               = buffer_init( JABBER_JID_BUFSIZE );
38         session->router_from_buffer     = buffer_init( JABBER_JID_BUFSIZE );
39         session->osrf_xid_buffer        = buffer_init( JABBER_JID_BUFSIZE );
40         session->router_class_buffer    = buffer_init( JABBER_JID_BUFSIZE );
41         session->router_command_buffer  = buffer_init( JABBER_JID_BUFSIZE );
42
43         session->router_broadcast   = 0;
44
45         if(     session->body_buffer            == NULL || session->subject_buffer       == NULL        ||
46                         session->thread_buffer  == NULL || session->from_buffer          == NULL        ||
47                         session->status_buffer  == NULL || session->recipient_buffer == NULL ||
48                         session->router_to_buffer       == NULL || session->router_from_buffer   == NULL ||
49                         session->router_class_buffer == NULL || session->router_command_buffer == NULL ||
50                         session->session_id == NULL ) { 
51
52                 osrfLogError(OSRF_LOG_MARK,  "init_transport(): buffer_init returned NULL" );
53                 buffer_free( session->body_buffer );
54                 buffer_free( session->subject_buffer );
55                 buffer_free( session->thread_buffer );
56                 buffer_free( session->from_buffer );
57                 buffer_free( session->status_buffer );
58                 buffer_free( session->recipient_buffer );
59                 buffer_free( session->router_to_buffer );
60                 buffer_free( session->router_from_buffer );
61                 buffer_free( session->router_class_buffer );
62                 buffer_free( session->router_command_buffer );
63                 buffer_free( session->session_id );
64                 free( session );
65                 return 0;
66         }
67
68
69         /* initialize the jabber state machine */
70         session->state_machine = (jabber_machine*) safe_malloc( sizeof(jabber_machine) );
71         session->state_machine->connected        = 0;
72         session->state_machine->connecting       = 0;
73         session->state_machine->in_message       = 0;
74         session->state_machine->in_message_body  = 0;
75         session->state_machine->in_thread        = 0;
76         session->state_machine->in_subject       = 0;
77         session->state_machine->in_error         = 0;
78         session->state_machine->in_message_error = 0;
79         session->state_machine->in_iq            = 0;
80         session->state_machine->in_presence      = 0;
81         session->state_machine->in_status        = 0;
82
83         /* initialize the sax push parser */
84         session->parser_ctxt = xmlCreatePushParserCtxt(SAXHandler, session, "", 0, NULL);
85
86         /* initialize the transport_socket structure */
87         session->sock_mgr = (socket_manager*) safe_malloc( sizeof(socket_manager) );
88
89         session->sock_mgr->data_received = &grab_incoming;
90         session->sock_mgr->on_socket_closed = NULL;
91         session->sock_mgr->socket = NULL;
92         session->sock_mgr->blob = session;
93         
94         session->port = port;
95         session->server = strdup(server);
96         if(unix_path)   
97                 session->unix_path = strdup(unix_path);
98         else session->unix_path = NULL;
99
100         session->sock_id = 0;
101         session->message_callback = NULL;
102
103         return session;
104 }
105
106
107
108 /* XXX FREE THE BUFFERS */
109 int session_free( transport_session* session ) {
110         if( ! session ) { return 0; }
111
112         if(session->sock_mgr)
113                 socket_manager_free(session->sock_mgr);
114
115         if( session->state_machine ) free( session->state_machine );
116         if( session->parser_ctxt) {
117                 xmlFreeDoc( session->parser_ctxt->myDoc );
118                 xmlFreeParserCtxt(session->parser_ctxt);
119         }
120
121         xmlCleanupCharEncodingHandlers();
122         xmlDictCleanup();
123         xmlCleanupParser();
124
125         buffer_free(session->body_buffer);
126         buffer_free(session->subject_buffer);
127         buffer_free(session->thread_buffer);
128         buffer_free(session->from_buffer);
129         buffer_free(session->recipient_buffer);
130         buffer_free(session->status_buffer);
131         buffer_free(session->message_error_type);
132         buffer_free(session->router_to_buffer);
133         buffer_free(session->router_from_buffer);
134         buffer_free(session->osrf_xid_buffer);
135         buffer_free(session->router_class_buffer);
136         buffer_free(session->router_command_buffer);
137         buffer_free(session->session_id);
138
139         free(session->server);
140         free(session->unix_path);
141
142         free( session );
143         return 1;
144 }
145
146
147 int session_wait( transport_session* session, int timeout ) {
148         if( ! session || ! session->sock_mgr ) {
149                 return 0;
150         }
151
152         int ret =  socket_wait( session->sock_mgr, timeout, session->sock_id );
153
154         if( ret ) {
155                 osrfLogDebug(OSRF_LOG_MARK, "socket_wait returned error code %d", ret);
156                 session->state_machine->connected = 0;
157         }
158         return ret;
159 }
160
161 int session_send_msg( 
162                 transport_session* session, transport_message* msg ) {
163
164         if( ! session ) { return -1; }
165
166         if( ! session->state_machine->connected ) {
167                 osrfLogWarning(OSRF_LOG_MARK, "State machine is not connected in send_msg()");
168                 return -1;
169         }
170
171         message_prepare_xml( msg );
172         //tcp_send( session->sock_obj, msg->msg_xml );
173         return socket_send( session->sock_id, msg->msg_xml );
174
175 }
176
177
178 /* connects to server and connects to jabber */
179 int session_connect( transport_session* session, 
180                 const char* username, const char* password, 
181                 const char* resource, int connect_timeout, enum TRANSPORT_AUTH_TYPE auth_type ) {
182
183         int size1 = 0;
184         int size2 = 0;
185
186         if( ! session ) { 
187                 osrfLogWarning(OSRF_LOG_MARK,  "session is null in connect" );
188                 return 0; 
189         }
190
191
192         //char* server = session->sock_obj->server;
193         char* server = session->server;
194
195         if( ! session->sock_id ) {
196
197                 if(session->port > 0) {
198                         if( (session->sock_id = socket_open_tcp_client(
199                                 session->sock_mgr, session->port, session->server)) <= 0 ) 
200                         return 0;
201
202                 } else if(session->unix_path != NULL) {
203                         if( (session->sock_id = socket_open_unix_client(
204                                 session->sock_mgr, session->unix_path)) <= 0 ) 
205                         return 0;
206                 }
207                 else {
208                         osrfLogWarning( OSRF_LOG_MARK, "Can't open session: no port or unix path" );
209                         return 0;
210                 }
211         }
212
213         if( session->component ) {
214
215                 /* the first Jabber connect stanza */
216                 char our_hostname[HOST_NAME_MAX + 1] = "";
217                 gethostname(our_hostname, sizeof(our_hostname) );
218                 our_hostname[HOST_NAME_MAX] = '\0';
219                 size1 = 150 + strlen( server );
220                 char stanza1[ size1 ]; 
221                 snprintf( stanza1, sizeof(stanza1),
222                                 "<stream:stream version='1.0' xmlns:stream='http://etherx.jabber.org/streams' "
223                                 "xmlns='jabber:component:accept' to='%s' from='%s' xml:lang='en'>",
224                                 username, our_hostname );
225
226                 /* send the first stanze */
227                 session->state_machine->connecting = CONNECTING_1;
228
229 //              if( ! tcp_send( session->sock_obj, stanza1 ) ) {
230                 if( socket_send( session->sock_id, stanza1 ) ) {
231                         osrfLogWarning(OSRF_LOG_MARK, "error sending");
232                         return 0;
233                 }
234         
235                 /* wait for reply */
236                 //tcp_wait( session->sock_obj, connect_timeout ); /* make the timeout smarter XXX */
237                 socket_wait(session->sock_mgr, connect_timeout, session->sock_id);
238         
239                 /* server acknowledges our existence, now see if we can login */
240                 if( session->state_machine->connecting == CONNECTING_2 ) {
241         
242                         int ss = session->session_id->n_used + strlen(password) + 5;
243                         char hashstuff[ss];
244                         snprintf( hashstuff, sizeof(hashstuff), "%s%s", session->session_id->buf, password );
245
246                         char* hash = shahash( hashstuff );
247                         size2 = 100 + strlen( hash );
248                         char stanza2[ size2 ];
249                         snprintf( stanza2, sizeof(stanza2), "<handshake>%s</handshake>", hash );
250         
251                         //if( ! tcp_send( session->sock_obj, stanza2 )  ) {
252                         if( socket_send( session->sock_id, stanza2 )  ) {
253                                 osrfLogWarning(OSRF_LOG_MARK, "error sending");
254                                 return 0;
255                         }
256                 }
257
258         } else { /* we're not a component */
259
260                 /* the first Jabber connect stanza */
261                 size1 = 100 + strlen( server );
262                 char stanza1[ size1 ]; 
263                 snprintf( stanza1, sizeof(stanza1), 
264                                 "<stream:stream to='%s' xmlns='jabber:client' "
265                                 "xmlns:stream='http://etherx.jabber.org/streams'>",
266                         server );
267         
268
269                 /* send the first stanze */
270                 session->state_machine->connecting = CONNECTING_1;
271                 //if( ! tcp_send( session->sock_obj, stanza1 ) ) {
272                 if( socket_send( session->sock_id, stanza1 ) ) {
273                         osrfLogWarning(OSRF_LOG_MARK, "error sending");
274                         return 0;
275                 }
276
277
278                 /* wait for reply */
279                 //tcp_wait( session->sock_obj, connect_timeout ); /* make the timeout smarter XXX */
280                 socket_wait( session->sock_mgr, connect_timeout, session->sock_id ); /* make the timeout smarter XXX */
281
282                 if( auth_type == AUTH_PLAIN ) {
283
284                         /* the second jabber connect stanza including login info*/
285                         size2 = 150 + strlen( username ) + strlen(password) + strlen(resource);
286                         char stanza2[ size2 ];
287                         snprintf( stanza2, sizeof(stanza2), 
288                                         "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
289                                         "<username>%s</username><password>%s</password><resource>%s</resource></query></iq>",
290                                         username, password, resource );
291         
292                         /* server acknowledges our existence, now see if we can login */
293                         if( session->state_machine->connecting == CONNECTING_2 ) {
294                                 //if( ! tcp_send( session->sock_obj, stanza2 )  ) {
295                                 if( socket_send( session->sock_id, stanza2 )  ) {
296                                         osrfLogWarning(OSRF_LOG_MARK, "error sending");
297                                         return 0;
298                                 }
299                         }
300
301                 } else if( auth_type == AUTH_DIGEST ) {
302
303                         int ss = session->session_id->n_used + strlen(password) + 5;
304                         char hashstuff[ss];
305                         snprintf( hashstuff, sizeof(hashstuff), "%s%s", session->session_id->buf, password );
306
307                         char* hash = shahash( hashstuff );
308
309                         /* the second jabber connect stanza including login info*/
310                         size2 = 150 + strlen( hash ) + strlen(password) + strlen(resource);
311                         char stanza2[ size2 ];
312                         snprintf( stanza2, sizeof(stanza2), 
313                                         "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
314                                         "<username>%s</username><digest>%s</digest><resource>%s</resource></query></iq>",
315                                         username, hash, resource );
316         
317                         /* server acknowledges our existence, now see if we can login */
318                         if( session->state_machine->connecting == CONNECTING_2 ) {
319                                 //if( ! tcp_send( session->sock_obj, stanza2 )  ) {
320                                 if( socket_send( session->sock_id, stanza2 )  ) {
321                                         osrfLogWarning(OSRF_LOG_MARK, "error sending");
322                                         return 0;
323                                 }
324                         }
325
326                 }
327
328         } // not component
329
330
331         /* wait for reply */
332         //tcp_wait( session->sock_obj, connect_timeout );
333         socket_wait( session->sock_mgr, connect_timeout, session->sock_id );
334
335         if( session->state_machine->connected ) {
336                 /* yar! */
337                 return 1;
338         }
339
340         return 0;
341 }
342
343 // ---------------------------------------------------------------------------------
344 // TCP data callback. Shove the data into the push parser.
345 // ---------------------------------------------------------------------------------
346 //void grab_incoming( void * session, char* data ) {
347 void grab_incoming(void* blob, socket_manager* mgr, int sockid, char* data, int parent) {
348         transport_session* ses = (transport_session*) blob;
349         if( ! ses ) { return; }
350         xmlParseChunk(ses->parser_ctxt, data, strlen(data), 0);
351 }
352
353
354 void startElementHandler(
355         void *session, const xmlChar *name, const xmlChar **atts) {
356
357         transport_session* ses = (transport_session*) session;
358         if( ! ses ) { return; }
359
360         
361         if( strcmp( (char*) name, "message" ) == 0 ) {
362                 ses->state_machine->in_message = 1;
363                 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
364                 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
365                 buffer_add( ses->router_from_buffer, get_xml_attr( atts, "router_from" ) );
366                 buffer_add( ses->osrf_xid_buffer, get_xml_attr( atts, "osrf_xid" ) );
367                 buffer_add( ses->router_to_buffer, get_xml_attr( atts, "router_to" ) );
368                 buffer_add( ses->router_class_buffer, get_xml_attr( atts, "router_class" ) );
369                 buffer_add( ses->router_command_buffer, get_xml_attr( atts, "router_command" ) );
370                 char* broadcast = get_xml_attr( atts, "broadcast" );
371                 if( broadcast )
372                         ses->router_broadcast = atoi( broadcast );
373
374                 return;
375         }
376
377         if( ses->state_machine->in_message ) {
378
379                 if( strcmp( (char*) name, "body" ) == 0 ) {
380                         ses->state_machine->in_message_body = 1;
381                         return;
382                 }
383         
384                 if( strcmp( (char*) name, "subject" ) == 0 ) {
385                         ses->state_machine->in_subject = 1;
386                         return;
387                 }
388         
389                 if( strcmp( (char*) name, "thread" ) == 0 ) {
390                         ses->state_machine->in_thread = 1;
391                         return;
392                 }
393
394         }
395
396         if( strcmp( (char*) name, "presence" ) == 0 ) {
397                 ses->state_machine->in_presence = 1;
398                 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
399                 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
400                 return;
401         }
402
403         if( strcmp( (char*) name, "status" ) == 0 ) {
404                 ses->state_machine->in_status = 1;
405                 return;
406         }
407
408
409         if( strcmp( (char*) name, "stream:error" ) == 0 ) {
410                 ses->state_machine->in_error = 1;
411                 ses->state_machine->connected = 0;
412                 osrfLogWarning(  OSRF_LOG_MARK, "Received <stream:error> message from Jabber server" );
413                 return;
414         }
415
416
417         /* first server response from a connect attempt */
418         if( strcmp( (char*) name, "stream:stream" ) == 0 ) {
419                 if( ses->state_machine->connecting == CONNECTING_1 ) {
420                         ses->state_machine->connecting = CONNECTING_2;
421                         buffer_add( ses->session_id, get_xml_attr(atts, "id") );
422                 }
423         }
424
425         if( strcmp( (char*) name, "handshake" ) == 0 ) {
426                 ses->state_machine->connected = 1;
427                 ses->state_machine->connecting = 0;
428                 return;
429         }
430
431
432         if( strcmp( (char*) name, "error" ) == 0 ) {
433                 ses->state_machine->in_message_error = 1;
434                 buffer_add( ses->message_error_type, get_xml_attr( atts, "type" ) );
435                 ses->message_error_code = atoi( get_xml_attr( atts, "code" ) );
436                 osrfLogInfo( OSRF_LOG_MARK,  "Received <error> message with type %s and code %s", 
437                         get_xml_attr( atts, "type"), get_xml_attr( atts, "code") );
438                 return;
439         }
440
441         if( strcmp( (char*) name, "iq" ) == 0 ) {
442                 ses->state_machine->in_iq = 1;
443
444                 if( strcmp( get_xml_attr(atts, "type"), "result") == 0 
445                                 && ses->state_machine->connecting == CONNECTING_2 ) {
446                         ses->state_machine->connected = 1;
447                         ses->state_machine->connecting = 0;
448                         return;
449                 }
450
451                 if( strcmp( get_xml_attr(atts, "type"), "error") == 0 ) {
452                         osrfLogWarning( OSRF_LOG_MARK,  "Error connecting to jabber" );
453                         return;
454                 }
455         }
456 }
457
458 // ------------------------------------------------------------------
459 // Returns the value of the given XML attribute
460 // The xmlChar** construct is commonly returned from SAX event
461 // handlers.  Pass that in with the name of the attribute you want
462 // to retrieve.
463 // ------------------------------------------------------------------
464 static char* get_xml_attr( const xmlChar** atts, const char* attr_name ) {
465         int i;
466         if (atts != NULL) {
467                 for(i = 0;(atts[i] != NULL);i++) {
468                         if( strcmp( (char*) atts[i++], attr_name ) == 0 ) {
469                                 if( atts[i] != NULL ) {
470                                         return (char*) atts[i];
471                                 }
472                         }
473                 }
474         }
475         return NULL;
476 }
477
478
479 // ------------------------------------------------------------------
480 // See which tags are ending
481 // ------------------------------------------------------------------
482 void endElementHandler( void *session, const xmlChar *name) {
483         transport_session* ses = (transport_session*) session;
484         if( ! ses ) { return; }
485
486         if( strcmp( (char*) name, "message" ) == 0 ) {
487
488
489                 /* pass off the message info the callback */
490                 if( ses->message_callback ) {
491
492                         /* here it's ok to pass in the raw buffers because
493                                 message_init allocates new space for the chars 
494                                 passed in */
495                         transport_message* msg =  message_init( 
496                                 ses->body_buffer->buf, 
497                                 ses->subject_buffer->buf,
498                                 ses->thread_buffer->buf, 
499                                 ses->recipient_buffer->buf, 
500                                 ses->from_buffer->buf );
501
502                         message_set_router_info( msg, 
503                                 ses->router_from_buffer->buf, 
504                                 ses->router_to_buffer->buf, 
505                                 ses->router_class_buffer->buf,
506                                 ses->router_command_buffer->buf,
507                                 ses->router_broadcast );
508
509          message_set_osrf_xid( msg, ses->osrf_xid_buffer->buf );
510
511                         if( ses->message_error_type->n_used > 0 ) {
512                                 set_msg_error( msg, ses->message_error_type->buf, ses->message_error_code );
513                         }
514
515                         if( msg == NULL ) { return; }
516                         ses->message_callback( ses->user_data, msg );
517                 }
518
519                 ses->state_machine->in_message = 0;
520                 reset_session_buffers( session );
521                 return;
522         }
523         
524         if( strcmp( (const char*) name, "body" ) == 0 ) {
525                 ses->state_machine->in_message_body = 0;
526                 return;
527         }
528
529         if( strcmp( (const char*) name, "subject" ) == 0 ) {
530                 ses->state_machine->in_subject = 0;
531                 return;
532         }
533
534         if( strcmp( (const char*) name, "thread" ) == 0 ) {
535                 ses->state_machine->in_thread = 0;
536                 return;
537         }
538         
539         if( strcmp( (const char*) name, "iq" ) == 0 ) {
540                 ses->state_machine->in_iq = 0;
541                 if( ses->message_error_code > 0 ) {
542                         osrfLogWarning( OSRF_LOG_MARK,  "Error in IQ packet: code %d",  ses->message_error_code );
543                         osrfLogWarning( OSRF_LOG_MARK,  "Error 401 means not authorized" );
544                 }
545                 reset_session_buffers( session );
546                 return;
547         }
548
549         if( strcmp( (const char*) name, "presence" ) == 0 ) {
550                 ses->state_machine->in_presence = 0;
551                 /*
552                 if( ses->presence_callback ) {
553                         // call the callback with the status, etc.
554                 }
555                 */
556                 reset_session_buffers( session );
557                 return;
558         }
559
560         if( strcmp( (const char*) name, "status" ) == 0 ) {
561                 ses->state_machine->in_status = 0;
562                 return;
563         }
564
565         if( strcmp( (const char*) name, "error" ) == 0 ) {
566                 ses->state_machine->in_message_error = 0;
567                 return;
568         }
569
570         if( strcmp( (const char*) name, "error:error" ) == 0 ) {
571                 ses->state_machine->in_error = 0;
572                 return;
573         }
574 }
575
576 int reset_session_buffers( transport_session* ses ) {
577         buffer_reset( ses->body_buffer );
578         buffer_reset( ses->subject_buffer );
579         buffer_reset( ses->thread_buffer );
580         buffer_reset( ses->from_buffer );
581         buffer_reset( ses->recipient_buffer );
582         buffer_reset( ses->router_from_buffer );
583         buffer_reset( ses->osrf_xid_buffer );
584         buffer_reset( ses->router_to_buffer );
585         buffer_reset( ses->router_class_buffer );
586         buffer_reset( ses->router_command_buffer );
587         buffer_reset( ses->message_error_type );
588         buffer_reset( ses->session_id );
589
590         return 1;
591 }
592
593 // ------------------------------------------------------------------
594 // takes data out of the body of the message and pushes it into
595 // the appropriate buffer
596 // ------------------------------------------------------------------
597 void characterHandler(
598                 void *session, const xmlChar *ch, int len) {
599
600         char data[len+1];
601         strncpy( data, (const char*) ch, len );
602         data[len] = 0;
603
604         //printf( "Handling characters: %s\n", data );
605         transport_session* ses = (transport_session*) session;
606         if( ! ses ) { return; }
607
608         /* set the various message parts */
609         if( ses->state_machine->in_message ) {
610
611                 if( ses->state_machine->in_message_body ) {
612                         buffer_add( ses->body_buffer, data );
613                 }
614
615                 if( ses->state_machine->in_subject ) {
616                         buffer_add( ses->subject_buffer, data );
617                 }
618
619                 if( ses->state_machine->in_thread ) {
620                         buffer_add( ses->thread_buffer, data );
621                 }
622         }
623
624         /* set the presence status */
625         if( ses->state_machine->in_presence && ses->state_machine->in_status ) {
626                 buffer_add( ses->status_buffer, data );
627         }
628
629         if( ses->state_machine->in_error ) {
630                 /* for now... */
631                 osrfLogWarning( OSRF_LOG_MARK,  "ERROR XML fragment: %s\n", ch );
632         }
633
634 }
635
636 /* XXX change to warning handlers */
637 void  parseWarningHandler( void *session, const char* msg, ... ) {
638
639         va_list args;
640         va_start(args, msg);
641         fprintf(stdout, "transport_session XML WARNING");
642         vfprintf(stdout, msg, args);
643         va_end(args);
644         fprintf(stderr, "XML WARNING: %s\n", msg ); 
645 }
646
647 void  parseErrorHandler( void *session, const char* msg, ... ){
648
649         va_list args;
650         va_start(args, msg);
651         fprintf(stdout, "transport_session XML ERROR");
652         vfprintf(stdout, msg, args);
653         va_end(args);
654         fprintf(stderr, "XML ERROR: %s\n", msg ); 
655
656 }
657
658 int session_disconnect( transport_session* session ) {
659         if( session == NULL ) { return 0; }
660         //tcp_send( session->sock_obj, "</stream:stream>");
661         socket_send(session->sock_id, "</stream:stream>");
662         socket_disconnect(session->sock_mgr, session->sock_id);
663         return 0;
664         //return tcp_disconnect( session->sock_obj );
665 }
666