1 #include <opensrf/transport_session.h>
3 // ---------------------------------------------------------------------------------
4 // Callback for handling the startElement event. Much of the jabber logic occurs
5 // in this and the characterHandler callbacks.
6 // Here we check for the various top level jabber elements: body, iq, etc.
7 // ---------------------------------------------------------------------------------
8 static void startElementHandler(
9 void *session, const xmlChar *name, const xmlChar **atts);
11 // ---------------------------------------------------------------------------------
12 // Callback for handling the endElement event. Updates the Jabber state machine
13 // to let us know the element is over.
14 // ---------------------------------------------------------------------------------
15 static void endElementHandler( void *session, const xmlChar *name);
17 // ---------------------------------------------------------------------------------
18 // This is where we extract XML text content. In particular, this is useful for
19 // extracting Jabber message bodies.
20 // ---------------------------------------------------------------------------------
21 static void characterHandler(
22 void *session, const xmlChar *ch, int len);
24 static void parseWarningHandler( void *session, const char* msg, ... );
25 static void parseErrorHandler( void *session, const char* msg, ... );
27 // ---------------------------------------------------------------------------------
28 // Tells the SAX parser which functions will be used as event callbacks
29 // ---------------------------------------------------------------------------------
30 static xmlSAXHandler SAXHandlerStruct = {
31 NULL, /* internalSubset */
32 NULL, /* isStandalone */
33 NULL, /* hasInternalSubset */
34 NULL, /* hasExternalSubset */
35 NULL, /* resolveEntity */
37 NULL, /* entityDecl */
38 NULL, /* notationDecl */
39 NULL, /* attributeDecl */
40 NULL, /* elementDecl */
41 NULL, /* unparsedEntityDecl */
42 NULL, /* setDocumentLocator */
43 NULL, /* startDocument */
44 NULL, /* endDocument */
45 startElementHandler, /* startElement */
46 endElementHandler, /* endElement */
48 characterHandler, /* characters */
49 NULL, /* ignorableWhitespace */
50 NULL, /* processingInstruction */
52 parseWarningHandler, /* xmlParserWarning */
53 parseErrorHandler, /* xmlParserError */
54 NULL, /* xmlParserFatalError : unused */
55 NULL, /* getParameterEntity */
56 NULL, /* cdataBlock; */
57 NULL, /* externalSubset; */
60 NULL, /* startElementNs */
61 NULL, /* endElementNs */
62 NULL /* xmlStructuredErrorFunc */
65 // ---------------------------------------------------------------------------------
66 // Our SAX handler pointer.
67 // ---------------------------------------------------------------------------------
68 static const xmlSAXHandlerPtr SAXHandler = &SAXHandlerStruct;
71 #define HOST_NAME_MAX 256
74 static void grab_incoming(void* blob, socket_manager* mgr, int sockid, char* data, int parent);
75 static int reset_session_buffers( transport_session* session );
76 static char* get_xml_attr( const xmlChar** atts, const char* attr_name );
78 // ---------------------------------------------------------------------------------
79 // returns a built and allocated transport_session object.
80 // This codes does no network activity, only memory initilization
81 // ---------------------------------------------------------------------------------
82 transport_session* init_transport( const char* server,
83 int port, const char* unix_path, void* user_data, int component ) {
85 /* create the session struct */
86 transport_session* session =
87 (transport_session*) safe_malloc( sizeof(transport_session) );
89 session->user_data = user_data;
91 session->component = component;
93 /* initialize the data buffers */
94 session->body_buffer = buffer_init( JABBER_BODY_BUFSIZE );
95 session->subject_buffer = buffer_init( JABBER_SUBJECT_BUFSIZE );
96 session->thread_buffer = buffer_init( JABBER_THREAD_BUFSIZE );
97 session->from_buffer = buffer_init( JABBER_JID_BUFSIZE );
98 session->status_buffer = buffer_init( JABBER_STATUS_BUFSIZE );
99 session->recipient_buffer = buffer_init( JABBER_JID_BUFSIZE );
100 session->message_error_type = buffer_init( JABBER_JID_BUFSIZE );
101 session->session_id = buffer_init( 64 );
103 session->message_error_code = 0;
105 /* for OpenSRF extensions */
106 session->router_to_buffer = buffer_init( JABBER_JID_BUFSIZE );
107 session->router_from_buffer = buffer_init( JABBER_JID_BUFSIZE );
108 session->osrf_xid_buffer = buffer_init( JABBER_JID_BUFSIZE );
109 session->router_class_buffer = buffer_init( JABBER_JID_BUFSIZE );
110 session->router_command_buffer = buffer_init( JABBER_JID_BUFSIZE );
112 session->router_broadcast = 0;
114 /* initialize the jabber state machine */
115 session->state_machine = (jabber_machine*) safe_malloc( sizeof(jabber_machine) );
116 session->state_machine->connected = 0;
117 session->state_machine->connecting = 0;
118 session->state_machine->in_message = 0;
119 session->state_machine->in_message_body = 0;
120 session->state_machine->in_thread = 0;
121 session->state_machine->in_subject = 0;
122 session->state_machine->in_error = 0;
123 session->state_machine->in_message_error = 0;
124 session->state_machine->in_iq = 0;
125 session->state_machine->in_presence = 0;
126 session->state_machine->in_status = 0;
128 /* initialize the sax push parser */
129 session->parser_ctxt = xmlCreatePushParserCtxt(SAXHandler, session, "", 0, NULL);
131 /* initialize the transport_socket structure */
132 session->sock_mgr = (socket_manager*) safe_malloc( sizeof(socket_manager) );
134 session->sock_mgr->data_received = &grab_incoming;
135 session->sock_mgr->on_socket_closed = NULL;
136 session->sock_mgr->socket = NULL;
137 session->sock_mgr->blob = session;
139 session->port = port;
140 session->server = strdup(server);
142 session->unix_path = strdup(unix_path);
143 else session->unix_path = NULL;
145 session->sock_id = 0;
146 session->message_callback = NULL;
153 /* XXX FREE THE BUFFERS */
154 int session_free( transport_session* session ) {
155 if( ! session ) { return 0; }
157 if(session->sock_mgr)
158 socket_manager_free(session->sock_mgr);
160 if( session->state_machine ) free( session->state_machine );
161 if( session->parser_ctxt) {
162 xmlFreeDoc( session->parser_ctxt->myDoc );
163 xmlFreeParserCtxt(session->parser_ctxt);
166 xmlCleanupCharEncodingHandlers();
170 buffer_free(session->body_buffer);
171 buffer_free(session->subject_buffer);
172 buffer_free(session->thread_buffer);
173 buffer_free(session->from_buffer);
174 buffer_free(session->recipient_buffer);
175 buffer_free(session->status_buffer);
176 buffer_free(session->message_error_type);
177 buffer_free(session->router_to_buffer);
178 buffer_free(session->router_from_buffer);
179 buffer_free(session->osrf_xid_buffer);
180 buffer_free(session->router_class_buffer);
181 buffer_free(session->router_command_buffer);
182 buffer_free(session->session_id);
184 free(session->server);
185 free(session->unix_path);
192 int session_wait( transport_session* session, int timeout ) {
193 if( ! session || ! session->sock_mgr ) {
197 int ret = socket_wait( session->sock_mgr, timeout, session->sock_id );
200 osrfLogDebug(OSRF_LOG_MARK, "socket_wait returned error code %d", ret);
201 session->state_machine->connected = 0;
206 int session_send_msg(
207 transport_session* session, transport_message* msg ) {
209 if( ! session ) { return -1; }
211 if( ! session->state_machine->connected ) {
212 osrfLogWarning(OSRF_LOG_MARK, "State machine is not connected in send_msg()");
216 message_prepare_xml( msg );
217 return socket_send( session->sock_id, msg->msg_xml );
222 /* connects to server and connects to jabber */
223 int session_connect( transport_session* session,
224 const char* username, const char* password,
225 const char* resource, int connect_timeout, enum TRANSPORT_AUTH_TYPE auth_type ) {
231 osrfLogWarning(OSRF_LOG_MARK, "session is null in connect" );
236 char* server = session->server;
238 if( ! session->sock_id ) {
240 if(session->port > 0) {
241 if( (session->sock_id = socket_open_tcp_client(
242 session->sock_mgr, session->port, session->server)) <= 0 )
245 } else if(session->unix_path != NULL) {
246 if( (session->sock_id = socket_open_unix_client(
247 session->sock_mgr, session->unix_path)) <= 0 )
251 osrfLogWarning( OSRF_LOG_MARK, "Can't open session: no port or unix path" );
256 if( session->component ) {
258 /* the first Jabber connect stanza */
259 char our_hostname[HOST_NAME_MAX + 1] = "";
260 gethostname(our_hostname, sizeof(our_hostname) );
261 our_hostname[HOST_NAME_MAX] = '\0';
262 size1 = 150 + strlen( server );
263 char stanza1[ size1 ];
264 snprintf( stanza1, sizeof(stanza1),
265 "<stream:stream version='1.0' xmlns:stream='http://etherx.jabber.org/streams' "
266 "xmlns='jabber:component:accept' to='%s' from='%s' xml:lang='en'>",
267 username, our_hostname );
269 /* send the first stanze */
270 session->state_machine->connecting = CONNECTING_1;
272 if( socket_send( session->sock_id, stanza1 ) ) {
273 osrfLogWarning(OSRF_LOG_MARK, "error sending");
278 socket_wait(session->sock_mgr, connect_timeout, session->sock_id);
280 /* server acknowledges our existence, now see if we can login */
281 if( session->state_machine->connecting == CONNECTING_2 ) {
283 int ss = session->session_id->n_used + strlen(password) + 5;
285 snprintf( hashstuff, sizeof(hashstuff), "%s%s", session->session_id->buf, password );
287 char* hash = shahash( hashstuff );
288 size2 = 100 + strlen( hash );
289 char stanza2[ size2 ];
290 snprintf( stanza2, sizeof(stanza2), "<handshake>%s</handshake>", hash );
292 if( socket_send( session->sock_id, stanza2 ) ) {
293 osrfLogWarning(OSRF_LOG_MARK, "error sending");
298 } else { /* we're not a component */
300 /* the first Jabber connect stanza */
301 size1 = 100 + strlen( server );
302 char stanza1[ size1 ];
303 snprintf( stanza1, sizeof(stanza1),
304 "<stream:stream to='%s' xmlns='jabber:client' "
305 "xmlns:stream='http://etherx.jabber.org/streams'>",
309 /* send the first stanze */
310 session->state_machine->connecting = CONNECTING_1;
311 if( socket_send( session->sock_id, stanza1 ) ) {
312 osrfLogWarning(OSRF_LOG_MARK, "error sending");
318 socket_wait( session->sock_mgr, connect_timeout, session->sock_id ); /* make the timeout smarter XXX */
320 if( auth_type == AUTH_PLAIN ) {
322 /* the second jabber connect stanza including login info*/
323 size2 = 150 + strlen( username ) + strlen(password) + strlen(resource);
324 char stanza2[ size2 ];
325 snprintf( stanza2, sizeof(stanza2),
326 "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
327 "<username>%s</username><password>%s</password><resource>%s</resource></query></iq>",
328 username, password, resource );
330 /* server acknowledges our existence, now see if we can login */
331 if( session->state_machine->connecting == CONNECTING_2 ) {
332 if( socket_send( session->sock_id, stanza2 ) ) {
333 osrfLogWarning(OSRF_LOG_MARK, "error sending");
338 } else if( auth_type == AUTH_DIGEST ) {
340 int ss = session->session_id->n_used + strlen(password) + 5;
342 snprintf( hashstuff, sizeof(hashstuff), "%s%s", session->session_id->buf, password );
344 char* hash = shahash( hashstuff );
346 /* the second jabber connect stanza including login info*/
347 size2 = 150 + strlen( hash ) + strlen(password) + strlen(resource);
348 char stanza2[ size2 ];
349 snprintf( stanza2, sizeof(stanza2),
350 "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'>"
351 "<username>%s</username><digest>%s</digest><resource>%s</resource></query></iq>",
352 username, hash, resource );
354 /* server acknowledges our existence, now see if we can login */
355 if( session->state_machine->connecting == CONNECTING_2 ) {
356 if( socket_send( session->sock_id, stanza2 ) ) {
357 osrfLogWarning(OSRF_LOG_MARK, "error sending");
368 socket_wait( session->sock_mgr, connect_timeout, session->sock_id );
370 if( session->state_machine->connected ) {
378 // ---------------------------------------------------------------------------------
379 // TCP data callback. Takes data from the socket handler and pushes it directly
380 // into the push parser
381 // ---------------------------------------------------------------------------------
382 static void grab_incoming(void* blob, socket_manager* mgr, int sockid, char* data, int parent) {
383 transport_session* ses = (transport_session*) blob;
384 if( ! ses ) { return; }
385 xmlParseChunk(ses->parser_ctxt, data, strlen(data), 0);
389 static void startElementHandler(
390 void *session, const xmlChar *name, const xmlChar **atts) {
392 transport_session* ses = (transport_session*) session;
393 if( ! ses ) { return; }
396 if( strcmp( (char*) name, "message" ) == 0 ) {
397 ses->state_machine->in_message = 1;
398 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
399 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
400 buffer_add( ses->router_from_buffer, get_xml_attr( atts, "router_from" ) );
401 buffer_add( ses->osrf_xid_buffer, get_xml_attr( atts, "osrf_xid" ) );
402 buffer_add( ses->router_to_buffer, get_xml_attr( atts, "router_to" ) );
403 buffer_add( ses->router_class_buffer, get_xml_attr( atts, "router_class" ) );
404 buffer_add( ses->router_command_buffer, get_xml_attr( atts, "router_command" ) );
405 char* broadcast = get_xml_attr( atts, "broadcast" );
407 ses->router_broadcast = atoi( broadcast );
412 if( ses->state_machine->in_message ) {
414 if( strcmp( (char*) name, "body" ) == 0 ) {
415 ses->state_machine->in_message_body = 1;
419 if( strcmp( (char*) name, "subject" ) == 0 ) {
420 ses->state_machine->in_subject = 1;
424 if( strcmp( (char*) name, "thread" ) == 0 ) {
425 ses->state_machine->in_thread = 1;
431 if( strcmp( (char*) name, "presence" ) == 0 ) {
432 ses->state_machine->in_presence = 1;
433 buffer_add( ses->from_buffer, get_xml_attr( atts, "from" ) );
434 buffer_add( ses->recipient_buffer, get_xml_attr( atts, "to" ) );
438 if( strcmp( (char*) name, "status" ) == 0 ) {
439 ses->state_machine->in_status = 1;
444 if( strcmp( (char*) name, "stream:error" ) == 0 ) {
445 ses->state_machine->in_error = 1;
446 ses->state_machine->connected = 0;
447 osrfLogWarning( OSRF_LOG_MARK, "Received <stream:error> message from Jabber server" );
452 /* first server response from a connect attempt */
453 if( strcmp( (char*) name, "stream:stream" ) == 0 ) {
454 if( ses->state_machine->connecting == CONNECTING_1 ) {
455 ses->state_machine->connecting = CONNECTING_2;
456 buffer_add( ses->session_id, get_xml_attr(atts, "id") );
460 if( strcmp( (char*) name, "handshake" ) == 0 ) {
461 ses->state_machine->connected = 1;
462 ses->state_machine->connecting = 0;
467 if( strcmp( (char*) name, "error" ) == 0 ) {
468 ses->state_machine->in_message_error = 1;
469 buffer_add( ses->message_error_type, get_xml_attr( atts, "type" ) );
470 ses->message_error_code = atoi( get_xml_attr( atts, "code" ) );
471 osrfLogInfo( OSRF_LOG_MARK, "Received <error> message with type %s and code %s",
472 get_xml_attr( atts, "type"), get_xml_attr( atts, "code") );
476 if( strcmp( (char*) name, "iq" ) == 0 ) {
477 ses->state_machine->in_iq = 1;
479 if( strcmp( get_xml_attr(atts, "type"), "result") == 0
480 && ses->state_machine->connecting == CONNECTING_2 ) {
481 ses->state_machine->connected = 1;
482 ses->state_machine->connecting = 0;
486 if( strcmp( get_xml_attr(atts, "type"), "error") == 0 ) {
487 osrfLogWarning( OSRF_LOG_MARK, "Error connecting to jabber" );
493 // ------------------------------------------------------------------
494 // Returns the value of the given XML attribute
495 // The xmlChar** construct is commonly returned from SAX event
496 // handlers. Pass that in with the name of the attribute you want
498 // ------------------------------------------------------------------
499 static char* get_xml_attr( const xmlChar** atts, const char* attr_name ) {
502 for(i = 0;(atts[i] != NULL);i++) {
503 if( strcmp( (char*) atts[i++], attr_name ) == 0 ) {
504 if( atts[i] != NULL ) {
505 return (char*) atts[i];
514 // ------------------------------------------------------------------
515 // See which tags are ending
516 // ------------------------------------------------------------------
517 static void endElementHandler( void *session, const xmlChar *name) {
518 transport_session* ses = (transport_session*) session;
519 if( ! ses ) { return; }
521 if( strcmp( (char*) name, "message" ) == 0 ) {
524 /* pass off the message info the callback */
525 if( ses->message_callback ) {
527 /* here it's ok to pass in the raw buffers because
528 message_init allocates new space for the chars
530 transport_message* msg = message_init(
531 ses->body_buffer->buf,
532 ses->subject_buffer->buf,
533 ses->thread_buffer->buf,
534 ses->recipient_buffer->buf,
535 ses->from_buffer->buf );
537 message_set_router_info( msg,
538 ses->router_from_buffer->buf,
539 ses->router_to_buffer->buf,
540 ses->router_class_buffer->buf,
541 ses->router_command_buffer->buf,
542 ses->router_broadcast );
544 message_set_osrf_xid( msg, ses->osrf_xid_buffer->buf );
546 if( ses->message_error_type->n_used > 0 ) {
547 set_msg_error( msg, ses->message_error_type->buf, ses->message_error_code );
550 if( msg == NULL ) { return; }
551 ses->message_callback( ses->user_data, msg );
554 ses->state_machine->in_message = 0;
555 reset_session_buffers( session );
559 if( strcmp( (const char*) name, "body" ) == 0 ) {
560 ses->state_machine->in_message_body = 0;
564 if( strcmp( (const char*) name, "subject" ) == 0 ) {
565 ses->state_machine->in_subject = 0;
569 if( strcmp( (const char*) name, "thread" ) == 0 ) {
570 ses->state_machine->in_thread = 0;
574 if( strcmp( (const char*) name, "iq" ) == 0 ) {
575 ses->state_machine->in_iq = 0;
576 if( ses->message_error_code > 0 ) {
577 osrfLogWarning( OSRF_LOG_MARK, "Error in IQ packet: code %d", ses->message_error_code );
578 osrfLogWarning( OSRF_LOG_MARK, "Error 401 means not authorized" );
580 reset_session_buffers( session );
584 if( strcmp( (const char*) name, "presence" ) == 0 ) {
585 ses->state_machine->in_presence = 0;
587 if( ses->presence_callback ) {
588 // call the callback with the status, etc.
591 reset_session_buffers( session );
595 if( strcmp( (const char*) name, "status" ) == 0 ) {
596 ses->state_machine->in_status = 0;
600 if( strcmp( (const char*) name, "error" ) == 0 ) {
601 ses->state_machine->in_message_error = 0;
605 if( strcmp( (const char*) name, "error:error" ) == 0 ) {
606 ses->state_machine->in_error = 0;
611 static int reset_session_buffers( transport_session* ses ) {
612 buffer_reset( ses->body_buffer );
613 buffer_reset( ses->subject_buffer );
614 buffer_reset( ses->thread_buffer );
615 buffer_reset( ses->from_buffer );
616 buffer_reset( ses->recipient_buffer );
617 buffer_reset( ses->router_from_buffer );
618 buffer_reset( ses->osrf_xid_buffer );
619 buffer_reset( ses->router_to_buffer );
620 buffer_reset( ses->router_class_buffer );
621 buffer_reset( ses->router_command_buffer );
622 buffer_reset( ses->message_error_type );
623 buffer_reset( ses->session_id );
628 // ------------------------------------------------------------------
629 // takes data out of the body of the message and pushes it into
630 // the appropriate buffer
631 // ------------------------------------------------------------------
632 static void characterHandler(
633 void *session, const xmlChar *ch, int len) {
635 const char* p = (const char*) ch;
637 transport_session* ses = (transport_session*) session;
638 if( ! ses ) { return; }
640 /* set the various message parts */
641 if( ses->state_machine->in_message ) {
643 if( ses->state_machine->in_message_body ) {
644 buffer_add_n( ses->body_buffer, p, len );
647 if( ses->state_machine->in_subject ) {
648 buffer_add_n( ses->subject_buffer, p, len );
651 if( ses->state_machine->in_thread ) {
652 buffer_add_n( ses->thread_buffer, p, len );
656 /* set the presence status */
657 if( ses->state_machine->in_presence && ses->state_machine->in_status ) {
658 buffer_add_n( ses->status_buffer, p, len );
661 if( ses->state_machine->in_error ) {
663 osrfLogWarning( OSRF_LOG_MARK, "ERROR XML fragment: %s\n", ch );
668 /* XXX change to warning handlers */
669 static void parseWarningHandler( void *session, const char* msg, ... ) {
673 fprintf(stdout, "transport_session XML WARNING");
674 vfprintf(stdout, msg, args);
676 fprintf(stderr, "XML WARNING: %s\n", msg );
679 static void parseErrorHandler( void *session, const char* msg, ... ){
683 fprintf(stdout, "transport_session XML ERROR");
684 vfprintf(stdout, msg, args);
686 fprintf(stderr, "XML ERROR: %s\n", msg );
690 int session_disconnect( transport_session* session ) {
691 if( session == NULL ) { return 0; }
692 socket_send(session->sock_id, "</stream:stream>");
693 socket_disconnect(session->sock_mgr, session->sock_id);