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