1 #include <opensrf/transport_session.h>
4 #define HOST_NAME_MAX 256
7 static char* get_xml_attr( const xmlChar** atts, const char* attr_name );
9 // ---------------------------------------------------------------------------------
10 // returns a built and allocated transport_session object.
11 // This codes does no network activity, only memory initilization
12 // ---------------------------------------------------------------------------------
13 transport_session* init_transport( const char* server,
14 int port, const char* unix_path, void* user_data, int component ) {
16 /* create the session struct */
17 transport_session* session =
18 (transport_session*) safe_malloc( sizeof(transport_session) );
20 session->user_data = user_data;
22 session->component = component;
24 /* initialize the data buffers */
25 session->body_buffer = buffer_init( JABBER_BODY_BUFSIZE );
26 session->subject_buffer = buffer_init( JABBER_SUBJECT_BUFSIZE );
27 session->thread_buffer = buffer_init( JABBER_THREAD_BUFSIZE );
28 session->from_buffer = buffer_init( JABBER_JID_BUFSIZE );
29 session->status_buffer = buffer_init( JABBER_STATUS_BUFSIZE );
30 session->recipient_buffer = buffer_init( JABBER_JID_BUFSIZE );
31 session->message_error_type = buffer_init( JABBER_JID_BUFSIZE );
32 session->session_id = buffer_init( 64 );
34 session->message_error_code = 0;
36 /* for OpenSRF extensions */
37 session->router_to_buffer = buffer_init( JABBER_JID_BUFSIZE );
38 session->router_from_buffer = buffer_init( JABBER_JID_BUFSIZE );
39 session->osrf_xid_buffer = buffer_init( JABBER_JID_BUFSIZE );
40 session->router_class_buffer = buffer_init( JABBER_JID_BUFSIZE );
41 session->router_command_buffer = buffer_init( JABBER_JID_BUFSIZE );
43 session->router_broadcast = 0;
45 if( session->body_buffer == NULL || session->subject_buffer == NULL ||
46 session->thread_buffer == NULL || session->from_buffer == NULL ||
47 session->status_buffer == NULL || session->recipient_buffer == NULL ||
48 session->router_to_buffer == NULL || session->router_from_buffer == NULL ||
49 session->router_class_buffer == NULL || session->router_command_buffer == NULL ||
50 session->session_id == NULL ) {
52 osrfLogError(OSRF_LOG_MARK, "init_transport(): buffer_init returned NULL" );
53 buffer_free( session->body_buffer );
54 buffer_free( session->subject_buffer );
55 buffer_free( session->thread_buffer );
56 buffer_free( session->from_buffer );
57 buffer_free( session->status_buffer );
58 buffer_free( session->recipient_buffer );
59 buffer_free( session->router_to_buffer );
60 buffer_free( session->router_from_buffer );
61 buffer_free( session->router_class_buffer );
62 buffer_free( session->router_command_buffer );
63 buffer_free( session->session_id );
69 /* initialize the jabber state machine */
70 session->state_machine = (jabber_machine*) safe_malloc( sizeof(jabber_machine) );
71 session->state_machine->connected = 0;
72 session->state_machine->connecting = 0;
73 session->state_machine->in_message = 0;
74 session->state_machine->in_message_body = 0;
75 session->state_machine->in_thread = 0;
76 session->state_machine->in_subject = 0;
77 session->state_machine->in_error = 0;
78 session->state_machine->in_message_error = 0;
79 session->state_machine->in_iq = 0;
80 session->state_machine->in_presence = 0;
81 session->state_machine->in_status = 0;
83 /* initialize the sax push parser */
84 session->parser_ctxt = xmlCreatePushParserCtxt(SAXHandler, session, "", 0, NULL);
86 /* initialize the transport_socket structure */
87 session->sock_mgr = (socket_manager*) safe_malloc( sizeof(socket_manager) );
89 session->sock_mgr->data_received = &grab_incoming;
90 session->sock_mgr->on_socket_closed = NULL;
91 session->sock_mgr->socket = NULL;
92 session->sock_mgr->blob = session;
95 session->server = strdup(server);
97 session->unix_path = strdup(unix_path);
98 else session->unix_path = NULL;
100 session->sock_id = 0;
101 session->message_callback = NULL;
108 /* XXX FREE THE BUFFERS */
109 int session_free( transport_session* session ) {
110 if( ! session ) { return 0; }
112 if(session->sock_mgr)
113 socket_manager_free(session->sock_mgr);
115 if( session->state_machine ) free( session->state_machine );
116 if( session->parser_ctxt) {
117 xmlFreeDoc( session->parser_ctxt->myDoc );
118 xmlFreeParserCtxt(session->parser_ctxt);
121 xmlCleanupCharEncodingHandlers();
125 buffer_free(session->body_buffer);
126 buffer_free(session->subject_buffer);
127 buffer_free(session->thread_buffer);
128 buffer_free(session->from_buffer);
129 buffer_free(session->recipient_buffer);
130 buffer_free(session->status_buffer);
131 buffer_free(session->message_error_type);
132 buffer_free(session->router_to_buffer);
133 buffer_free(session->router_from_buffer);
134 buffer_free(session->osrf_xid_buffer);
135 buffer_free(session->router_class_buffer);
136 buffer_free(session->router_command_buffer);
137 buffer_free(session->session_id);
139 free(session->server);
140 free(session->unix_path);
147 int session_wait( transport_session* session, int timeout ) {
148 if( ! session || ! session->sock_mgr ) {
152 int ret = socket_wait( session->sock_mgr, timeout, session->sock_id );
155 osrfLogDebug(OSRF_LOG_MARK, "socket_wait returned error code %d", ret);
156 session->state_machine->connected = 0;
161 int session_send_msg(
162 transport_session* session, transport_message* msg ) {
164 if( ! session ) { return -1; }
166 if( ! session->state_machine->connected ) {
167 osrfLogWarning(OSRF_LOG_MARK, "State machine is not connected in send_msg()");
171 message_prepare_xml( msg );
172 //tcp_send( session->sock_obj, msg->msg_xml );
173 return socket_send( session->sock_id, msg->msg_xml );
178 /* connects to server and connects to jabber */
179 int session_connect( transport_session* session,
180 const char* username, const char* password,
181 const char* resource, int connect_timeout, enum TRANSPORT_AUTH_TYPE auth_type ) {
187 osrfLogWarning(OSRF_LOG_MARK, "session is null in connect" );
192 //char* server = session->sock_obj->server;
193 char* server = session->server;
195 if( ! session->sock_id ) {
197 if(session->port > 0) {
198 if( (session->sock_id = socket_open_tcp_client(
199 session->sock_mgr, session->port, session->server)) <= 0 )
202 } else if(session->unix_path != NULL) {
203 if( (session->sock_id = socket_open_unix_client(
204 session->sock_mgr, session->unix_path)) <= 0 )
208 osrfLogWarning( OSRF_LOG_MARK, "Can't open session: no port or unix path" );
213 if( session->component ) {
215 /* the first Jabber connect stanza */
216 char our_hostname[HOST_NAME_MAX + 1] = "";
217 gethostname(our_hostname, sizeof(our_hostname) );
218 our_hostname[HOST_NAME_MAX] = '\0';
219 size1 = 150 + strlen( server );
220 char stanza1[ size1 ];
221 snprintf( stanza1, sizeof(stanza1),
222 "<stream:stream version='1.0' xmlns:stream='http://etherx.jabber.org/streams' "
223 "xmlns='jabber:component:accept' to='%s' from='%s' xml:lang='en'>",
224 username, our_hostname );
226 /* send the first stanze */
227 session->state_machine->connecting = CONNECTING_1;
229 // if( ! tcp_send( session->sock_obj, stanza1 ) ) {
230 if( socket_send( session->sock_id, stanza1 ) ) {
231 osrfLogWarning(OSRF_LOG_MARK, "error sending");
236 //tcp_wait( session->sock_obj, connect_timeout ); /* make the timeout smarter XXX */
237 socket_wait(session->sock_mgr, connect_timeout, session->sock_id);
239 /* server acknowledges our existence, now see if we can login */
240 if( session->state_machine->connecting == CONNECTING_2 ) {
242 int ss = session->session_id->n_used + strlen(password) + 5;
244 snprintf( hashstuff, sizeof(hashstuff), "%s%s", session->session_id->buf, password );
246 char* hash = shahash( hashstuff );
247 size2 = 100 + strlen( hash );
248 char stanza2[ size2 ];
249 snprintf( stanza2, sizeof(stanza2), "<handshake>%s</handshake>", hash );
251 //if( ! tcp_send( session->sock_obj, stanza2 ) ) {
252 if( socket_send( session->sock_id, stanza2 ) ) {
253 osrfLogWarning(OSRF_LOG_MARK, "error sending");
258 } else { /* we're not a component */
260 /* the first Jabber connect stanza */
261 size1 = 100 + strlen( server );
262 char stanza1[ size1 ];
263 snprintf( stanza1, sizeof(stanza1),
264 "<stream:stream to='%s' xmlns='jabber:client' "
265 "xmlns:stream='http://etherx.jabber.org/streams'>",
269 /* send the first stanze */
270 session->state_machine->connecting = CONNECTING_1;
271 //if( ! tcp_send( session->sock_obj, stanza1 ) ) {
272 if( socket_send( session->sock_id, stanza1 ) ) {
273 osrfLogWarning(OSRF_LOG_MARK, "error sending");
279 //tcp_wait( session->sock_obj, connect_timeout ); /* make the timeout smarter XXX */
280 socket_wait( session->sock_mgr, connect_timeout, session->sock_id ); /* make the timeout smarter XXX */
282 if( auth_type == AUTH_PLAIN ) {
284 /* the second jabber connect stanza including login info*/
285 size2 = 150 + strlen( username ) + strlen(password) + strlen(resource);
286 char stanza2[ size2 ];
287 snprintf( stanza2, sizeof(stanza2),
288 "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
289 "<username>%s</username><password>%s</password><resource>%s</resource></query></iq>",
290 username, password, resource );
292 /* server acknowledges our existence, now see if we can login */
293 if( session->state_machine->connecting == CONNECTING_2 ) {
294 //if( ! tcp_send( session->sock_obj, stanza2 ) ) {
295 if( socket_send( session->sock_id, stanza2 ) ) {
296 osrfLogWarning(OSRF_LOG_MARK, "error sending");
301 } else if( auth_type == AUTH_DIGEST ) {
303 int ss = session->session_id->n_used + strlen(password) + 5;
305 snprintf( hashstuff, sizeof(hashstuff), "%s%s", session->session_id->buf, password );
307 char* hash = shahash( hashstuff );
309 /* the second jabber connect stanza including login info*/
310 size2 = 150 + strlen( hash ) + strlen(password) + strlen(resource);
311 char stanza2[ size2 ];
312 snprintf( stanza2, sizeof(stanza2),
313 "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
314 "<username>%s</username><digest>%s</digest><resource>%s</resource></query></iq>",
315 username, hash, resource );
317 /* server acknowledges our existence, now see if we can login */
318 if( session->state_machine->connecting == CONNECTING_2 ) {
319 //if( ! tcp_send( session->sock_obj, stanza2 ) ) {
320 if( socket_send( session->sock_id, stanza2 ) ) {
321 osrfLogWarning(OSRF_LOG_MARK, "error sending");
332 //tcp_wait( session->sock_obj, connect_timeout );
333 socket_wait( session->sock_mgr, connect_timeout, session->sock_id );
335 if( session->state_machine->connected ) {
343 // ---------------------------------------------------------------------------------
344 // TCP data callback. Shove the data into the push parser.
345 // ---------------------------------------------------------------------------------
346 //void grab_incoming( void * session, char* data ) {
347 void grab_incoming(void* blob, socket_manager* mgr, int sockid, char* data, int parent) {
348 transport_session* ses = (transport_session*) blob;
349 if( ! ses ) { return; }
350 xmlParseChunk(ses->parser_ctxt, data, strlen(data), 0);
354 void startElementHandler(
355 void *session, const xmlChar *name, const xmlChar **atts) {
357 transport_session* ses = (transport_session*) session;
358 if( ! ses ) { return; }
361 if( strcmp( (char*) name, "message" ) == 0 ) {
362 ses->state_machine->in_message = 1;
363 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
364 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
365 buffer_add( ses->router_from_buffer, get_xml_attr( atts, "router_from" ) );
366 buffer_add( ses->osrf_xid_buffer, get_xml_attr( atts, "osrf_xid" ) );
367 buffer_add( ses->router_to_buffer, get_xml_attr( atts, "router_to" ) );
368 buffer_add( ses->router_class_buffer, get_xml_attr( atts, "router_class" ) );
369 buffer_add( ses->router_command_buffer, get_xml_attr( atts, "router_command" ) );
370 char* broadcast = get_xml_attr( atts, "broadcast" );
372 ses->router_broadcast = atoi( broadcast );
377 if( ses->state_machine->in_message ) {
379 if( strcmp( (char*) name, "body" ) == 0 ) {
380 ses->state_machine->in_message_body = 1;
384 if( strcmp( (char*) name, "subject" ) == 0 ) {
385 ses->state_machine->in_subject = 1;
389 if( strcmp( (char*) name, "thread" ) == 0 ) {
390 ses->state_machine->in_thread = 1;
396 if( strcmp( (char*) name, "presence" ) == 0 ) {
397 ses->state_machine->in_presence = 1;
398 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
399 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
403 if( strcmp( (char*) name, "status" ) == 0 ) {
404 ses->state_machine->in_status = 1;
409 if( strcmp( (char*) name, "stream:error" ) == 0 ) {
410 ses->state_machine->in_error = 1;
411 ses->state_machine->connected = 0;
412 osrfLogWarning( OSRF_LOG_MARK, "Received <stream:error> message from Jabber server" );
417 /* first server response from a connect attempt */
418 if( strcmp( (char*) name, "stream:stream" ) == 0 ) {
419 if( ses->state_machine->connecting == CONNECTING_1 ) {
420 ses->state_machine->connecting = CONNECTING_2;
421 buffer_add( ses->session_id, get_xml_attr(atts, "id") );
425 if( strcmp( (char*) name, "handshake" ) == 0 ) {
426 ses->state_machine->connected = 1;
427 ses->state_machine->connecting = 0;
432 if( strcmp( (char*) name, "error" ) == 0 ) {
433 ses->state_machine->in_message_error = 1;
434 buffer_add( ses->message_error_type, get_xml_attr( atts, "type" ) );
435 ses->message_error_code = atoi( get_xml_attr( atts, "code" ) );
436 osrfLogInfo( OSRF_LOG_MARK, "Received <error> message with type %s and code %s",
437 get_xml_attr( atts, "type"), get_xml_attr( atts, "code") );
441 if( strcmp( (char*) name, "iq" ) == 0 ) {
442 ses->state_machine->in_iq = 1;
444 if( strcmp( get_xml_attr(atts, "type"), "result") == 0
445 && ses->state_machine->connecting == CONNECTING_2 ) {
446 ses->state_machine->connected = 1;
447 ses->state_machine->connecting = 0;
451 if( strcmp( get_xml_attr(atts, "type"), "error") == 0 ) {
452 osrfLogWarning( OSRF_LOG_MARK, "Error connecting to jabber" );
458 // ------------------------------------------------------------------
459 // Returns the value of the given XML attribute
460 // The xmlChar** construct is commonly returned from SAX event
461 // handlers. Pass that in with the name of the attribute you want
463 // ------------------------------------------------------------------
464 static char* get_xml_attr( const xmlChar** atts, const char* attr_name ) {
467 for(i = 0;(atts[i] != NULL);i++) {
468 if( strcmp( (char*) atts[i++], attr_name ) == 0 ) {
469 if( atts[i] != NULL ) {
470 return (char*) atts[i];
479 // ------------------------------------------------------------------
480 // See which tags are ending
481 // ------------------------------------------------------------------
482 void endElementHandler( void *session, const xmlChar *name) {
483 transport_session* ses = (transport_session*) session;
484 if( ! ses ) { return; }
486 if( strcmp( (char*) name, "message" ) == 0 ) {
489 /* pass off the message info the callback */
490 if( ses->message_callback ) {
492 /* here it's ok to pass in the raw buffers because
493 message_init allocates new space for the chars
495 transport_message* msg = message_init(
496 ses->body_buffer->buf,
497 ses->subject_buffer->buf,
498 ses->thread_buffer->buf,
499 ses->recipient_buffer->buf,
500 ses->from_buffer->buf );
502 message_set_router_info( msg,
503 ses->router_from_buffer->buf,
504 ses->router_to_buffer->buf,
505 ses->router_class_buffer->buf,
506 ses->router_command_buffer->buf,
507 ses->router_broadcast );
509 message_set_osrf_xid( msg, ses->osrf_xid_buffer->buf );
511 if( ses->message_error_type->n_used > 0 ) {
512 set_msg_error( msg, ses->message_error_type->buf, ses->message_error_code );
515 if( msg == NULL ) { return; }
516 ses->message_callback( ses->user_data, msg );
519 ses->state_machine->in_message = 0;
520 reset_session_buffers( session );
524 if( strcmp( (const char*) name, "body" ) == 0 ) {
525 ses->state_machine->in_message_body = 0;
529 if( strcmp( (const char*) name, "subject" ) == 0 ) {
530 ses->state_machine->in_subject = 0;
534 if( strcmp( (const char*) name, "thread" ) == 0 ) {
535 ses->state_machine->in_thread = 0;
539 if( strcmp( (const char*) name, "iq" ) == 0 ) {
540 ses->state_machine->in_iq = 0;
541 if( ses->message_error_code > 0 ) {
542 osrfLogWarning( OSRF_LOG_MARK, "Error in IQ packet: code %d", ses->message_error_code );
543 osrfLogWarning( OSRF_LOG_MARK, "Error 401 means not authorized" );
545 reset_session_buffers( session );
549 if( strcmp( (const char*) name, "presence" ) == 0 ) {
550 ses->state_machine->in_presence = 0;
552 if( ses->presence_callback ) {
553 // call the callback with the status, etc.
556 reset_session_buffers( session );
560 if( strcmp( (const char*) name, "status" ) == 0 ) {
561 ses->state_machine->in_status = 0;
565 if( strcmp( (const char*) name, "error" ) == 0 ) {
566 ses->state_machine->in_message_error = 0;
570 if( strcmp( (const char*) name, "error:error" ) == 0 ) {
571 ses->state_machine->in_error = 0;
576 int reset_session_buffers( transport_session* ses ) {
577 buffer_reset( ses->body_buffer );
578 buffer_reset( ses->subject_buffer );
579 buffer_reset( ses->thread_buffer );
580 buffer_reset( ses->from_buffer );
581 buffer_reset( ses->recipient_buffer );
582 buffer_reset( ses->router_from_buffer );
583 buffer_reset( ses->osrf_xid_buffer );
584 buffer_reset( ses->router_to_buffer );
585 buffer_reset( ses->router_class_buffer );
586 buffer_reset( ses->router_command_buffer );
587 buffer_reset( ses->message_error_type );
588 buffer_reset( ses->session_id );
593 // ------------------------------------------------------------------
594 // takes data out of the body of the message and pushes it into
595 // the appropriate buffer
596 // ------------------------------------------------------------------
597 void characterHandler(
598 void *session, const xmlChar *ch, int len) {
601 strncpy( data, (const char*) ch, len );
604 //printf( "Handling characters: %s\n", data );
605 transport_session* ses = (transport_session*) session;
606 if( ! ses ) { return; }
608 /* set the various message parts */
609 if( ses->state_machine->in_message ) {
611 if( ses->state_machine->in_message_body ) {
612 buffer_add( ses->body_buffer, data );
615 if( ses->state_machine->in_subject ) {
616 buffer_add( ses->subject_buffer, data );
619 if( ses->state_machine->in_thread ) {
620 buffer_add( ses->thread_buffer, data );
624 /* set the presence status */
625 if( ses->state_machine->in_presence && ses->state_machine->in_status ) {
626 buffer_add( ses->status_buffer, data );
629 if( ses->state_machine->in_error ) {
631 osrfLogWarning( OSRF_LOG_MARK, "ERROR XML fragment: %s\n", ch );
636 /* XXX change to warning handlers */
637 void parseWarningHandler( void *session, const char* msg, ... ) {
641 fprintf(stdout, "transport_session XML WARNING");
642 vfprintf(stdout, msg, args);
644 fprintf(stderr, "XML WARNING: %s\n", msg );
647 void parseErrorHandler( void *session, const char* msg, ... ){
651 fprintf(stdout, "transport_session XML ERROR");
652 vfprintf(stdout, msg, args);
654 fprintf(stderr, "XML ERROR: %s\n", msg );
658 int session_disconnect( transport_session* session ) {
659 if( session == NULL ) { return 0; }
660 //tcp_send( session->sock_obj, "</stream:stream>");
661 socket_send(session->sock_id, "</stream:stream>");
662 socket_disconnect(session->sock_mgr, session->sock_id);
664 //return tcp_disconnect( session->sock_obj );