1 #include "transport_session.h"
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 ) {
11 /* create the session struct */
12 transport_session* session =
13 (transport_session*) safe_malloc( sizeof(transport_session) );
15 session->user_data = user_data;
17 session->component = component;
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 );
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 );
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 ) {
44 fatal_handler( "init_transport(): buffer_init returned NULL" );
49 /* initialize the jabber state machine */
50 session->state_machine = (jabber_machine*) safe_malloc( sizeof(jabber_machine) );
52 /* initialize the sax push parser */
53 session->parser_ctxt = xmlCreatePushParserCtxt(SAXHandler, session, "", 0, NULL);
55 /* initialize the transport_socket structure */
56 session->sock_obj = (transport_socket*) safe_malloc( sizeof(transport_socket) );
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;
63 /* this will be handed back to us in callbacks */
64 session->sock_obj->user_data = session;
69 /* XXX FREE THE BUFFERS */
70 int session_free( transport_session* session ) {
71 if( ! session ) { return 0; }
73 if( session->sock_obj )
74 free( session->sock_obj );
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);
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);
103 int session_wait( transport_session* session, int timeout ) {
104 if( ! session || ! session->sock_obj ) {
107 int ret = tcp_wait( session->sock_obj, timeout );
109 session->state_machine->connected = 0;
114 int session_send_msg(
115 transport_session* session, transport_message* msg ) {
117 if( ! session ) { return 0; }
119 if( ! session->state_machine->connected ) {
120 warning_handler("State machine is not connected in send_msg()");
124 message_prepare_xml( msg );
125 tcp_send( session->sock_obj, msg->msg_xml );
132 /* connects to server and connects to jabber */
133 int session_connect( transport_session* session,
134 const char* username, const char* password,
135 const char* resource, int connect_timeout, enum TRANSPORT_AUTH_TYPE auth_type ) {
141 warning_handler( "session is null in connect" );
147 session->state_machine->connected =
148 tcp_connected( session->sock_obj );
150 if( session->state_machine->connected ) {
156 char* server = session->sock_obj->server;
158 if( ! session->sock_obj ) {
162 if( ! session->sock_obj->connected ) {
163 if( ! tcp_connect( session->sock_obj ))
167 if( session->component ) {
169 /* the first Jabber connect stanza */
170 char* our_hostname = getenv("HOSTNAME");
171 size1 = 150 + strlen( server );
172 char stanza1[ size1 ];
173 memset( stanza1, 0, size1 );
175 "<stream:stream version='1.0' xmlns:stream='http://etherx.jabber.org/streams' "
176 "xmlns='jabber:component:accept' to='%s' from='%s' xml:lang='en'>",
177 username, our_hostname );
179 /* send the first stanze */
180 session->state_machine->connecting = CONNECTING_1;
181 if( ! tcp_send( session->sock_obj, stanza1 ) ) {
182 warning_handler("error sending");
187 tcp_wait( session->sock_obj, connect_timeout ); /* make the timeout smarter XXX */
189 /* server acknowledges our existence, now see if we can login */
190 if( session->state_machine->connecting == CONNECTING_2 ) {
192 int ss = session->session_id->n_used + strlen(password) + 5;
194 memset(hashstuff,0,ss);
195 sprintf( hashstuff, "%s%s", session->session_id->buf, password );
197 char* hash = shahash( hashstuff );
198 size2 = 100 + strlen( hash );
199 char stanza2[ size2 ];
200 memset( stanza2, 0, size2 );
201 sprintf( stanza2, "<handshake>%s</handshake>", hash );
203 if( ! tcp_send( session->sock_obj, stanza2 ) ) {
204 warning_handler("error sending");
209 } else { /* we're not a component */
211 /* the first Jabber connect stanza */
212 size1 = 100 + strlen( server );
213 char stanza1[ size1 ];
214 memset( stanza1, 0, size1 );
216 "<stream:stream to='%s' xmlns='jabber:client' "
217 "xmlns:stream='http://etherx.jabber.org/streams'>",
221 /* send the first stanze */
222 session->state_machine->connecting = CONNECTING_1;
223 if( ! tcp_send( session->sock_obj, stanza1 ) ) {
224 warning_handler("error sending");
230 tcp_wait( session->sock_obj, connect_timeout ); /* make the timeout smarter XXX */
232 if( auth_type == AUTH_PLAIN ) {
234 /* the second jabber connect stanza including login info*/
235 size2 = 150 + strlen( username ) + strlen(password) + strlen(resource);
236 char stanza2[ size2 ];
237 memset( stanza2, 0, size2 );
240 "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
241 "<username>%s</username><password>%s</password><resource>%s</resource></query></iq>",
242 username, password, resource );
244 /* server acknowledges our existence, now see if we can login */
245 if( session->state_machine->connecting == CONNECTING_2 ) {
246 if( ! tcp_send( session->sock_obj, stanza2 ) ) {
247 warning_handler("error sending");
252 } else if( auth_type == AUTH_DIGEST ) {
254 int ss = session->session_id->n_used + strlen(password) + 5;
256 memset(hashstuff,0,ss);
257 sprintf( hashstuff, "%s%s", session->session_id->buf, password );
259 char* hash = shahash( hashstuff );
261 /* the second jabber connect stanza including login info*/
262 size2 = 150 + strlen( hash ) + strlen(password) + strlen(resource);
263 char stanza2[ size2 ];
264 memset( stanza2, 0, size2 );
267 "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
268 "<username>%s</username><digest>%s</digest><resource>%s</resource></query></iq>",
269 username, hash, resource );
271 /* server acknowledges our existence, now see if we can login */
272 if( session->state_machine->connecting == CONNECTING_2 ) {
273 if( ! tcp_send( session->sock_obj, stanza2 ) ) {
274 warning_handler("error sending");
285 tcp_wait( session->sock_obj, connect_timeout );
287 if( session->state_machine->connected ) {
295 // ---------------------------------------------------------------------------------
296 // TCP data callback. Shove the data into the push parser.
297 // ---------------------------------------------------------------------------------
298 void grab_incoming( void * session, char* data ) {
299 transport_session* ses = (transport_session*) session;
300 if( ! ses ) { return; }
301 debug_handler("Parsing incoming XML chunk");
302 xmlParseChunk(ses->parser_ctxt, data, strlen(data), 0);
303 debug_handler("Completed parsing incoming XML chunk");
307 void startElementHandler(
308 void *session, const xmlChar *name, const xmlChar **atts) {
310 transport_session* ses = (transport_session*) session;
311 if( ! ses ) { return; }
314 if( strcmp( name, "message" ) == 0 ) {
315 ses->state_machine->in_message = 1;
316 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
317 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
318 buffer_add( ses->router_from_buffer, get_xml_attr( atts, "router_from" ) );
319 buffer_add( ses->router_to_buffer, get_xml_attr( atts, "router_to" ) );
320 buffer_add( ses->router_class_buffer, get_xml_attr( atts, "router_class" ) );
321 buffer_add( ses->router_command_buffer, get_xml_attr( atts, "router_command" ) );
322 char* broadcast = get_xml_attr( atts, "broadcast" );
324 ses->router_broadcast = atoi( broadcast );
329 if( ses->state_machine->in_message ) {
331 if( strcmp( name, "body" ) == 0 ) {
332 ses->state_machine->in_message_body = 1;
336 if( strcmp( name, "subject" ) == 0 ) {
337 ses->state_machine->in_subject = 1;
341 if( strcmp( name, "thread" ) == 0 ) {
342 ses->state_machine->in_thread = 1;
348 if( strcmp( name, "presence" ) == 0 ) {
349 ses->state_machine->in_presence = 1;
350 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
351 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
355 if( strcmp( name, "status" ) == 0 ) {
356 ses->state_machine->in_status = 1;
361 if( strcmp( name, "stream:error" ) == 0 ) {
362 ses->state_machine->in_error = 1;
363 warning_handler( "Received <stream:error> message from Jabber server" );
368 /* first server response from a connect attempt */
369 if( strcmp( name, "stream:stream" ) == 0 ) {
370 if( ses->state_machine->connecting == CONNECTING_1 ) {
371 ses->state_machine->connecting = CONNECTING_2;
372 buffer_add( ses->session_id, get_xml_attr(atts, "id") );
376 if( strcmp( name, "handshake" ) == 0 ) {
377 ses->state_machine->connected = 1;
378 ses->state_machine->connecting = 0;
383 if( strcmp( name, "error" ) == 0 ) {
384 ses->state_machine->in_message_error = 1;
385 buffer_add( ses->message_error_type, get_xml_attr( atts, "type" ) );
386 ses->message_error_code = atoi( get_xml_attr( atts, "code" ) );
387 warning_handler( "Received <error> message" );
391 if( strcmp( name, "iq" ) == 0 ) {
392 ses->state_machine->in_iq = 1;
394 if( strcmp( get_xml_attr(atts, "type"), "result") == 0
395 && ses->state_machine->connecting == CONNECTING_2 ) {
396 ses->state_machine->connected = 1;
397 ses->state_machine->connecting = 0;
401 if( strcmp( get_xml_attr(atts, "type"), "error") == 0 ) {
402 warning_handler( "Error connecting to jabber" );
408 char* get_xml_attr( const xmlChar** atts, char* attr_name ) {
411 for(i = 0;(atts[i] != NULL);i++) {
412 if( strcmp( atts[i++], attr_name ) == 0 ) {
413 if( atts[i] != NULL ) {
414 return (char*) atts[i];
423 // ------------------------------------------------------------------
424 // See which tags are ending
425 // ------------------------------------------------------------------
426 void endElementHandler( void *session, const xmlChar *name) {
427 transport_session* ses = (transport_session*) session;
428 if( ! ses ) { return; }
430 if( strcmp( name, "message" ) == 0 ) {
436 xmlDocDumpFormatMemory( ses->parser_ctxt->myDoc, &xmlbuf, &bufsize, 0 );
437 debug_handler("endElementHandler SAX Doc:\n%s\n", (char*) xmlbuf );
441 /* pass off the message info the callback */
442 if( ses->message_callback ) {
444 /* here it's ok to pass in the raw buffers because
445 message_init allocates new space for the chars
447 transport_message* msg = message_init(
448 ses->body_buffer->buf,
449 ses->subject_buffer->buf,
450 ses->thread_buffer->buf,
451 ses->recipient_buffer->buf,
452 ses->from_buffer->buf );
454 message_set_router_info( msg,
455 ses->router_from_buffer->buf,
456 ses->router_to_buffer->buf,
457 ses->router_class_buffer->buf,
458 ses->router_command_buffer->buf,
459 ses->router_broadcast );
461 if( ses->message_error_type->n_used > 0 ) {
462 set_msg_error( msg, ses->message_error_type->buf, ses->message_error_code );
465 if( msg == NULL ) { return; }
466 ses->message_callback( ses->user_data, msg );
469 ses->state_machine->in_message = 0;
470 reset_session_buffers( session );
474 if( strcmp( name, "body" ) == 0 ) {
475 ses->state_machine->in_message_body = 0;
479 if( strcmp( name, "subject" ) == 0 ) {
480 ses->state_machine->in_subject = 0;
484 if( strcmp( name, "thread" ) == 0 ) {
485 ses->state_machine->in_thread = 0;
489 if( strcmp( name, "iq" ) == 0 ) {
490 ses->state_machine->in_iq = 0;
491 if( ses->message_error_code > 0 ) {
492 warning_handler( "Error in IQ packet: code %d", ses->message_error_code );
493 warning_handler( "Error 401 means not authorized" );
495 reset_session_buffers( session );
499 if( strcmp( name, "presence" ) == 0 ) {
500 ses->state_machine->in_presence = 0;
502 if( ses->presence_callback ) {
503 // call the callback with the status, etc.
506 reset_session_buffers( session );
510 if( strcmp( name, "status" ) == 0 ) {
511 ses->state_machine->in_status = 0;
515 if( strcmp( name, "error" ) == 0 ) {
516 ses->state_machine->in_message_error = 0;
520 if( strcmp( name, "error:error" ) == 0 ) {
521 ses->state_machine->in_error = 0;
526 int reset_session_buffers( transport_session* ses ) {
527 buffer_reset( ses->body_buffer );
528 buffer_reset( ses->subject_buffer );
529 buffer_reset( ses->thread_buffer );
530 buffer_reset( ses->from_buffer );
531 buffer_reset( ses->recipient_buffer );
532 buffer_reset( ses->router_from_buffer );
533 buffer_reset( ses->router_to_buffer );
534 buffer_reset( ses->router_class_buffer );
535 buffer_reset( ses->router_command_buffer );
536 buffer_reset( ses->message_error_type );
537 buffer_reset( ses->session_id );
542 // ------------------------------------------------------------------
543 // takes data out of the body of the message and pushes it into
544 // the appropriate buffer
545 // ------------------------------------------------------------------
546 void characterHandler(
547 void *session, const xmlChar *ch, int len) {
550 memset( data, 0, len+1 );
551 strncpy( data, (char*) ch, len );
554 //printf( "Handling characters: %s\n", data );
555 transport_session* ses = (transport_session*) session;
556 if( ! ses ) { return; }
558 /* set the various message parts */
559 if( ses->state_machine->in_message ) {
561 if( ses->state_machine->in_message_body ) {
562 buffer_add( ses->body_buffer, data );
565 if( ses->state_machine->in_subject ) {
566 buffer_add( ses->subject_buffer, data );
569 if( ses->state_machine->in_thread ) {
570 buffer_add( ses->thread_buffer, data );
574 /* set the presence status */
575 if( ses->state_machine->in_presence && ses->state_machine->in_status ) {
576 buffer_add( ses->status_buffer, data );
579 if( ses->state_machine->in_error ) {
581 warning_handler( "ERROR Xml fragment: %s\n", ch );
586 /* XXX change to warning handlers */
587 void parseWarningHandler( void *session, const char* msg, ... ) {
591 fprintf(stdout, "transport_session XML WARNING");
592 vfprintf(stdout, msg, args);
594 fprintf(stderr, "XML WARNING: %s\n", msg );
597 void parseErrorHandler( void *session, const char* msg, ... ){
601 fprintf(stdout, "transport_session XML ERROR");
602 vfprintf(stdout, msg, args);
604 fprintf(stderr, "XML ERROR: %s\n", msg );
608 int session_disconnect( transport_session* session ) {
609 if( session == NULL ) { return 0; }
610 tcp_send( session->sock_obj, "</stream:stream>");
611 return tcp_disconnect( session->sock_obj );