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