From 03f73b4f671d6bdc7ecff43985ee403d9c20f713 Mon Sep 17 00:00:00 2001 From: scottmk Date: Sat, 24 Oct 2009 14:05:55 +0000 Subject: [PATCH] Implemented session_connected(), which had been declared in the header but never defined. Changed transport_client.c to use session_connected(), so that it wouldn't need to see a declaration of jabber_machine. Moved from transport_session.h to transport_session.c: several macros and the declaration of jabber_machine, of which none are needed elsewhere. Removed some comments from the header so that they won't override more complete comments in the implementation file. In session_connect(): inserted a return statement in order to bypass some superfluous string comparisons. Added the const qualifier to the return type of get_xml_attr(). We don't want to be able to overwrite the text returned by the XML parser. In endElementHandler(): look for "stream:error", to match the opening tag, instead of "error:error". Resest status_buffer along with the other buffers. Introduced several minor performance tweaks. Added more doxygen-style comments for documentation. M include/opensrf/transport_session.h M src/libopensrf/transport_session.c M src/libopensrf/transport_client.c git-svn-id: svn://svn.open-ils.org/OpenSRF/trunk@1823 9efc2488-bf62-4759-914b-345cdb29e865 --- include/opensrf/transport_session.h | 77 +++------- src/libopensrf/transport_client.c | 2 +- src/libopensrf/transport_session.c | 211 +++++++++++++++++++--------- 3 files changed, 161 insertions(+), 129 deletions(-) diff --git a/include/opensrf/transport_session.h b/include/opensrf/transport_session.h index 425b977..824b504 100644 --- a/include/opensrf/transport_session.h +++ b/include/opensrf/transport_session.h @@ -1,11 +1,14 @@ -// --------------------------------------------------------------------------------- -// Manages the Jabber session. Data is taken from the TCP object and pushed into -// a SAX push parser as it arrives. When key Jabber documetn elements are met, -// logic ensues. -// --------------------------------------------------------------------------------- #ifndef TRANSPORT_SESSION_H #define TRANSPORT_SESSION_H +/** + @file transport_session.h + @brief Header for routines to manage a connection to a Jabber server. + + Manages the Jabber session. Reads data from a socket and pushes it into a SAX + parser as it arrives. When key Jabber document elements are met, logic ensues. +*/ + #include #include @@ -27,38 +30,11 @@ extern "C" { #endif -#define CONNECTING_1 1 /* just starting the connection to Jabber */ -#define CONNECTING_2 2 /* First packet sent and packet received from server */ - -/* Note. these are growing buffers, so all that's necessary is a sane starting point */ -#define JABBER_BODY_BUFSIZE 4096 -#define JABBER_SUBJECT_BUFSIZE 64 -#define JABBER_THREAD_BUFSIZE 64 -#define JABBER_JID_BUFSIZE 64 -#define JABBER_STATUS_BUFSIZE 16 +enum TRANSPORT_AUTH_TYPE { AUTH_PLAIN, AUTH_DIGEST }; -// --------------------------------------------------------------------------------- -// Jabber state machine. This is how we know where we are in the Jabber -// conversation. -// --------------------------------------------------------------------------------- -struct jabber_state_machine_struct { - int connected; - int connecting; - int in_message; - int in_message_body; - int in_thread; - int in_subject; - int in_error; - int in_message_error; - int in_iq; - int in_presence; - int in_status; -}; +struct jabber_state_machine_struct; typedef struct jabber_state_machine_struct jabber_machine; - -enum TRANSPORT_AUTH_TYPE { AUTH_PLAIN, AUTH_DIGEST }; - // --------------------------------------------------------------------------------- // Transport session. This maintains all the various parts of a session // --------------------------------------------------------------------------------- @@ -91,7 +67,7 @@ struct transport_session_struct { growing_buffer* osrf_xid_buffer; int router_broadcast; - /* this can be anything. It will show up in the + /* this can be anything. It will show up in the callbacks for your convenience. Otherwise, it's left untouched. */ void* user_data; @@ -109,20 +85,9 @@ struct transport_session_struct { }; typedef struct transport_session_struct transport_session; - -// ------------------------------------------------------------------ -// Allocates and initializes the necessary transport session -// data structures. -// If port > 0, then this session uses TCP connection. Otherwise, -// if unix_path != NULL, it uses a UNIX domain socket. -// ------------------------------------------------------------------ -transport_session* init_transport( const char* server, int port, +transport_session* init_transport( const char* server, int port, const char* unix_path, void* user_data, int component ); -// ------------------------------------------------------------------ -// Waits at most 'timeout' seconds for data to arrive from the -// TCP handler. A timeout of -1 means to wait indefinitely. -// ------------------------------------------------------------------ int session_wait( transport_session* session, int timeout ); // --------------------------------------------------------------------------------- @@ -130,23 +95,13 @@ int session_wait( transport_session* session, int timeout ); // --------------------------------------------------------------------------------- int session_send_msg( transport_session* session, transport_message* msg ); -// --------------------------------------------------------------------------------- -// Returns 1 if this session is connected to the jabber server. 0 otherwise -// --------------------------------------------------------------------------------- -int session_connected( transport_session* ); +int session_connected( transport_session* session ); -// ------------------------------------------------------------------ -// Deallocates session memory -// ------------------------------------------------------------------ int session_free( transport_session* session ); -// ------------------------------------------------------------------ -// Connects to the Jabber server. Waits at most connect_timeout -// seconds before failing -// ------------------------------------------------------------------ -int session_connect( transport_session* session, - const char* username, const char* password, - const char* resource, int connect_timeout, +int session_connect( transport_session* session, + const char* username, const char* password, + const char* resource, int connect_timeout, enum TRANSPORT_AUTH_TYPE auth_type ); int session_disconnect( transport_session* session ); diff --git a/src/libopensrf/transport_client.c b/src/libopensrf/transport_client.c index 1d9f7fe..8118d95 100644 --- a/src/libopensrf/transport_client.c +++ b/src/libopensrf/transport_client.c @@ -85,7 +85,7 @@ int client_disconnect( transport_client* client ) { int client_connected( const transport_client* client ) { if(client == NULL) return 0; - return client->session->state_machine->connected; + return session_connected( client->session ); } int client_send_message( transport_client* client, transport_message* msg ) { diff --git a/src/libopensrf/transport_session.c b/src/libopensrf/transport_session.c index e34bd5c..e9eaa94 100644 --- a/src/libopensrf/transport_session.c +++ b/src/libopensrf/transport_session.c @@ -7,6 +7,34 @@ In all cases, a transport_session acts as a client with regard to Jabber. */ +#define CONNECTING_1 1 /* just starting the connection to Jabber */ +#define CONNECTING_2 2 /* First packet sent and packet received from server */ + +/* Note. these are growing buffers, so all that's necessary is a sane starting point */ +#define JABBER_BODY_BUFSIZE 4096 +#define JABBER_SUBJECT_BUFSIZE 64 +#define JABBER_THREAD_BUFSIZE 64 +#define JABBER_JID_BUFSIZE 64 +#define JABBER_STATUS_BUFSIZE 16 + +// --------------------------------------------------------------------------------- +// Jabber state machine. This is how we know where we are in the Jabber +// conversation. +// --------------------------------------------------------------------------------- +struct jabber_state_machine_struct { + int connected; + int connecting; + int in_message; + int in_message_body; + int in_thread; + int in_subject; + int in_error; + int in_message_error; + int in_iq; + int in_presence; + int in_status; +}; + // --------------------------------------------------------------------------------- // Callback for handling the startElement event. Much of the jabber logic occurs // in this and the characterHandler callbacks. @@ -79,8 +107,8 @@ static const xmlSAXHandlerPtr SAXHandler = &SAXHandlerStruct; #endif static void grab_incoming(void* blob, socket_manager* mgr, int sockid, char* data, int parent); -static int reset_session_buffers( transport_session* session ); -static char* get_xml_attr( const xmlChar** atts, const char* attr_name ); +static void reset_session_buffers( transport_session* session ); +static const char* get_xml_attr( const xmlChar** atts, const char* attr_name ); /** @brief Allocate and initialize a transport_session. @@ -220,6 +248,14 @@ int session_free( transport_session* session ) { return 1; } +/** + @brief Determine whether a transport_session is connected. + @param session Pointer to the transport_session to be tested. + @return 1 if connected, or 0 if not. +*/ +int session_connected( transport_session* session ) { + return session ? session->state_machine->connected : 0; +} /** @brief Wait on the client socket connected to Jabber, and process any resulting input. @@ -473,10 +509,18 @@ int session_connect( transport_session* session, } } -// --------------------------------------------------------------------------------- -// TCP data callback. Takes data from the socket handler and pushes it directly -// into the push parser -// --------------------------------------------------------------------------------- +/** + @brief Callback function: push a buffer of XML into an XML parser. + @param blob Void pointer pointing to the transport_session. + @param mgr Pointer to the socket_manager (not used). + @param sockid Socket file descriptor (not used) + @param data Pointer to a buffer of received data, as a nul-terminated string. + @param parent Not applicable. + + The socket_manager calls this function when it reads a buffer's worth of data from + the Jabber socket. The XML parser calls other callback functions when it sees various + features of the XML. +*/ static void grab_incoming(void* blob, socket_manager* mgr, int sockid, char* data, int parent) { transport_session* ses = (transport_session*) blob; if( ! ses ) { return; } @@ -484,6 +528,16 @@ static void grab_incoming(void* blob, socket_manager* mgr, int sockid, char* dat } +/** + @brief Respond to the beginning of an XML element. + @param session Pointer to the transport_session, cast to a void pointer. + @param name Name of the XML element. + @param atts Pointer to a ragged array containing attributes and values. + + The XML parser calls this when it sees the beginning of an XML element. We note what + element it is by setting the corresponding switch in the state machine, and grab whatever + attributes we expect to find. +*/ static void startElementHandler( void *session, const xmlChar *name, const xmlChar **atts) { @@ -500,7 +554,7 @@ static void startElementHandler( buffer_add( ses->router_to_buffer, get_xml_attr( atts, "router_to" ) ); buffer_add( ses->router_class_buffer, get_xml_attr( atts, "router_class" ) ); buffer_add( ses->router_command_buffer, get_xml_attr( atts, "router_command" ) ); - char* broadcast = get_xml_attr( atts, "broadcast" ); + const char* broadcast = get_xml_attr( atts, "broadcast" ); if( broadcast ) ses->router_broadcast = atoi( broadcast ); @@ -553,6 +607,7 @@ static void startElementHandler( ses->state_machine->connecting = CONNECTING_2; buffer_add( ses->session_id, get_xml_attr(atts, "id") ); } + return; } if( strcmp( (char*) name, "handshake" ) == 0 ) { @@ -588,19 +643,23 @@ static void startElementHandler( } } -// ------------------------------------------------------------------ -// Returns the value of the given XML attribute -// The xmlChar** construct is commonly returned from SAX event -// handlers. Pass that in with the name of the attribute you want -// to retrieve. -// ------------------------------------------------------------------ -static char* get_xml_attr( const xmlChar** atts, const char* attr_name ) { +/** + @brief Return the value of a given XML attribute. + @param atts Pointer to a NULL terminated array of strings. + @param attr_name Name of the attribute you're looking for. + @return The value of the attribute if found, or NULL if not. + + In the array to which @a atts points, the zeroth entry is an attribute name, and the + one after that is its value. Subsequent entries alternate between names and values. + The last entry is NULL to terminate the list. +*/ +static const char* get_xml_attr( const xmlChar** atts, const char* attr_name ) { int i; if (atts != NULL) { for(i = 0;(atts[i] != NULL);i++) { - if( strcmp( (char*) atts[i++], attr_name ) == 0 ) { + if( strcmp( (const char*) atts[i++], attr_name ) == 0 ) { if( atts[i] != NULL ) { - return (char*) atts[i]; + return (const char*) atts[i]; } } } @@ -609,28 +668,31 @@ static char* get_xml_attr( const xmlChar** atts, const char* attr_name ) { } -// ------------------------------------------------------------------ -// See which tags are ending -// ------------------------------------------------------------------ +/** + @brief React to the closing of an XML tag. + @param session Pointer to a transport_session, cast to a void pointer. + @param name Pointer to the name of the tag that is closing. + + See what kind of tag is closing, and respond accordingly. +*/ static void endElementHandler( void *session, const xmlChar *name) { transport_session* ses = (transport_session*) session; if( ! ses ) { return; } - if( strcmp( (char*) name, "message" ) == 0 ) { + // Bypass a level of indirection, since we'll examine the machine repeatedly: + jabber_machine* machine = ses->state_machine; + if( machine->in_message && strcmp( (char*) name, "message" ) == 0 ) { /* pass off the message info the callback */ if( ses->message_callback ) { - /* here it's ok to pass in the raw buffers because - message_init allocates new space for the chars - passed in */ transport_message* msg = message_init( - ses->body_buffer->buf, - ses->subject_buffer->buf, - ses->thread_buffer->buf, - ses->recipient_buffer->buf, - ses->from_buffer->buf ); + OSRF_BUFFER_C_STR( ses->body_buffer ), + OSRF_BUFFER_C_STR( ses->subject_buffer ), + OSRF_BUFFER_C_STR( ses->thread_buffer ), + OSRF_BUFFER_C_STR( ses->recipient_buffer ), + OSRF_BUFFER_C_STR( ses->from_buffer ) ); message_set_router_info( msg, ses->router_from_buffer->buf, @@ -639,7 +701,7 @@ static void endElementHandler( void *session, const xmlChar *name) { ses->router_command_buffer->buf, ses->router_broadcast ); - message_set_osrf_xid( msg, ses->osrf_xid_buffer->buf ); + message_set_osrf_xid( msg, ses->osrf_xid_buffer->buf ); if( ses->message_error_type->n_used > 0 ) { set_msg_error( msg, ses->message_error_type->buf, ses->message_error_code ); @@ -649,28 +711,28 @@ static void endElementHandler( void *session, const xmlChar *name) { ses->message_callback( ses->user_data, msg ); } - ses->state_machine->in_message = 0; + machine->in_message = 0; reset_session_buffers( session ); return; } - if( strcmp( (const char*) name, "body" ) == 0 ) { - ses->state_machine->in_message_body = 0; + if( machine->in_message_body && strcmp( (const char*) name, "body" ) == 0 ) { + machine->in_message_body = 0; return; } - if( strcmp( (const char*) name, "subject" ) == 0 ) { - ses->state_machine->in_subject = 0; + if( machine->in_subject && strcmp( (const char*) name, "subject" ) == 0 ) { + machine->in_subject = 0; return; } - if( strcmp( (const char*) name, "thread" ) == 0 ) { - ses->state_machine->in_thread = 0; + if( machine->in_thread && strcmp( (const char*) name, "thread" ) == 0 ) { + machine->in_thread = 0; return; } - if( strcmp( (const char*) name, "iq" ) == 0 ) { - ses->state_machine->in_iq = 0; + if( machine->in_iq && strcmp( (const char*) name, "iq" ) == 0 ) { + machine->in_iq = 0; if( ses->message_error_code > 0 ) { osrfLogWarning( OSRF_LOG_MARK, "Error in IQ packet: code %d", ses->message_error_code ); osrfLogWarning( OSRF_LOG_MARK, "Error 401 means not authorized" ); @@ -679,8 +741,8 @@ static void endElementHandler( void *session, const xmlChar *name) { return; } - if( strcmp( (const char*) name, "presence" ) == 0 ) { - ses->state_machine->in_presence = 0; + if( machine->in_presence && strcmp( (const char*) name, "presence" ) == 0 ) { + machine->in_presence = 0; /* if( ses->presence_callback ) { // call the callback with the status, etc. @@ -690,43 +752,56 @@ static void endElementHandler( void *session, const xmlChar *name) { return; } - if( strcmp( (const char*) name, "status" ) == 0 ) { - ses->state_machine->in_status = 0; + if( machine->in_status && strcmp( (const char*) name, "status" ) == 0 ) { + machine->in_status = 0; return; } - if( strcmp( (const char*) name, "error" ) == 0 ) { - ses->state_machine->in_message_error = 0; + if( machine->in_message_error && strcmp( (const char*) name, "error" ) == 0 ) { + machine->in_message_error = 0; return; } - if( strcmp( (const char*) name, "error:error" ) == 0 ) { - ses->state_machine->in_error = 0; + if( machine->in_error && strcmp( (const char*) name, "stream:error" ) == 0 ) { + machine->in_error = 0; return; } } -static int reset_session_buffers( transport_session* ses ) { - buffer_reset( ses->body_buffer ); - buffer_reset( ses->subject_buffer ); - buffer_reset( ses->thread_buffer ); - buffer_reset( ses->from_buffer ); - buffer_reset( ses->recipient_buffer ); - buffer_reset( ses->router_from_buffer ); - buffer_reset( ses->osrf_xid_buffer ); - buffer_reset( ses->router_to_buffer ); - buffer_reset( ses->router_class_buffer ); - buffer_reset( ses->router_command_buffer ); - buffer_reset( ses->message_error_type ); - buffer_reset( ses->session_id ); - - return 1; +/** + @brief Clear all the buffers of a transport_session. + @param ses Pointer to the transport_session whose buffers are to be cleared. +*/ +static void reset_session_buffers( transport_session* ses ) { + OSRF_BUFFER_RESET( ses->body_buffer ); + OSRF_BUFFER_RESET( ses->subject_buffer ); + OSRF_BUFFER_RESET( ses->thread_buffer ); + OSRF_BUFFER_RESET( ses->from_buffer ); + OSRF_BUFFER_RESET( ses->recipient_buffer ); + OSRF_BUFFER_RESET( ses->router_from_buffer ); + OSRF_BUFFER_RESET( ses->osrf_xid_buffer ); + OSRF_BUFFER_RESET( ses->router_to_buffer ); + OSRF_BUFFER_RESET( ses->router_class_buffer ); + OSRF_BUFFER_RESET( ses->router_command_buffer ); + OSRF_BUFFER_RESET( ses->message_error_type ); + OSRF_BUFFER_RESET( ses->session_id ); + OSRF_BUFFER_RESET( ses->status_buffer ); } // ------------------------------------------------------------------ // takes data out of the body of the message and pushes it into // the appropriate buffer // ------------------------------------------------------------------ +/** + @brief Copy XML text (outside of tags) into the appropriate buffer. + @param session Pointer to the transport_session. + @param ch Pointer to the text to be copied. + @param len How many characters to be copied. + + The XML parser calls this as a callback when it finds text outside of a tag, We check + the state machine to figure out what kind of text it is, and then append it to the + corresponding buffer. +*/ static void characterHandler( void *session, const xmlChar *ch, int len) { @@ -735,28 +810,30 @@ static void characterHandler( transport_session* ses = (transport_session*) session; if( ! ses ) { return; } + jabber_machine* machine = ses->state_machine; + /* set the various message parts */ - if( ses->state_machine->in_message ) { + if( machine->in_message ) { - if( ses->state_machine->in_message_body ) { + if( machine->in_message_body ) { buffer_add_n( ses->body_buffer, p, len ); } - if( ses->state_machine->in_subject ) { + if( machine->in_subject ) { buffer_add_n( ses->subject_buffer, p, len ); } - if( ses->state_machine->in_thread ) { + if( machine->in_thread ) { buffer_add_n( ses->thread_buffer, p, len ); } } /* set the presence status */ - if( ses->state_machine->in_presence && ses->state_machine->in_status ) { + if( machine->in_presence && ses->state_machine->in_status ) { buffer_add_n( ses->status_buffer, p, len ); } - if( ses->state_machine->in_error ) { + if( machine->in_error ) { /* for now... */ osrfLogWarning( OSRF_LOG_MARK, "ERROR XML fragment: %s\n", ch ); } -- 2.43.2