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