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,
10 int port, char* unix_path, void* user_data, int component ) {
12 /* create the session struct */
13 transport_session* session =
14 (transport_session*) safe_malloc( sizeof(transport_session) );
16 session->user_data = user_data;
18 session->component = component;
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 );
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 );
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) );
57 session->sock_mgr = (socket_manager*) safe_malloc( sizeof(socket_manager) );
59 //int serv_size = strlen( server );
61 session->sock_obj->server = server;
62 session->sock_obj->port = port;
63 session->sock_obj->data_received_callback = &grab_incoming;
66 session->sock_mgr->data_received = &grab_incoming;
67 session->sock_mgr->blob = session;
70 session->server = strdup(server);
72 session->unix_path = strdup(unix_path);
73 session->unix_path = NULL;
77 /* this will be handed back to us in callbacks */
78 //session->sock_obj->user_data = session;
85 /* XXX FREE THE BUFFERS */
86 int session_free( transport_session* session ) {
87 if( ! session ) { return 0; }
89 //if( session->sock_obj )
90 // free( session->sock_obj );
93 socket_manager_free(session->sock_mgr);
95 if( session->state_machine ) free( session->state_machine );
96 if( session->parser_ctxt) {
97 xmlCleanupCharEncodingHandlers();
98 xmlFreeDoc( session->parser_ctxt->myDoc );
99 xmlFreeParserCtxt(session->parser_ctxt);
103 buffer_free(session->body_buffer);
104 buffer_free(session->subject_buffer);
105 buffer_free(session->thread_buffer);
106 buffer_free(session->from_buffer);
107 buffer_free(session->recipient_buffer);
108 buffer_free(session->status_buffer);
109 buffer_free(session->message_error_type);
110 buffer_free(session->router_to_buffer);
111 buffer_free(session->router_from_buffer);
112 buffer_free(session->router_class_buffer);
113 buffer_free(session->router_command_buffer);
114 buffer_free(session->session_id);
116 free(session->server);
117 free(session->unix_path);
124 int session_wait( transport_session* session, int timeout ) {
125 if( ! session || ! session->sock_mgr ) {
129 int ret = socket_wait( session->sock_mgr, timeout, session->sock_id );
132 session->state_machine->connected = 0;
137 int session_send_msg(
138 transport_session* session, transport_message* msg ) {
140 if( ! session ) { return 0; }
142 if( ! session->state_machine->connected ) {
143 warning_handler("State machine is not connected in send_msg()");
147 message_prepare_xml( msg );
148 //tcp_send( session->sock_obj, msg->msg_xml );
149 socket_send( session->sock_id, msg->msg_xml );
156 /* connects to server and connects to jabber */
157 int session_connect( transport_session* session,
158 const char* username, const char* password,
159 const char* resource, int connect_timeout, enum TRANSPORT_AUTH_TYPE auth_type ) {
165 warning_handler( "session is null in connect" );
170 //char* server = session->sock_obj->server;
171 char* server = session->server;
173 if( ! session->sock_id ) {
175 if(session->port > 0) {
176 if( (session->sock_id = socket_open_tcp_client(
177 session->sock_mgr, session->port, session->server)) <= 0 )
180 } else if(session->unix_path != NULL) {
181 if( (session->sock_id = socket_open_unix_client(
182 session->sock_mgr, session->unix_path)) <= 0 )
187 if( session->component ) {
189 /* the first Jabber connect stanza */
190 char* our_hostname = getenv("HOSTNAME");
191 size1 = 150 + strlen( server );
192 char stanza1[ size1 ];
193 memset( stanza1, 0, size1 );
195 "<stream:stream version='1.0' xmlns:stream='http://etherx.jabber.org/streams' "
196 "xmlns='jabber:component:accept' to='%s' from='%s' xml:lang='en'>",
197 username, our_hostname );
199 /* send the first stanze */
200 session->state_machine->connecting = CONNECTING_1;
202 // if( ! tcp_send( session->sock_obj, stanza1 ) ) {
203 if( socket_send( session->sock_id, stanza1 ) ) {
204 warning_handler("error sending");
209 //tcp_wait( session->sock_obj, connect_timeout ); /* make the timeout smarter XXX */
210 socket_wait(session->sock_mgr, connect_timeout, session->sock_id);
212 /* server acknowledges our existence, now see if we can login */
213 if( session->state_machine->connecting == CONNECTING_2 ) {
215 int ss = session->session_id->n_used + strlen(password) + 5;
217 memset(hashstuff,0,ss);
218 sprintf( hashstuff, "%s%s", session->session_id->buf, password );
220 char* hash = shahash( hashstuff );
221 size2 = 100 + strlen( hash );
222 char stanza2[ size2 ];
223 memset( stanza2, 0, size2 );
224 sprintf( stanza2, "<handshake>%s</handshake>", hash );
226 //if( ! tcp_send( session->sock_obj, stanza2 ) ) {
227 if( socket_send( session->sock_id, stanza2 ) ) {
228 warning_handler("error sending");
233 } else { /* we're not a component */
235 /* the first Jabber connect stanza */
236 size1 = 100 + strlen( server );
237 char stanza1[ size1 ];
238 memset( stanza1, 0, size1 );
240 "<stream:stream to='%s' xmlns='jabber:client' "
241 "xmlns:stream='http://etherx.jabber.org/streams'>",
245 /* send the first stanze */
246 session->state_machine->connecting = CONNECTING_1;
247 //if( ! tcp_send( session->sock_obj, stanza1 ) ) {
248 if( socket_send( session->sock_id, stanza1 ) ) {
249 warning_handler("error sending");
255 //tcp_wait( session->sock_obj, connect_timeout ); /* make the timeout smarter XXX */
256 socket_wait( session->sock_mgr, connect_timeout, session->sock_id ); /* make the timeout smarter XXX */
258 if( auth_type == AUTH_PLAIN ) {
260 /* the second jabber connect stanza including login info*/
261 size2 = 150 + strlen( username ) + strlen(password) + strlen(resource);
262 char stanza2[ size2 ];
263 memset( stanza2, 0, size2 );
266 "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
267 "<username>%s</username><password>%s</password><resource>%s</resource></query></iq>",
268 username, password, resource );
270 /* server acknowledges our existence, now see if we can login */
271 if( session->state_machine->connecting == CONNECTING_2 ) {
272 //if( ! tcp_send( session->sock_obj, stanza2 ) ) {
273 if( socket_send( session->sock_id, stanza2 ) ) {
274 warning_handler("error sending");
279 } else if( auth_type == AUTH_DIGEST ) {
281 int ss = session->session_id->n_used + strlen(password) + 5;
283 memset(hashstuff,0,ss);
284 sprintf( hashstuff, "%s%s", session->session_id->buf, password );
286 char* hash = shahash( hashstuff );
288 /* the second jabber connect stanza including login info*/
289 size2 = 150 + strlen( hash ) + strlen(password) + strlen(resource);
290 char stanza2[ size2 ];
291 memset( stanza2, 0, size2 );
294 "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
295 "<username>%s</username><digest>%s</digest><resource>%s</resource></query></iq>",
296 username, hash, resource );
298 /* server acknowledges our existence, now see if we can login */
299 if( session->state_machine->connecting == CONNECTING_2 ) {
300 //if( ! tcp_send( session->sock_obj, stanza2 ) ) {
301 if( socket_send( session->sock_id, stanza2 ) ) {
302 warning_handler("error sending");
313 //tcp_wait( session->sock_obj, connect_timeout );
314 socket_wait( session->sock_mgr, connect_timeout, session->sock_id );
316 if( session->state_machine->connected ) {
324 // ---------------------------------------------------------------------------------
325 // TCP data callback. Shove the data into the push parser.
326 // ---------------------------------------------------------------------------------
327 //void grab_incoming( void * session, char* data ) {
328 void grab_incoming(void* blob, socket_manager* mgr, int sockid, char* data, int parent) {
329 transport_session* ses = (transport_session*) blob;
330 if( ! ses ) { return; }
331 xmlParseChunk(ses->parser_ctxt, data, strlen(data), 0);
335 void startElementHandler(
336 void *session, const xmlChar *name, const xmlChar **atts) {
338 transport_session* ses = (transport_session*) session;
339 if( ! ses ) { return; }
342 if( strcmp( name, "message" ) == 0 ) {
343 ses->state_machine->in_message = 1;
344 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
345 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
346 buffer_add( ses->router_from_buffer, get_xml_attr( atts, "router_from" ) );
347 buffer_add( ses->router_to_buffer, get_xml_attr( atts, "router_to" ) );
348 buffer_add( ses->router_class_buffer, get_xml_attr( atts, "router_class" ) );
349 buffer_add( ses->router_command_buffer, get_xml_attr( atts, "router_command" ) );
350 char* broadcast = get_xml_attr( atts, "broadcast" );
352 ses->router_broadcast = atoi( broadcast );
357 if( ses->state_machine->in_message ) {
359 if( strcmp( name, "body" ) == 0 ) {
360 ses->state_machine->in_message_body = 1;
364 if( strcmp( name, "subject" ) == 0 ) {
365 ses->state_machine->in_subject = 1;
369 if( strcmp( name, "thread" ) == 0 ) {
370 ses->state_machine->in_thread = 1;
376 if( strcmp( name, "presence" ) == 0 ) {
377 ses->state_machine->in_presence = 1;
378 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
379 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
383 if( strcmp( name, "status" ) == 0 ) {
384 ses->state_machine->in_status = 1;
389 if( strcmp( name, "stream:error" ) == 0 ) {
390 ses->state_machine->in_error = 1;
391 warning_handler( "Received <stream:error> message from Jabber server" );
396 /* first server response from a connect attempt */
397 if( strcmp( name, "stream:stream" ) == 0 ) {
398 if( ses->state_machine->connecting == CONNECTING_1 ) {
399 ses->state_machine->connecting = CONNECTING_2;
400 buffer_add( ses->session_id, get_xml_attr(atts, "id") );
404 if( strcmp( name, "handshake" ) == 0 ) {
405 ses->state_machine->connected = 1;
406 ses->state_machine->connecting = 0;
411 if( strcmp( name, "error" ) == 0 ) {
412 ses->state_machine->in_message_error = 1;
413 buffer_add( ses->message_error_type, get_xml_attr( atts, "type" ) );
414 ses->message_error_code = atoi( get_xml_attr( atts, "code" ) );
415 warning_handler( "Received <error> message" );
419 if( strcmp( name, "iq" ) == 0 ) {
420 ses->state_machine->in_iq = 1;
422 if( strcmp( get_xml_attr(atts, "type"), "result") == 0
423 && ses->state_machine->connecting == CONNECTING_2 ) {
424 ses->state_machine->connected = 1;
425 ses->state_machine->connecting = 0;
429 if( strcmp( get_xml_attr(atts, "type"), "error") == 0 ) {
430 warning_handler( "Error connecting to jabber" );
436 char* get_xml_attr( const xmlChar** atts, char* attr_name ) {
439 for(i = 0;(atts[i] != NULL);i++) {
440 if( strcmp( atts[i++], attr_name ) == 0 ) {
441 if( atts[i] != NULL ) {
442 return (char*) atts[i];
451 // ------------------------------------------------------------------
452 // See which tags are ending
453 // ------------------------------------------------------------------
454 void endElementHandler( void *session, const xmlChar *name) {
455 transport_session* ses = (transport_session*) session;
456 if( ! ses ) { return; }
458 if( strcmp( name, "message" ) == 0 ) {
461 /* pass off the message info the callback */
462 if( ses->message_callback ) {
464 /* here it's ok to pass in the raw buffers because
465 message_init allocates new space for the chars
467 transport_message* msg = message_init(
468 ses->body_buffer->buf,
469 ses->subject_buffer->buf,
470 ses->thread_buffer->buf,
471 ses->recipient_buffer->buf,
472 ses->from_buffer->buf );
474 message_set_router_info( msg,
475 ses->router_from_buffer->buf,
476 ses->router_to_buffer->buf,
477 ses->router_class_buffer->buf,
478 ses->router_command_buffer->buf,
479 ses->router_broadcast );
481 if( ses->message_error_type->n_used > 0 ) {
482 set_msg_error( msg, ses->message_error_type->buf, ses->message_error_code );
485 if( msg == NULL ) { return; }
486 ses->message_callback( ses->user_data, msg );
489 ses->state_machine->in_message = 0;
490 reset_session_buffers( session );
494 if( strcmp( name, "body" ) == 0 ) {
495 ses->state_machine->in_message_body = 0;
499 if( strcmp( name, "subject" ) == 0 ) {
500 ses->state_machine->in_subject = 0;
504 if( strcmp( name, "thread" ) == 0 ) {
505 ses->state_machine->in_thread = 0;
509 if( strcmp( name, "iq" ) == 0 ) {
510 ses->state_machine->in_iq = 0;
511 if( ses->message_error_code > 0 ) {
512 warning_handler( "Error in IQ packet: code %d", ses->message_error_code );
513 warning_handler( "Error 401 means not authorized" );
515 reset_session_buffers( session );
519 if( strcmp( name, "presence" ) == 0 ) {
520 ses->state_machine->in_presence = 0;
522 if( ses->presence_callback ) {
523 // call the callback with the status, etc.
526 reset_session_buffers( session );
530 if( strcmp( name, "status" ) == 0 ) {
531 ses->state_machine->in_status = 0;
535 if( strcmp( name, "error" ) == 0 ) {
536 ses->state_machine->in_message_error = 0;
540 if( strcmp( name, "error:error" ) == 0 ) {
541 ses->state_machine->in_error = 0;
546 int reset_session_buffers( transport_session* ses ) {
547 buffer_reset( ses->body_buffer );
548 buffer_reset( ses->subject_buffer );
549 buffer_reset( ses->thread_buffer );
550 buffer_reset( ses->from_buffer );
551 buffer_reset( ses->recipient_buffer );
552 buffer_reset( ses->router_from_buffer );
553 buffer_reset( ses->router_to_buffer );
554 buffer_reset( ses->router_class_buffer );
555 buffer_reset( ses->router_command_buffer );
556 buffer_reset( ses->message_error_type );
557 buffer_reset( ses->session_id );
562 // ------------------------------------------------------------------
563 // takes data out of the body of the message and pushes it into
564 // the appropriate buffer
565 // ------------------------------------------------------------------
566 void characterHandler(
567 void *session, const xmlChar *ch, int len) {
570 memset( data, 0, len+1 );
571 strncpy( data, (char*) ch, len );
574 //printf( "Handling characters: %s\n", data );
575 transport_session* ses = (transport_session*) session;
576 if( ! ses ) { return; }
578 /* set the various message parts */
579 if( ses->state_machine->in_message ) {
581 if( ses->state_machine->in_message_body ) {
582 buffer_add( ses->body_buffer, data );
585 if( ses->state_machine->in_subject ) {
586 buffer_add( ses->subject_buffer, data );
589 if( ses->state_machine->in_thread ) {
590 buffer_add( ses->thread_buffer, data );
594 /* set the presence status */
595 if( ses->state_machine->in_presence && ses->state_machine->in_status ) {
596 buffer_add( ses->status_buffer, data );
599 if( ses->state_machine->in_error ) {
601 warning_handler( "ERROR Xml fragment: %s\n", ch );
606 /* XXX change to warning handlers */
607 void parseWarningHandler( void *session, const char* msg, ... ) {
611 fprintf(stdout, "transport_session XML WARNING");
612 vfprintf(stdout, msg, args);
614 fprintf(stderr, "XML WARNING: %s\n", msg );
617 void parseErrorHandler( void *session, const char* msg, ... ){
621 fprintf(stdout, "transport_session XML ERROR");
622 vfprintf(stdout, msg, args);
624 fprintf(stderr, "XML ERROR: %s\n", msg );
628 int session_disconnect( transport_session* session ) {
629 if( session == NULL ) { return 0; }
630 //tcp_send( session->sock_obj, "</stream:stream>");
631 socket_send(session->sock_id, "</stream:stream>");
632 socket_disconnect(session->sock_mgr, session->sock_id);
634 //return tcp_disconnect( session->sock_obj );