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