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