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