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 ) {
11 /* create the session struct */
12 transport_session* session =
13 (transport_session*) safe_malloc( sizeof(transport_session) );
15 session->user_data = user_data;
17 /* initialize the data buffers */
18 session->body_buffer = buffer_init( JABBER_BODY_BUFSIZE );
19 session->subject_buffer = buffer_init( JABBER_SUBJECT_BUFSIZE );
20 session->thread_buffer = buffer_init( JABBER_THREAD_BUFSIZE );
21 session->from_buffer = buffer_init( JABBER_JID_BUFSIZE );
22 session->status_buffer = buffer_init( JABBER_STATUS_BUFSIZE );
23 session->recipient_buffer = buffer_init( JABBER_JID_BUFSIZE );
24 session->message_error_type = buffer_init( JABBER_JID_BUFSIZE );
26 /* for OILS extensions */
27 session->router_to_buffer = buffer_init( JABBER_JID_BUFSIZE );
28 session->router_from_buffer = buffer_init( JABBER_JID_BUFSIZE );
29 session->router_class_buffer = buffer_init( JABBER_JID_BUFSIZE );
30 session->router_command_buffer = buffer_init( JABBER_JID_BUFSIZE );
34 if( session->body_buffer == NULL || session->subject_buffer == NULL ||
35 session->thread_buffer == NULL || session->from_buffer == NULL ||
36 session->status_buffer == NULL || session->recipient_buffer == NULL ||
37 session->router_to_buffer == NULL || session->router_from_buffer == NULL ||
38 session->router_class_buffer == NULL || session->router_command_buffer == NULL ) {
40 fatal_handler( "init_transport(): buffer_init returned NULL" );
45 /* initialize the jabber state machine */
46 session->state_machine = (jabber_machine*) safe_malloc( sizeof(jabber_machine) );
48 /* initialize the sax push parser */
49 session->parser_ctxt = xmlCreatePushParserCtxt(SAXHandler, session, "", 0, NULL);
51 /* initialize the transport_socket structure */
52 session->sock_obj = (transport_socket*) safe_malloc( sizeof(transport_socket) );
54 //int serv_size = strlen( server );
55 session->sock_obj->server = server;
56 session->sock_obj->port = port;
57 session->sock_obj->data_received_callback = &grab_incoming;
59 /* this will be handed back to us in callbacks */
60 session->sock_obj->user_data = session;
65 /* XXX FREE THE BUFFERS */
66 int session_free( transport_session* session ) {
67 if( ! session ) { return 0; }
69 if( session->sock_obj )
70 free( session->sock_obj );
72 if( session->state_machine ) free( session->state_machine );
73 if( session->parser_ctxt) {
74 xmlCleanupCharEncodingHandlers();
75 xmlFreeDoc( session->parser_ctxt->myDoc );
76 xmlFreeParserCtxt(session->parser_ctxt);
80 buffer_free(session->body_buffer);
81 buffer_free(session->subject_buffer);
82 buffer_free(session->thread_buffer);
83 buffer_free(session->from_buffer);
84 buffer_free(session->recipient_buffer);
85 buffer_free(session->status_buffer);
86 buffer_free(session->message_error_type);
87 buffer_free(session->router_to_buffer);
88 buffer_free(session->router_from_buffer);
89 buffer_free(session->router_class_buffer);
90 buffer_free(session->router_command_buffer);
98 int session_wait( transport_session* session, int timeout ) {
99 if( ! session || ! session->sock_obj ) {
102 int ret = tcp_wait( session->sock_obj, timeout );
104 session->state_machine->connected = 0;
109 int session_send_msg(
110 transport_session* session, transport_message* msg ) {
112 if( ! session ) { return 0; }
114 if( ! session->state_machine->connected ) {
115 warning_handler("State machine is not connected in send_msg()");
119 message_prepare_xml( msg );
120 tcp_send( session->sock_obj, msg->msg_xml );
127 /* connects to server and connects to jabber */
128 int session_connect( transport_session* session,
129 const char* username, const char* password, const char* resource, int connect_timeout ) {
135 warning_handler( "session is null in connect" );
141 session->state_machine->connected =
142 tcp_connected( session->sock_obj );
144 if( session->state_machine->connected ) {
150 char* server = session->sock_obj->server;
152 if( ! session->sock_obj ) {
156 if( ! session->sock_obj->connected ) {
157 if( ! tcp_connect( session->sock_obj ))
161 /* the first Jabber connect stanza */
162 size1 = 100 + strlen( server );
163 char stanza1[ size1 ];
164 memset( stanza1, 0, size1 );
166 "<stream:stream to='%s' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>",
169 /* the second jabber connect stanza including login info*/
170 /* currently, we only support plain text login */
171 size2 = 150 + strlen( username ) + strlen(password) + strlen(resource);
172 char stanza2[ size2 ];
173 memset( stanza2, 0, size2 );
176 "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'><username>%s</username><password>%s</password><resource>%s</resource></query></iq>",
177 username, password, resource );
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 ) {
191 if( ! tcp_send( session->sock_obj, stanza2 ) ) {
192 warning_handler("error sending");
198 tcp_wait( session->sock_obj, connect_timeout );
201 if( session->state_machine->connected ) {
209 // ---------------------------------------------------------------------------------
210 // TCP data callback. Shove the data into the push parser.
211 // ---------------------------------------------------------------------------------
212 void grab_incoming( void * session, char* data ) {
213 transport_session* ses = (transport_session*) session;
214 if( ! ses ) { return; }
215 xmlParseChunk(ses->parser_ctxt, data, strlen(data), 0);
219 void startElementHandler(
220 void *session, const xmlChar *name, const xmlChar **atts) {
222 transport_session* ses = (transport_session*) session;
223 if( ! ses ) { return; }
226 if( strcmp( name, "message" ) == 0 ) {
227 ses->state_machine->in_message = 1;
228 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
229 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
230 buffer_add( ses->router_from_buffer, get_xml_attr( atts, "router_from" ) );
231 buffer_add( ses->router_to_buffer, get_xml_attr( atts, "router_to" ) );
232 buffer_add( ses->router_class_buffer, get_xml_attr( atts, "router_class" ) );
233 buffer_add( ses->router_command_buffer, get_xml_attr( atts, "router_command" ) );
234 char* broadcast = get_xml_attr( atts, "broadcast" );
236 ses->router_broadcast = atoi( broadcast );
241 if( ses->state_machine->in_message ) {
243 if( strcmp( name, "body" ) == 0 ) {
244 ses->state_machine->in_message_body = 1;
248 if( strcmp( name, "subject" ) == 0 ) {
249 ses->state_machine->in_subject = 1;
253 if( strcmp( name, "thread" ) == 0 ) {
254 ses->state_machine->in_thread = 1;
260 if( strcmp( name, "presence" ) == 0 ) {
261 ses->state_machine->in_presence = 1;
262 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
263 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
267 if( strcmp( name, "status" ) == 0 ) {
268 ses->state_machine->in_status = 1;
273 if( strcmp( name, "stream:error" ) == 0 ) {
274 ses->state_machine->in_error = 1;
275 warning_handler( "Received <stream:error> message from Jabber server" );
280 /* first server response from a connect attempt */
281 if( strcmp( name, "stream:stream" ) == 0 ) {
282 if( ses->state_machine->connecting == CONNECTING_1 ) {
283 ses->state_machine->connecting = CONNECTING_2;
287 if( strcmp( name, "error" ) == 0 ) {
288 ses->state_machine->in_message_error = 1;
289 buffer_add( ses->message_error_type, get_xml_attr( atts, "type" ) );
290 ses->message_error_code = atoi( get_xml_attr( atts, "code" ) );
291 warning_handler( "Received <error> message" );
295 if( strcmp( name, "iq" ) == 0 ) {
296 ses->state_machine->in_iq = 1;
298 if( strcmp( get_xml_attr(atts, "type"), "result") == 0
299 && ses->state_machine->connecting == CONNECTING_2 ) {
300 ses->state_machine->connected = 1;
301 ses->state_machine->connecting = 0;
305 if( strcmp( get_xml_attr(atts, "type"), "error") == 0 ) {
306 warning_handler( "Error connecting to jabber" );
312 char* get_xml_attr( const xmlChar** atts, char* attr_name ) {
315 for(i = 0;(atts[i] != NULL);i++) {
316 if( strcmp( atts[i++], attr_name ) == 0 ) {
317 if( atts[i] != NULL ) {
318 return (char*) atts[i];
327 // ------------------------------------------------------------------
328 // See which tags are ending
329 // ------------------------------------------------------------------
330 void endElementHandler( void *session, const xmlChar *name) {
331 transport_session* ses = (transport_session*) session;
332 if( ! ses ) { return; }
334 if( strcmp( name, "message" ) == 0 ) {
336 /* pass off the message info the callback */
337 if( ses->message_callback ) {
339 /* here it's ok to pass in the raw buffers because
340 message_init allocates new space for the chars
342 transport_message* msg = message_init(
343 ses->body_buffer->buf,
344 ses->subject_buffer->buf,
345 ses->thread_buffer->buf,
346 ses->recipient_buffer->buf,
347 ses->from_buffer->buf );
349 message_set_router_info( msg,
350 ses->router_from_buffer->buf,
351 ses->router_to_buffer->buf,
352 ses->router_class_buffer->buf,
353 ses->router_command_buffer->buf,
354 ses->router_broadcast );
356 if( ses->message_error_type->n_used > 0 ) {
357 set_msg_error( msg, ses->message_error_type->buf, ses->message_error_code );
360 if( msg == NULL ) { return; }
361 ses->message_callback( ses->user_data, msg );
364 ses->state_machine->in_message = 0;
365 reset_session_buffers( session );
369 if( strcmp( name, "body" ) == 0 ) {
370 ses->state_machine->in_message_body = 0;
374 if( strcmp( name, "subject" ) == 0 ) {
375 ses->state_machine->in_subject = 0;
379 if( strcmp( name, "thread" ) == 0 ) {
380 ses->state_machine->in_thread = 0;
384 if( strcmp( name, "iq" ) == 0 ) {
385 ses->state_machine->in_iq = 0;
386 if( ses->message_error_code > 0 ) {
387 warning_handler( "Error in IQ packet: code %d", ses->message_error_code );
388 warning_handler( "Error 401 means not authorized" );
390 reset_session_buffers( session );
394 if( strcmp( name, "presence" ) == 0 ) {
395 ses->state_machine->in_presence = 0;
397 if( ses->presence_callback ) {
398 // call the callback with the status, etc.
401 reset_session_buffers( session );
405 if( strcmp( name, "status" ) == 0 ) {
406 ses->state_machine->in_status = 0;
410 if( strcmp( name, "error" ) == 0 ) {
411 ses->state_machine->in_message_error = 0;
415 if( strcmp( name, "error:error" ) == 0 ) {
416 ses->state_machine->in_error = 0;
421 int reset_session_buffers( transport_session* ses ) {
422 buffer_reset( ses->body_buffer );
423 buffer_reset( ses->subject_buffer );
424 buffer_reset( ses->thread_buffer );
425 buffer_reset( ses->from_buffer );
426 buffer_reset( ses->recipient_buffer );
427 buffer_reset( ses->router_from_buffer );
428 buffer_reset( ses->router_to_buffer );
429 buffer_reset( ses->router_class_buffer );
430 buffer_reset( ses->router_command_buffer );
431 buffer_reset( ses->message_error_type );
436 // ------------------------------------------------------------------
437 // takes data out of the body of the message and pushes it into
438 // the appropriate buffer
439 // ------------------------------------------------------------------
440 void characterHandler(
441 void *session, const xmlChar *ch, int len) {
444 memset( data, 0, len+1 );
445 strncpy( data, (char*) ch, len );
448 //printf( "Handling characters: %s\n", data );
449 transport_session* ses = (transport_session*) session;
450 if( ! ses ) { return; }
452 /* set the various message parts */
453 if( ses->state_machine->in_message ) {
455 if( ses->state_machine->in_message_body ) {
456 buffer_add( ses->body_buffer, data );
459 if( ses->state_machine->in_subject ) {
460 buffer_add( ses->subject_buffer, data );
463 if( ses->state_machine->in_thread ) {
464 buffer_add( ses->thread_buffer, data );
468 /* set the presence status */
469 if( ses->state_machine->in_presence && ses->state_machine->in_status ) {
470 buffer_add( ses->status_buffer, data );
473 if( ses->state_machine->in_error ) {
475 warning_handler( "ERROR Xml fragment: %s\n", ch );
480 /* XXX change to warning handlers */
481 void parseWarningHandler( void *session, const char* msg, ... ) {
485 fprintf(stdout, "WARNING");
486 vfprintf(stdout, msg, args);
488 fprintf(stderr, "XML WARNING: %s\n", msg );
491 void parseErrorHandler( void *session, const char* msg, ... ){
495 fprintf(stdout, "ERROR");
496 vfprintf(stdout, msg, args);
498 fprintf(stderr, "XML ERROR: %s\n", msg );
502 int session_disconnect( transport_session* session ) {
503 if( session == NULL ) { return 0; }
504 tcp_send( session->sock_obj, "</stream:stream>");
505 return tcp_disconnect( session->sock_obj );