]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/libtransport/transport_session.c
added 'component' login functionality to the lib code
[OpenSRF.git] / 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, const char* resource, int connect_timeout ) {
135
136         int size1 = 0;
137         int size2 = 0;
138
139         if( ! session ) { 
140                 warning_handler( "session is null in connect" );
141                 return 0; 
142         }
143
144
145         /*
146         session->state_machine->connected = 
147                 tcp_connected( session->sock_obj );
148
149         if( session->state_machine->connected ) {
150                 return 1;
151         }
152         */
153
154
155         char* server = session->sock_obj->server;
156
157         if( ! session->sock_obj ) {
158                 return 0;
159         }
160
161         if( ! session->sock_obj->connected ) {
162                 if( ! tcp_connect( session->sock_obj ))
163                         return 0;
164         }
165
166         if( session->component ) {
167
168                 /* the first Jabber connect stanza */
169                 char* our_hostname = getenv("HOSTNAME");
170                 size1 = 150 + strlen( server );
171                 char stanza1[ size1 ]; 
172                 memset( stanza1, 0, size1 );
173                 sprintf( stanza1, 
174                                 "<stream:stream version='1.0' xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:component:accept' to='%s' from='%s' xml:lang='en'>",
175                                 username, our_hostname );
176
177                 /* send the first stanze */
178                 session->state_machine->connecting = CONNECTING_1;
179                 if( ! tcp_send( session->sock_obj, stanza1 ) ) {
180                         warning_handler("error sending");
181                         return 0;
182                 }
183         
184                 /* wait for reply */
185                 tcp_wait( session->sock_obj, connect_timeout ); /* make the timeout smarter XXX */
186         
187                 /* server acknowledges our existence, now see if we can login */
188                 if( session->state_machine->connecting == CONNECTING_2 ) {
189         
190                         int ss = session->session_id->n_used + strlen(password) + 5;
191                         char hashstuff[ss];
192                         memset(hashstuff,0,ss);
193                         sprintf( hashstuff, "%s%s", session->session_id->buf, password );
194
195                         char* hash = shahash( hashstuff );
196                         size2 = 100 + strlen( hash );
197                         char stanza2[ size2 ];
198                         memset( stanza2, 0, size2 );
199                         sprintf( stanza2, "<handshake>%s</handshake>", hash );
200         
201                         if( ! tcp_send( session->sock_obj, stanza2 )  ) {
202                                 warning_handler("error sending");
203                                 return 0;
204                         }
205                 }
206
207         } else { /* we're not a component */
208
209                 /* the first Jabber connect stanza */
210                 size1 = 100 + strlen( server );
211                 char stanza1[ size1 ]; 
212                 memset( stanza1, 0, size1 );
213                 sprintf( stanza1, 
214                                 "<stream:stream to='%s' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>",
215                         server );
216         
217                 /* the second jabber connect stanza including login info*/
218                 /* currently, we only support plain text login */
219                 size2 = 150 + strlen( username ) + strlen(password) + strlen(resource);
220                 char stanza2[ size2 ];
221                 memset( stanza2, 0, size2 );
222         
223                 sprintf( stanza2, 
224                                 "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'><username>%s</username><password>%s</password><resource>%s</resource></query></iq>",
225                                 username, password, resource );
226
227
228                 /* send the first stanze */
229                 session->state_machine->connecting = CONNECTING_1;
230                 if( ! tcp_send( session->sock_obj, stanza1 ) ) {
231                         warning_handler("error sending");
232                         return 0;
233                 }
234         
235                 /* wait for reply */
236                 tcp_wait( session->sock_obj, connect_timeout ); /* make the timeout smarter XXX */
237         
238                 /* server acknowledges our existence, now see if we can login */
239                 if( session->state_machine->connecting == CONNECTING_2 ) {
240                         if( ! tcp_send( session->sock_obj, stanza2 )  ) {
241                                 warning_handler("error sending");
242                                 return 0;
243                         }
244                 }
245
246         } // not component
247         
248         /* wait for reply */
249         tcp_wait( session->sock_obj, connect_timeout );
250
251
252         if( session->state_machine->connected ) {
253                 /* yar! */
254                 return 1;
255         }
256
257         return 0;
258 }
259
260 // ---------------------------------------------------------------------------------
261 // TCP data callback. Shove the data into the push parser.
262 // ---------------------------------------------------------------------------------
263 void grab_incoming( void * session, char* data ) {
264         transport_session* ses = (transport_session*) session;
265         if( ! ses ) { return; }
266         xmlParseChunk(ses->parser_ctxt, data, strlen(data), 0);
267 }
268
269
270 void startElementHandler(
271         void *session, const xmlChar *name, const xmlChar **atts) {
272
273         transport_session* ses = (transport_session*) session;
274         if( ! ses ) { return; }
275
276         
277         if( strcmp( name, "message" ) == 0 ) {
278                 ses->state_machine->in_message = 1;
279                 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
280                 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
281                 buffer_add( ses->router_from_buffer, get_xml_attr( atts, "router_from" ) );
282                 buffer_add( ses->router_to_buffer, get_xml_attr( atts, "router_to" ) );
283                 buffer_add( ses->router_class_buffer, get_xml_attr( atts, "router_class" ) );
284                 buffer_add( ses->router_command_buffer, get_xml_attr( atts, "router_command" ) );
285                 char* broadcast = get_xml_attr( atts, "broadcast" );
286                 if( broadcast )
287                         ses->router_broadcast = atoi( broadcast );
288
289                 return;
290         }
291
292         if( ses->state_machine->in_message ) {
293
294                 if( strcmp( name, "body" ) == 0 ) {
295                         ses->state_machine->in_message_body = 1;
296                         return;
297                 }
298         
299                 if( strcmp( name, "subject" ) == 0 ) {
300                         ses->state_machine->in_subject = 1;
301                         return;
302                 }
303         
304                 if( strcmp( name, "thread" ) == 0 ) {
305                         ses->state_machine->in_thread = 1;
306                         return;
307                 }
308
309         }
310
311         if( strcmp( name, "presence" ) == 0 ) {
312                 ses->state_machine->in_presence = 1;
313                 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
314                 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
315                 return;
316         }
317
318         if( strcmp( name, "status" ) == 0 ) {
319                 ses->state_machine->in_status = 1;
320                 return;
321         }
322
323
324         if( strcmp( name, "stream:error" ) == 0 ) {
325                 ses->state_machine->in_error = 1;
326                 warning_handler( "Received <stream:error> message from Jabber server" );
327                 return;
328         }
329
330
331         /* first server response from a connect attempt */
332         if( strcmp( name, "stream:stream" ) == 0 ) {
333                 if( ses->state_machine->connecting == CONNECTING_1 ) {
334                         ses->state_machine->connecting = CONNECTING_2;
335                         buffer_add( ses->session_id, get_xml_attr(atts, "id") );
336                 }
337         }
338
339         if( strcmp( name, "handshake" ) == 0 ) {
340                 ses->state_machine->connected = 1;
341                 ses->state_machine->connecting = 0;
342                 return;
343         }
344
345
346         if( strcmp( name, "error" ) == 0 ) {
347                 ses->state_machine->in_message_error = 1;
348                 buffer_add( ses->message_error_type, get_xml_attr( atts, "type" ) );
349                 ses->message_error_code = atoi( get_xml_attr( atts, "code" ) );
350                 warning_handler( "Received <error> message" );
351                 return;
352         }
353
354         if( strcmp( name, "iq" ) == 0 ) {
355                 ses->state_machine->in_iq = 1;
356
357                 if( strcmp( get_xml_attr(atts, "type"), "result") == 0 
358                                 && ses->state_machine->connecting == CONNECTING_2 ) {
359                         ses->state_machine->connected = 1;
360                         ses->state_machine->connecting = 0;
361                         return;
362                 }
363
364                 if( strcmp( get_xml_attr(atts, "type"), "error") == 0 ) {
365                         warning_handler( "Error connecting to jabber" );
366                         return;
367                 }
368         }
369 }
370
371 char* get_xml_attr( const xmlChar** atts, char* attr_name ) {
372         int i;
373         if (atts != NULL) {
374                 for(i = 0;(atts[i] != NULL);i++) {
375                         if( strcmp( atts[i++], attr_name ) == 0 ) {
376                                 if( atts[i] != NULL ) {
377                                         return (char*) atts[i];
378                                 }
379                         }
380                 }
381         }
382         return NULL;
383 }
384
385
386 // ------------------------------------------------------------------
387 // See which tags are ending
388 // ------------------------------------------------------------------
389 void endElementHandler( void *session, const xmlChar *name) {
390         transport_session* ses = (transport_session*) session;
391         if( ! ses ) { return; }
392
393         if( strcmp( name, "message" ) == 0 ) {
394
395                 /* pass off the message info the callback */
396                 if( ses->message_callback ) {
397
398                         /* here it's ok to pass in the raw buffers because
399                                 message_init allocates new space for the chars 
400                                 passed in */
401                         transport_message* msg =  message_init( 
402                                 ses->body_buffer->buf, 
403                                 ses->subject_buffer->buf,
404                                 ses->thread_buffer->buf, 
405                                 ses->recipient_buffer->buf, 
406                                 ses->from_buffer->buf );
407
408                         message_set_router_info( msg, 
409                                 ses->router_from_buffer->buf, 
410                                 ses->router_to_buffer->buf, 
411                                 ses->router_class_buffer->buf,
412                                 ses->router_command_buffer->buf,
413                                 ses->router_broadcast );
414
415                         if( ses->message_error_type->n_used > 0 ) {
416                                 set_msg_error( msg, ses->message_error_type->buf, ses->message_error_code );
417                         }
418
419                         if( msg == NULL ) { return; }
420                         ses->message_callback( ses->user_data, msg );
421                 }
422
423                 ses->state_machine->in_message = 0;
424                 reset_session_buffers( session );
425                 return;
426         }
427         
428         if( strcmp( name, "body" ) == 0 ) {
429                 ses->state_machine->in_message_body = 0;
430                 return;
431         }
432
433         if( strcmp( name, "subject" ) == 0 ) {
434                 ses->state_machine->in_subject = 0;
435                 return;
436         }
437
438         if( strcmp( name, "thread" ) == 0 ) {
439                 ses->state_machine->in_thread = 0;
440                 return;
441         }
442         
443         if( strcmp( name, "iq" ) == 0 ) {
444                 ses->state_machine->in_iq = 0;
445                 if( ses->message_error_code > 0 ) {
446                         warning_handler( "Error in IQ packet: code %d",  ses->message_error_code );
447                         warning_handler( "Error 401 means not authorized" );
448                 }
449                 reset_session_buffers( session );
450                 return;
451         }
452
453         if( strcmp( name, "presence" ) == 0 ) {
454                 ses->state_machine->in_presence = 0;
455                 /*
456                 if( ses->presence_callback ) {
457                         // call the callback with the status, etc.
458                 }
459                 */
460                 reset_session_buffers( session );
461                 return;
462         }
463
464         if( strcmp( name, "status" ) == 0 ) {
465                 ses->state_machine->in_status = 0;
466                 return;
467         }
468
469         if( strcmp( name, "error" ) == 0 ) {
470                 ses->state_machine->in_message_error = 0;
471                 return;
472         }
473
474         if( strcmp( name, "error:error" ) == 0 ) {
475                 ses->state_machine->in_error = 0;
476                 return;
477         }
478 }
479
480 int reset_session_buffers( transport_session* ses ) {
481         buffer_reset( ses->body_buffer );
482         buffer_reset( ses->subject_buffer );
483         buffer_reset( ses->thread_buffer );
484         buffer_reset( ses->from_buffer );
485         buffer_reset( ses->recipient_buffer );
486         buffer_reset( ses->router_from_buffer );
487         buffer_reset( ses->router_to_buffer );
488         buffer_reset( ses->router_class_buffer );
489         buffer_reset( ses->router_command_buffer );
490         buffer_reset( ses->message_error_type );
491         buffer_reset( ses->session_id );
492
493         return 1;
494 }
495
496 // ------------------------------------------------------------------
497 // takes data out of the body of the message and pushes it into
498 // the appropriate buffer
499 // ------------------------------------------------------------------
500 void characterHandler(
501                 void *session, const xmlChar *ch, int len) {
502
503         char data[len+1];
504         memset( data, 0, len+1 );
505         strncpy( data, (char*) ch, len );
506         data[len] = 0;
507
508         //printf( "Handling characters: %s\n", data );
509         transport_session* ses = (transport_session*) session;
510         if( ! ses ) { return; }
511
512         /* set the various message parts */
513         if( ses->state_machine->in_message ) {
514
515                 if( ses->state_machine->in_message_body ) {
516                         buffer_add( ses->body_buffer, data );
517                 }
518
519                 if( ses->state_machine->in_subject ) {
520                         buffer_add( ses->subject_buffer, data );
521                 }
522
523                 if( ses->state_machine->in_thread ) {
524                         buffer_add( ses->thread_buffer, data );
525                 }
526         }
527
528         /* set the presence status */
529         if( ses->state_machine->in_presence && ses->state_machine->in_status ) {
530                 buffer_add( ses->status_buffer, data );
531         }
532
533         if( ses->state_machine->in_error ) {
534                 /* for now... */
535                 warning_handler( "ERROR Xml fragment: %s\n", ch );
536         }
537
538 }
539
540 /* XXX change to warning handlers */
541 void  parseWarningHandler( void *session, const char* msg, ... ) {
542
543         va_list args;
544         va_start(args, msg);
545         fprintf(stdout, "WARNING");
546         vfprintf(stdout, msg, args);
547         va_end(args);
548         fprintf(stderr, "XML WARNING: %s\n", msg ); 
549 }
550
551 void  parseErrorHandler( void *session, const char* msg, ... ){
552
553         va_list args;
554         va_start(args, msg);
555         fprintf(stdout, "ERROR");
556         vfprintf(stdout, msg, args);
557         va_end(args);
558         fprintf(stderr, "XML ERROR: %s\n", msg ); 
559
560 }
561
562 int session_disconnect( transport_session* session ) {
563         if( session == NULL ) { return 0; }
564         tcp_send( session->sock_obj, "</stream:stream>");
565         return tcp_disconnect( session->sock_obj );
566 }
567