From c1c7db3d68bd365bf51ca469b182aee34ae92a6b Mon Sep 17 00:00:00 2001 From: erickson Date: Mon, 10 Oct 2005 20:22:30 +0000 Subject: [PATCH] gutted chopchop moved to osrfList and osrfHash for faster lookups added core server 2 server code much testing required, more robust error messages required git-svn-id: svn://svn.open-ils.org/ILS/trunk@1904 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- OpenSRF/src/jserver/Makefile | 18 +- OpenSRF/src/jserver/jserver-c.c | 306 ---------- OpenSRF/src/jserver/jserver-c.h | 86 --- OpenSRF/src/jserver/jserver-c_main.c | 111 ---- OpenSRF/src/jserver/jserver-c_session.c | 281 --------- OpenSRF/src/jserver/jserver-c_session.h | 121 ---- OpenSRF/src/jserver/jstrings.h | 17 - OpenSRF/src/jserver/osrf_chat.c | 757 ++++++++++++++++++++++++ OpenSRF/src/jserver/osrf_chat.h | 263 ++++++++ OpenSRF/src/jserver/osrf_chat_main.c | 56 ++ 10 files changed, 1084 insertions(+), 932 deletions(-) delete mode 100644 OpenSRF/src/jserver/jserver-c.c delete mode 100644 OpenSRF/src/jserver/jserver-c.h delete mode 100644 OpenSRF/src/jserver/jserver-c_main.c delete mode 100644 OpenSRF/src/jserver/jserver-c_session.c delete mode 100644 OpenSRF/src/jserver/jserver-c_session.h delete mode 100644 OpenSRF/src/jserver/jstrings.h create mode 100644 OpenSRF/src/jserver/osrf_chat.c create mode 100644 OpenSRF/src/jserver/osrf_chat.h create mode 100644 OpenSRF/src/jserver/osrf_chat_main.c diff --git a/OpenSRF/src/jserver/Makefile b/OpenSRF/src/jserver/Makefile index 085ced5dd6..e21cfe1f53 100644 --- a/OpenSRF/src/jserver/Makefile +++ b/OpenSRF/src/jserver/Makefile @@ -1,19 +1,17 @@ LDLIBS += -lopensrf -lobjson -lxml2 -OBJECTS = jserver-c.o jserver-c_main.o jserver-c_session.o +CFLAGS += -D_GNU_SOURCE -all: jserver-c +all: chopchop -jserver-c: $(OBJECTS) -# $(CC) $(LD_OPTS) $(OBJECTS) -o $@ - -jserver-c_main.o: jserver-c_main.c -jserver-c.o: jserver-c.c jserver-c.h -jserver-c_session.o: jserver-c_session.c jserver-c_session.h +chopchop: osrf_chat.o osrf_chat_main.o + $(CC) $(CFLAGS) $(LDFLAGS) $(LDLIBS) osrf_chat.o osrf_chat_main.o -o $@ +osrf_chat.o: osrf_chat.c osrf_chat.h +osrf_chat_main.o: osrf_chat_main.c install: - cp jserver-c $(BINDIR) + cp chopchop $(BINDIR) clean: - /bin/rm -f *.o jserver-c + /bin/rm -f *.o chopchop diff --git a/OpenSRF/src/jserver/jserver-c.c b/OpenSRF/src/jserver/jserver-c.c deleted file mode 100644 index 9590708a30..0000000000 --- a/OpenSRF/src/jserver/jserver-c.c +++ /dev/null @@ -1,306 +0,0 @@ -#include "jserver-c.h" - -jserver* jserver_init() { - jserver* js = safe_malloc(sizeof(jserver)); - js->mgr = safe_malloc(sizeof(socket_manager)); - js->mgr->data_received = &jserver_handle_request; - js->mgr->blob = js; - js->mgr->on_socket_closed = &jserver_socket_closed; - js->client = NULL; - - return js; -} - -void jserver_free(jserver* js) { - if(js == NULL) return; - jclient_node* node; - while(js->client) { - node = js->client->next; - _jserver_remove_client_id(js, js->client->id); - js->client = node; - } - socket_manager_free(js->mgr); - free(js); -} - -void jserver_socket_closed(void* blob, int sock_id) { - jserver* js = (jserver*) blob; - if(js == NULL) return; - info_handler("Removing client %d - site closed socket",sock_id); - _jserver_remove_client_id(js, sock_id); -} - -/* opens the inet and unix sockets that we're listening on */ -int jserver_connect(jserver* js, int port, char* listen_ip, char* unix_path) { - if(js == NULL || js->mgr == NULL) return -1; - int status = 0; - - if(port > 0) { - status = socket_open_tcp_server(js->mgr, port, listen_ip); - if(status == -1) return status; - } - - if(unix_path != NULL) { - status = socket_open_unix_server(js->mgr, unix_path); - if(status == -1) return status; - } - - return 0; -} - -void _free_jclient_node(jclient_node* node) { - if(node == NULL) return; - free(node->addr); - jserver_session_free(node->session); - free(node); -} - -/* allocates a new client node */ -jclient_node* _new_jclient_node(int id) { - jclient_node* node = safe_malloc(sizeof(jclient_node)); - node->id = id; - node->addr = NULL; - node->session = jserver_session_init(); - node->session->blob = node; - node->session->on_msg_complete = &jserver_client_handle_msg; - node->session->on_from_discovered = &jserver_client_from_found; - node->session->on_login_init = &jserver_client_login_init; - node->session->on_login_ok = &jserver_client_login_ok; - node->session->on_client_finish = &jserver_client_finish; - return node; -} - -/* client has sent the end of it's session doc, we may now disconnect */ -void jserver_client_finish(void* blob) { - jclient_node* node = (jclient_node*) blob; - if(node == NULL) return; - jserver_send_id(node->id, ""); - _jserver_remove_client(node->parent, node->addr); - -} - -void jserver_client_from_found(void* blob, char* from) { - jclient_node* node = (jclient_node*) blob; - if(node == NULL || from == NULL) return; - - /* prevent duplicate login - kick off original */ - _jserver_remove_client(node->parent, from); - info_handler("logged in: %s", from); - node->addr = strdup(from); -} - -void jserver_client_login_init(void* blob, char* reply) { - debug_handler("here"); - jclient_node* node = (jclient_node*) blob; - if(node == NULL || reply == NULL) return; - debug_handler("jserver handling login init"); - jserver_send_id(node->id, reply); -} - -void jserver_client_login_ok(void* blob) { - jclient_node* node = (jclient_node*) blob; - if(node == NULL) return; - info_handler("Client logging in ok => %d", node->id); - //jserver_send_id(node->id, xml_login_ok); - jserver_send_id(node->id, JSTRING_LOGIN_OK); -} - -void jserver_client_handle_msg( - void* blob, char* xml, char* from, char* to ) { - - jclient_node* node = (jclient_node*) blob; - if(node == NULL || xml == NULL || to == NULL) return; - int from_id = 0; - - jclient_node* from_node = jserver_find_client(node->parent, from); - if(from_node) - from_id = from_node->id; - - - debug_handler("Client %d received from %s message : %s", - node->id, from, xml ); - - jserver_send(node->parent, from_id, to, xml); -} - -/* allocates a new client node and adds it to the set */ -jclient_node* _jserver_add_client(jserver* js, int id) { - if(js == NULL) return NULL; - jclient_node* node = _new_jclient_node(id); - node->next = js->client; - js->client = node; - node->parent = js; - return node; -} - - -/* removes and frees a client node */ -void _jserver_remove_client(jserver* js, char* addr) { - if(js == NULL || js->client == NULL || addr == NULL) return; - - jclient_node* node = js->client; - - if(node->addr && !strcmp(node->addr,addr)) { - js->client = node->next; - debug_handler("Removing the first jserver client"); - socket_disconnect(js->mgr, node->id); - _free_jclient_node(node); - return; - } - - debug_handler("Searching for jclient to remove"); - jclient_node* tail_node = node; - node = node->next; - - while(node) { - if(node->addr && !strcmp(node->addr,addr)) { - tail_node->next = node->next; - debug_handler("Removing a jserver client"); - socket_disconnect(js->mgr, node->id); - _free_jclient_node(node); - return; - } - tail_node = node; - node = node->next; - } -} - - -/* removes and frees a client node */ -void _jserver_remove_client_id(jserver* js, int id) { - if(js == NULL || js->client == NULL) return; - - jclient_node* node = js->client; - - debug_handler("Searching for jclient to remove with id %d", id); - debug_handler("First node in list has id %d", node->id ); - - if(node->id == id) { - js->client = node->next; - debug_handler("Removing the first jserver client"); - socket_disconnect(js->mgr, node->id); - _free_jclient_node(node); - return; - } - - jclient_node* tail_node = node; - node = node->next; - - while(node) { - debug_handler("Checking node %d to remove", node->id); - if(node->id == id) { - tail_node->next = node->next; - debug_handler("Removing a jserver client"); - socket_disconnect(js->mgr, node->id); - _free_jclient_node(node); - return; - } - tail_node = node; - node = node->next; - } -} - -/* finds a client node by addr */ -jclient_node* jserver_find_client(jserver* js, char* addr) { - if(js == NULL || addr == NULL) return NULL; - jclient_node* node = js->client; - while(node) { - if(node->addr && !strcmp(node->addr, addr)) - return node; - node = node->next; - } - return NULL; -} - -jclient_node* jserver_find_client_id(jserver* js, int id) { - if(js == NULL) return NULL; - jclient_node* node = js->client; - while(node) { - if(node->id == id) - return node; - node = node->next; - } - return NULL; -} - -/* sends msg to client at 'to_addr' */ -int jserver_send(jserver* js, int from_id, char* to_addr, const char* msg_xml) { - debug_handler("sending message to %s : %s", to_addr, msg_xml); - if(to_addr == NULL || msg_xml == NULL) return -1; - - jclient_node* node = jserver_find_client(js, to_addr); - - if(node == NULL) { - info_handler("message to non-existent client %s", to_addr); - if(from_id > 0) { - jclient_node* from = jserver_find_client_id(js, from_id); - - if(from) { - info_handler("replying with error..."); - char buf[2048]; - memset(buf, 0, 2048); - /* - snprintf(buf, 2047, "" - "NOT ADDING BODY", to_addr, from->addr ); - */ - snprintf(buf, 2047, JSTRING_NO_RECIPIENT, to_addr, from->addr ); - jserver_send_id(from_id, buf); - } - } - return -1; - } - - return jserver_send_id(node->id, msg_xml); -} - -int jserver_send_id(int client_id, const char* msg_xml) { - if(msg_xml == NULL || client_id < 1) return -1; - return socket_send(client_id, msg_xml ); -} - -/* waits for any incoming data */ -int jserver_wait(jserver* js) { - if(js == NULL) return -1; - while(1) { - if(socket_wait_all(js->mgr, -1) < 0) - warning_handler( - "jserver_wait(): socket_wait_all() returned error"); - - } -} - - -int _jserver_push_client_data(jclient_node* node, char* data) { - if(node == NULL || data == NULL) return -1; - return jserver_session_push_data( node->session, data); -} - -void jserver_handle_request(void* js_blob, - socket_manager* mgr, int sock_id, char* data, int parent_id ) { - - jserver* js = (jserver*) js_blob; - - debug_handler("jsever received data from socket %d (parent %d)", sock_id, parent_id ); - - jclient_node* node = jserver_find_client_id(js, sock_id); - if(!node) { - debug_handler("We have a new client connection, adding to list"); - node = _jserver_add_client(js, sock_id); - } - - if(_jserver_push_client_data(node, data) == -1) { - warning_handler("Client sent bad data, disconnecting..."); - //jserver_send_id(node->id, xml_parse_error); - jserver_send_id(node->id, JSTRING_PARSE_ERROR); - _jserver_remove_client(js, node->addr); - - } else { - debug_handler("Client data successfully parsed"); - } - -} - - - diff --git a/OpenSRF/src/jserver/jserver-c.h b/OpenSRF/src/jserver/jserver-c.h deleted file mode 100644 index 498df0acd1..0000000000 --- a/OpenSRF/src/jserver/jserver-c.h +++ /dev/null @@ -1,86 +0,0 @@ -#include "opensrf/utils.h" -#include "opensrf/logging.h" -#include "opensrf/socket_bundle.h" -#include "jserver-c_session.h" -#include "jstrings.h" - - - -struct jclient_node_struct { - int id; - char* addr; - struct jclient_node_struct* next; - jserver_session* session; - struct jserver_struct* parent; -}; -typedef struct jclient_node_struct jclient_node; - -struct jserver_struct { - jclient_node* client; - socket_manager* mgr; -}; -typedef struct jserver_struct jserver; - -/* allocats and sets up a new jserver */ -jserver* jserver_init(); - -void jserver_socket_closed(void* blob, int sock_id); - -/* disconnects all client, deallocates the server and all clients */ -void jserver_free(); - -/* opens the inet and unix sockets that we're listening on - listen_ip is the IP address the server should listen on. - if listen_ip is NULL, jserver will bind to all local IP's. - if(port < 1) no inet socket is opened - if unix_path == NULL no unix socket is opened - returns -1 on error */ -int jserver_connect(jserver* js, int port, char* listen_ip, char* unix_path); - -/* allocates a new client node */ -jclient_node* _new_jclient_node(int id); - -void _free_jclient_node(jclient_node* node); - -int _jserver_push_client_data(jclient_node* node, char* data); - -void jclient_on_parse_error(void* blob, jserver_session* session); - -/* called when a newly connected client reveals its address */ -void jserver_client_from_found(void* blob, char* from); -void jserver_client_login_init(void* blob, char* reply); -void jserver_client_login_ok(void* blob); -void jserver_client_finish(void* blob); - -/* allocates a new client node and adds it to the set */ -jclient_node* _jserver_add_client(jserver* js, int id); - -/* removes and frees a client node */ -void _jserver_remove_client(jserver* js, char* addr); - -void _jserver_remove_client_id(jserver* js, int id); - -/* finds a client node by addr */ -jclient_node* jserver_find_client(jserver* js, char* addr); - -jclient_node* jserver_find_client_id(jserver* js, int id); - -/* sends msg to client at 'to_addr'. from_id is the id - of the sending client (if from_id > 0). used for error replies */ -int jserver_send(jserver* js, int from_id, char* to_addr, const char* msg_xml); - -/* send the data to the client with client_id */ -int jserver_send_id(int client_id, const char* msg_xml); - -/* waits for any incoming data */ -int jserver_wait(jserver* js); - -/* handles all incoming socket data */ -void jserver_handle_request(void* js, - socket_manager* mgr, int sock_id, char* data, int parent_id ); - - -/* called by the jserver_session when any client has a - complete message parsed and ready to forward on */ -void jserver_client_handle_msg( - void* blob, char* xml, char* from, char* to ); diff --git a/OpenSRF/src/jserver/jserver-c_main.c b/OpenSRF/src/jserver/jserver-c_main.c deleted file mode 100644 index 53a69ad539..0000000000 --- a/OpenSRF/src/jserver/jserver-c_main.c +++ /dev/null @@ -1,111 +0,0 @@ -#include "jserver-c.h" -#include -#include - -/* config vars */ -jserver* js = NULL; -int port = -1; -char* unix_sock_file = NULL; -int log_level = -1; -char* log_file = NULL; -char* listen_ip = NULL; - -/* starts the logging and server processes */ -void launch_server(); - - -/* shut down, clean up, and restart */ -void sig_hup_handler( int a ) { - warning_handler(" +++ Re-launching server for SIGHUP"); - - jserver_free(js); - log_free(); - unlink(unix_sock_file); - - launch_server(); - return; -} - -/* die gracefully */ -void sig_int_handler( int a ) { - warning_handler(" +++ Shutting down because of user signal"); - jserver_free(js); - log_free(); - unlink(unix_sock_file); - exit(0); -} - - - -/* loads the command line settings and launches the server */ -int main(int argc, char* argv[]) { - - char* prog = argv[0]; - char* sport = argv[1]; - listen_ip = argv[2]; - unix_sock_file = argv[3]; - char* slog_level = argv[4]; - log_file = argv[5]; - - if(!sport || !unix_sock_file || !slog_level) { - fprintf(stderr, - "usage: %s [log_file (optional, goes to stderr otherwise)]\n" - "e.g: %s 5222 10.0.0.100 /tmp/server.sock 1 /tmp/server.log\n" - "if listen_ip is '*', then we will listen on all addresses", - prog, prog); - return 99; - } - - port = atoi(sport); - log_level = atoi(slog_level); - - if(port < 1) { - warning_handler("invalid port (%d), falling back to 5222"); - port = 5222; - } - - if(log_level < 1 || log_level > 4) { - warning_handler("log level (%d) is not recognized, falling back to WARN", log_level); - log_level = 2; - } - - fprintf(stderr, "Launching with port %d, unix sock %s, log level %d, log file %s\n", - port, unix_sock_file, log_level, log_file ); - - if (daemonize() == -1) { - fprintf(stderr, "!!! Error forking the daemon! Going away now... :(\n"); - exit(2); - } - - signal(SIGHUP, &sig_hup_handler); - signal(SIGINT, &sig_int_handler); - signal(SIGTERM, &sig_int_handler); - - //init_proc_title( argc, argv ); - //set_proc_title( "opensrf jabber" ); - - launch_server(); - return 0; -} - -void launch_server() { - - log_init(log_level, log_file); - info_handler("Booting jserver-c on port %d and " - "sock file %s", port, unix_sock_file); - - if(!strcmp(listen_ip,"*")) listen_ip = NULL; - - js = jserver_init(); - unlink(unix_sock_file); - if(jserver_connect(js, port, listen_ip, unix_sock_file) < 0) - fatal_handler("Could not connect..."); - - jserver_wait(js); -} - - - - - diff --git a/OpenSRF/src/jserver/jserver-c_session.c b/OpenSRF/src/jserver/jserver-c_session.c deleted file mode 100644 index 7c69c1c798..0000000000 --- a/OpenSRF/src/jserver/jserver-c_session.c +++ /dev/null @@ -1,281 +0,0 @@ -#include "jserver-c_session.h" - -static int xml_error_occured = 0; -static int client_sent_disconnect = 0; - -jserver_session* jserver_session_init() { - - jserver_session* session = safe_malloc(sizeof(jserver_session)); - session->parser_ctxt = xmlCreatePushParserCtxt(sax_handler, session, "", 0, NULL); - session->current_msg = xmlNewDoc(BAD_CAST "1.0"); - session->current_to = strdup(""); - session->current_from = strdup(""); - session->state = 0; - xmlKeepBlanksDefault(0); - return session; -} - -void jserver_session_free(jserver_session* session) { - if(session == NULL) return; - - if( session->parser_ctxt) { - xmlFreeDoc(session->parser_ctxt->myDoc); - xmlFreeParserCtxt(session->parser_ctxt); - } - - free(session->current_username); - free(session->current_resource); - free(session->current_domain); - - xmlCleanupCharEncodingHandlers(); - xmlFreeDoc(session->current_msg); - xmlCleanupParser(); - - free(session); -} - - -int jserver_session_push_data(jserver_session* session, char* data) { - if(session == NULL || data == NULL) return -1; - debug_handler("pushing data into xml parser: %s", data); - xmlParseChunk(session->parser_ctxt, data, strlen(data), 0); - if(xml_error_occured) { - xml_error_occured = 0; - return -1; - } - - if(client_sent_disconnect) { - client_sent_disconnect = 0; - if(session->on_client_finish) - session->on_client_finish(session->blob); - } - - return 0; -} - -void sax_start_doc(void* blob) { - debug_handler("Starting new session doc"); -} - -// --------------------------------------------------------------------------------- -// Our SAX handlers -// --------------------------------------------------------------------------------- -void sax_start_element( - void* blob, const xmlChar *name, const xmlChar **atts) { - - jserver_session* session = (jserver_session*) blob; - if(!session) return; - - debug_handler("jserver-c_session received opening XML node %s", name); - - if(!strcmp(name, "stream:stream")) { - - /* opening a new session */ - free(session->current_domain); - session->current_domain = strdup(sax_xml_attr(atts, "to")); - char* from_domain = sax_xml_attr(atts, "from"); - if(!from_domain) from_domain = ""; - - if(session->current_domain == NULL) { - sax_warning(session->blob, "No 'to' specified in stream opener"); - - } else { - debug_handler("jserver-c_session received opening stream from client on domain %s", - session->current_domain); - - char buf[1024]; - memset(buf,0,1024); - snprintf(buf, 1023, JSTRING_START_STREAM, session->current_domain, from_domain); - - debug_handler("Session Sending: %s", buf); - - session->state = JABBER_STATE_CONNECTING; - if(session->on_login_init) - session->on_login_init(session->blob, buf); - } - return; - } - - if(session->state & JABBER_STATE_CONNECTING) { - /* during the connect shuffle, we have to store off the - username and resource to determine the routing address */ - if(!strcmp(name,"iq")) - session->in_iq = 1; - if(!strcmp(name,"username")) - session->in_uname = 1; - if(!strcmp(name,"resource")) - session->in_resource = 1; - } - - if(session->state & JABBER_STATE_CONNECTED) { - - if(!strcmp(name, "message")) { - /* opening of a new message, build a new doc */ - xmlNodePtr root = xmlNewNode(NULL, name); - dom_add_attrs(root, atts); - xmlNodePtr old_root = xmlDocSetRootElement(session->current_msg, root); - - - free(session->current_to); - - char* from = sax_xml_attr(atts, "from"); - if(from == NULL) from = ""; - char* to = sax_xml_attr(atts, "to"); - if(to == NULL) to = ""; - - session->current_to = strdup(to); - - /* free the old message tree */ - if(old_root) xmlFreeNode(old_root); - - } else { - xmlNodePtr node = xmlNewNode(NULL, name); - dom_add_attrs(node, atts); - xmlAddChild(xmlDocGetRootElement(session->current_msg), node); - } - } -} - -void sax_end_element( void* blob, const xmlChar *name) { - jserver_session* session = (jserver_session*) blob; - if(!session) return; - - if(session->state & JABBER_STATE_CONNECTED) { - if(!strcmp(name, "message")) { - if(session->on_msg_complete) { - - debug_handler("Message is complete, finishing DOC"); - - /* we have to make sure the 'from' address is set.. */ - xmlNodePtr msg = xmlDocGetRootElement(session->current_msg); - if(msg) xmlSetProp(msg, BAD_CAST "from", BAD_CAST session->current_from ); - char* string = _xml_to_string(session->current_msg); - - session->on_msg_complete(session->blob, string, - session->current_from, session->current_to ); - free(string); - } - } - } - - if(session->state & JABBER_STATE_CONNECTING) { - if(session->in_iq) { - if(!strcmp(name, "iq")) { - session->in_iq = 0; - - char buf[1024]; - memset(buf, 0, 1024); - snprintf(buf, 1023, "%s@%s/%s", session->current_username, - session->current_domain, session->current_resource ); - if(session->on_from_discovered) - session->on_from_discovered(session->blob, buf); - - free(session->current_from); - session->current_from = strdup(buf); - debug_handler("Set from address to %s", session->current_from); - session->state = JABBER_STATE_CONNECTED; - if(session->on_login_ok) - session->on_login_ok(session->blob); - - } - } - } - - if(!strcmp(name,"stream:stream")) { - debug_handler("receive end of client session doc"); - client_sent_disconnect = 1; - } -} - -void sax_character( void* blob, const xmlChar *ch, int len) { - jserver_session* session = (jserver_session*) blob; - if(!session) return; - - if(session->state & JABBER_STATE_CONNECTED) { - xmlNodePtr last = xmlGetLastChild( - xmlDocGetRootElement(session->current_msg)); - - xmlNodePtr txt = xmlNewTextLen(ch, len); - xmlAddChild(last, txt); - return; - } - - if(session->state & JABBER_STATE_CONNECTING) { - if(session->in_iq) { - if(session->in_uname) { - free(session->current_username); - session->current_username = strndup((char*) ch, len); - session->in_uname = 0; - } - - if(session->in_resource) { - free(session->current_resource); - session->current_resource = strndup((char*) ch, len); - session->in_resource = 0; - } - } - - } -} - -void sax_warning( void* blob, const char* msg, ... ) { - - jserver_session* session = (jserver_session*) blob; - if(!session) return; - - char buf[1024]; - memset(buf, 0, 1024); - - va_list args; - va_start(args, msg); - vsnprintf(buf, 1023, msg, args); - va_end(args); - warning_handler("XML Warning : %s", buf); - xml_error_occured = 1; -} - - -void dom_add_attrs(xmlNodePtr node, const xmlChar** atts) { - int i; - if (node != NULL && atts != NULL) { - for(i = 0; (atts[i] != NULL); i++) { - if(atts[i+1]) { - xmlNewProp(node, atts[i], atts[i+1]); - i++; - } - } - } -} - -char* sax_xml_attr( const xmlChar** atts, char* attr_name ) { - int i; - if(attr_name == NULL) return NULL; - - if (atts != NULL) { - for(i = 0;(atts[i] != NULL);i++) { - if(!strcmp(atts[i], attr_name)) - if(atts[++i]) - return (char*) atts[i]; - } - } - return NULL; -} - - - -char* _xml_to_string( xmlDocPtr doc ) { - - xmlBufferPtr xmlbuf = xmlBufferCreate(); - xmlNodeDump( xmlbuf, doc, xmlDocGetRootElement(doc), 0, 0); - - char* xml = strdup( (char*) (xmlBufferContent(xmlbuf))); - xmlBufferFree(xmlbuf); - - int l = strlen(xml)-1; - if( xml[l] == 10 || xml[l] == 13 ) - xml[l] = '\0'; - - return xml; - -} - diff --git a/OpenSRF/src/jserver/jserver-c_session.h b/OpenSRF/src/jserver/jserver-c_session.h deleted file mode 100644 index d8e72290b6..0000000000 --- a/OpenSRF/src/jserver/jserver-c_session.h +++ /dev/null @@ -1,121 +0,0 @@ -#define _GNU_SOURCE - -#include "opensrf/utils.h" -#include "opensrf/logging.h" - -#include "jstrings.h" - -#include -#include - -#include -#include -#include -#include /* only for xmlNewInputFromFile() */ -#include -#include -#include - - -/* session states */ -#define JABBER_STATE_CONNECTED 2 -#define JABBER_STATE_CONNECTING 4 -#define JABBER_STATE_IN_MESSAGE 8 - - -struct jserver_session_struct { - - /* our connection state */ - unsigned int state; - - /* incoming XML is parsed with the SAX parser */ - xmlParserCtxtPtr parser_ctxt; - - /* incoming message are shoved into this DOM doc after they are parsed */ - xmlDocPtr current_msg; - - /* we have to grab off the from and to for routing */ - char* current_to; - char* current_from; - - char* current_domain; - char* current_resource; - char* current_username; - - int in_iq; - int in_uname; - int in_resource; - - void* blob; /* callback blob - can be anything that needs passing around */ - void (*on_msg_complete) (void* blob, char* msg_xml, char* from, char* to ); - - /* happens after someone logs in and we've pieced together the from address */ - void (*on_from_discovered) (void* blob, char* from ); - void (*on_login_init) (void* blob, char* reply ); - void (*on_login_ok) (void* blob); - void (*on_client_finish) (void* blob); - -}; -typedef struct jserver_session_struct jserver_session; - - -jserver_session* jserver_session_init(); -void jserver_session_free(jserver_session* session); -char* sax_xml_attr( const xmlChar** atts, char* attr_name ); -int jserver_session_push_data(jserver_session* session, char* data); - -void dom_add_attrs(xmlNodePtr node, const xmlChar** atts); -char* _xml_to_string( xmlDocPtr doc ); - - -// --------------------------------------------------------------------------------- -// Our SAX handlers -// --------------------------------------------------------------------------------- -void sax_start_element( - void *session, const xmlChar *name, const xmlChar **atts); - -void sax_end_element( void* blob, const xmlChar *name); - -void sax_start_doc(void* blob); -//void sax_end_doc(void* blob); - -void sax_character( void* blob, const xmlChar *ch, int len); - -void sax_warning( void* blob, const char* msg, ... ); - -static xmlSAXHandler sax_handler_struct = { - NULL, /* internalSubset */ - NULL, /* isStandalone */ - NULL, /* hasInternalSubset */ - NULL, /* hasExternalSubset */ - NULL, /* resolveEntity */ - NULL, /* getEntity */ - NULL, /* entityDecl */ - NULL, /* notationDecl */ - NULL, /* attributeDecl */ - NULL, /* elementDecl */ - NULL, /* unparsedEntityDecl */ - NULL, /* setDocumentLocator */ - sax_start_doc, /* startDocument */ - NULL, /* endDocument */ - sax_start_element, /* startElement */ - sax_end_element, /* endElement */ - NULL, /* reference */ - sax_character, /* characters */ - NULL, /* ignorableWhitespace */ - NULL, /* processingInstruction */ - NULL, /* comment */ - sax_warning, /* xmlParserWarning */ - sax_warning, /* xmlParserError */ - NULL, /* xmlParserFatalError : unused */ - NULL, /* getParameterEntity */ - NULL, /* cdataBlock; */ - NULL, /* externalSubset; */ - 1, - NULL, - NULL, /* startElementNs */ - NULL, /* endElementNs */ - NULL /* xmlStructuredErrorFunc */ -}; - -static const xmlSAXHandlerPtr sax_handler = &sax_handler_struct; diff --git a/OpenSRF/src/jserver/jstrings.h b/OpenSRF/src/jserver/jstrings.h deleted file mode 100644 index bc9878ce97..0000000000 --- a/OpenSRF/src/jserver/jstrings.h +++ /dev/null @@ -1,17 +0,0 @@ -/* ------------------------------------------------ - some pre-packaged Jabber XML - ------------------------------------------------ */ - -#ifndef _JSTRINGS_H_ -#define _JSTRINGS_H_ - -#define JSTRING_START_STREAM "" /* this will need to by dynamic when we add login handling */ - - -#define JSTRING_PARSE_ERROR "syntax error" - -#define JSTRING_LOGIN_OK "" - -#define JSTRING_NO_RECIPIENT "NOT ADDING BODY" - -#endif diff --git a/OpenSRF/src/jserver/osrf_chat.c b/OpenSRF/src/jserver/osrf_chat.c new file mode 100644 index 0000000000..98a100df8d --- /dev/null +++ b/OpenSRF/src/jserver/osrf_chat.c @@ -0,0 +1,757 @@ +/* +Copyright (C) 2005 Georgia Public Library Service +Bill Erickson + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "osrf_chat.h" +#include +#include + +int __osrfChatXMLErrorOcurred = 0; +int __osrfChatClientSentDisconnect = 0; + +/* shorter version of strcmp */ +static int eq(const char* a, const char* b) { return (a && b && !strcmp(a,b)); } + +/* gnarly debug function */ +static void chatdbg( osrfChatServer* server ) { + + if(!server) return; + + growing_buffer* buf = buffer_init(256); + + buffer_add(buf, "---------------------------------------------------------------------\n"); + + buffer_fadd(buf, + "ChopChop Debug:\n" + "Connections: %lu\n" + "Named nodes in hash: %lu\n" + "Domain: %s\n" + "Port: %d\n" + "S2S Port: %d\n" + "-------------------------------------------------------\n", + osrfListGetCount(server->nodeList), osrfHashGetCount(server->nodeHash), + server->domain, server->port, server->s2sport ); + + osrfListIterator* itr = osrfNewListIterator(server->nodeList); + osrfChatNode* node; + + while( (node = osrfListIteratorNext(itr)) ) { + + buffer_fadd( buf, + "sockid: %d\n" + "Remote: %s\n" + "State: %d\n" + "XMLState: %d\n" + "In Parse: %d\n" + "to: %s\n" + "Resource: %s\n" + "Username: %s\n" + "Domain: %s\n" + "Authkey: %s\n" + "type: %d\n" + "-------------------------------------------------------\n", + node->sockid, node->remote, node->state, node->xmlstate, node->inparse, + node->to, node->resource, node->username, node->domain, node->authkey, node->type ); + } + + debug_handler("DEBUG:\n%s", buf->buf ); + buffer_free(buf); + osrfListIteratorFree(itr); +} + +osrfChatServer* osrfNewChatServer( char* domain, char* secret, int s2sport ) { + if(!(domain && secret)) return NULL; + + osrfChatServer* server = safe_malloc(sizeof(osrfChatServer)); + + server->nodeHash = osrfNewHash(); + server->nodeList = osrfNewList(); + server->deadNodes = osrfNewList(); + server->nodeList->freeItem = &osrfChatNodeFree; + server->domain = strdup(domain); + server->s2sport = s2sport; + + server->mgr = safe_malloc(sizeof(socket_manager)); + server->mgr->data_received = &osrfChatHandleData; + server->mgr->blob = server; + server->mgr->on_socket_closed = &osrfChatSocketClosed; + + if(secret) server->secret = strdup(secret); + return server; +} + +void osrfChatCleanupClients( osrfChatServer* server ) { + if(server) { + osrfListFree(server->deadNodes); + server->deadNodes = osrfNewList(); + } +} + + + +osrfChatNode* osrfNewChatNode( int sockid, char* domain ) { + if(sockid < 1 || !domain) return NULL; + osrfChatNode* node = safe_malloc(sizeof(osrfChatNode)); + node->state = OSRF_CHAT_STATE_NONE; + node->msgs = NULL; /* only s2s nodes cache messages */ + node->parserCtx = xmlCreatePushParserCtxt(osrfChatSaxHandler, node, "", 0, NULL); + node->msgDoc = xmlNewDoc(BAD_CAST "1.0"); + node->domain = strdup(domain); + xmlKeepBlanksDefault(0); + node->authkey = NULL; + node->username = NULL; + node->resource = NULL; + node->to = NULL; + node->type = 0; + return node; +} + + +osrfChatNode* osrfNewChatS2SNode( char* domain, char* remote ) { + if(!(domain && remote)) return NULL; + osrfChatNode* n = osrfNewChatNode( 1, domain ); + n->state = OSRF_CHAT_STATE_S2S_CHALLENGE; + n->sockid = -1; + n->remote = strdup(remote); + n->msgs = osrfNewList(); + n->msgs->freeItem = &osrfChatS2SMessageFree; + n->type = 1; + return n; +} + +void osrfChatS2SMessageFree(void* n) { free(n); } + +void osrfChatNodeFree( void* node ) { + if(!node) return; + osrfChatNode* n = (osrfChatNode*) node; + + /* we can't free messages that are mid-parse because the + we can't free the parser context */ + if(n->inparse) { + n->inparse = 0; + osrfListPush(n->parent->deadNodes, n); + return; + } + + free(n->remote); + free(n->to); + free(n->username); + free(n->resource); + free(n->domain); + free(n->authkey); + + osrfListFree(n->msgs); + + if(n->parserCtx) { + xmlFreeDoc(n->parserCtx->myDoc); + xmlFreeParserCtxt(n->parserCtx); + } + + xmlFreeDoc(n->msgDoc); + free(n); +} + + + +int osrfChatServerConnect( osrfChatServer* cs, int port, int s2sport, char* listenAddr ) { + if(!(cs && port && listenAddr)) return -1; + cs->port = port; + cs->s2sport = s2sport; + if( socket_open_tcp_server(cs->mgr, port, listenAddr ) < 0 ) + return -1; + if( socket_open_tcp_server(cs->mgr, s2sport, listenAddr ) < 0 ) + return -1; + return 0; +} + + +int osrfChatServerWait( osrfChatServer* server ) { + if(!server) return -1; + while(1) { + if(socket_wait_all(server->mgr, -1) < 0) + warning_handler( "jserver_wait(): socket_wait_all() returned error"); + } + return -1; +} + + +void osrfChatServerFree(osrfChatServer* server ) { + if(!server) return; + osrfHashFree(server->nodeHash); + osrfListFree(server->nodeList); + free(server->mgr); + free(server->secret); +} + + +void osrfChatHandleData( void* cs, + socket_manager* mgr, int sockid, char* data, int parent_id ) { + + if(!(cs && mgr && sockid && data)) return; + + osrfChatServer* server = (osrfChatServer*) cs; + + osrfChatNode* node = osrfListGetIndex( server->nodeList, sockid ); + + if(!node) { + debug_handler("Adding new connection for sockid %d", sockid ); + node = osrfChatAddNode( server, sockid ); + } + + if(node) { + if( (osrfChatPushData( server, node, data ) == -1) ) { + warning_handler("Node at socket %d received bad XML, disconnecting...", sockid ); + osrfChatSendRaw( node, OSRF_CHAT_PARSE_ERROR ); + osrfChatRemoveNode( server, node ); + } + } + + osrfChatCleanupClients(server); /* clean up old dead clients */ +} + + +void osrfChatSocketClosed( void* blob, int sockid ) { + if(!blob) return; + osrfChatServer* server = (osrfChatServer*) blob; + osrfChatNode* node = osrfListGetIndex(server->nodeList, sockid); + osrfChatRemoveNode( server, node ); +} + +osrfChatNode* osrfChatAddNode( osrfChatServer* server, int sockid ) { + if(!(server && sockid)) return NULL; + osrfChatNode* node = osrfNewChatNode(sockid, server->domain); + node->parent = server; + node->sockid = sockid; + osrfListSet( server->nodeList, node, sockid ); + return node; +} + +void osrfChatRemoveNode( osrfChatServer* server, osrfChatNode* node ) { + if(!(server && node)) return; + socket_disconnect(server->mgr, node->sockid); + if(node->remote) + osrfHashRemove( server->nodeHash, node->remote ); + osrfListRemove( server->nodeList, node->sockid ); /* this will free it */ +} + +int osrfChatSendRaw( osrfChatNode* node, char* msgXML ) { + if(!(node && msgXML)) return -1; + return socket_send( node->sockid, msgXML ); +} + + + + +void osrfChatNodeFinish( osrfChatServer* server, osrfChatNode* node ) { + if(!(server && node)) return; + osrfChatSendRaw( node, ""); + osrfChatRemoveNode( server, node ); +} + + +int osrfChatSend( osrfChatServer* cs, osrfChatNode* node, char* toAddr, char* fromAddr, char* msgXML ) { + if(!(cs && node && toAddr && msgXML)) return -1; + + int l = strlen(toAddr); + char dombuf[l]; + bzero(dombuf, l); + jid_get_domain( toAddr, dombuf ); + + if( eq( dombuf, cs->domain ) ) { /* this is to a user we host */ + + osrfChatNode* tonode = osrfHashGet(cs->nodeHash, toAddr); + if(tonode) { + osrfChatSendRaw( tonode, msgXML ); + + } else { + + /* send an error message saying we don't have this connection */ + warning_handler("We have no connection for %s", toAddr); + char* xml = va_list_to_string( OSRF_CHAT_NO_RECIPIENT, toAddr, fromAddr ); + osrfChatSendRaw( node, xml ); + free(xml); + } + + } else { + + osrfChatNode* tonode = osrfHashGet(cs->nodeHash, dombuf); + if(tonode) { + if( tonode->state == OSRF_CHAT_STATE_CONNECTED ) { + debug_handler("Routing message to server %s", dombuf); + osrfChatSendRaw( tonode, msgXML ); + + } else { + info_handler("Received s2s message and we're still trying to connect...caching"); + osrfListPush( tonode->msgs, strdup(msgXML) ); + } + + } else { + + if( osrfChatInitS2S( cs, dombuf, toAddr, msgXML ) != 0 ) { + warning_handler("We are unable to connect to remote server %s for recipient %s", dombuf, toAddr); + char* xml = va_list_to_string( OSRF_CHAT_NO_RECIPIENT, toAddr, fromAddr ); + osrfChatSendRaw( node, xml ); + free(xml); + } + } + } + + return 0; +} + + +/* +void osrfChatCacheS2SMessage( char* toAddr, char* msgXML, osrfChatNode* snode ) { + if(!(toAddr && msgXML)) return; + osrfChatS2SMessage* msg = safe_malloc(sizeof(osrfChatS2SMessage)); + msg->toAddr = strdup(toAddr); + msg->msgXML = strdup(msgXML); + info_handler("Pushing client message onto s2s queue waiting for connect... "); + osrfListPush( snode->msgs, msgXML ); +} +*/ + + +int osrfChatInitS2S( osrfChatServer* cs, char* remote, char* toAddr, char* msgXML ) { + if(!(cs && remote && toAddr && msgXML)) return -1; + + info_handler("Initing server2server connection to domain %s", remote ); + osrfChatNode* snode = osrfNewChatS2SNode( cs->domain, remote ); + snode->parent = cs; + + /* try to connect to the remote site */ + snode->sockid = socket_open_tcp_client(cs->mgr, cs->s2sport, remote); + if(snode->sockid < 1) { + warning_handler("Unable to connect to remote server at %s", remote ); + return -1; + } + + /* store the message we were supposed to deliver until we're fully connected */ + //osrfChatCacheS2SMessage( toAddr, msgXML, snode ); + osrfListPush( snode->msgs, strdup(msgXML) ); + osrfHashSet(cs->nodeHash, snode, remote ); + osrfListSet(cs->nodeList, snode, snode->sockid ); + + /* send the initial s2s request */ + osrfChatSendRaw( snode, OSRF_CHAT_S2S_INIT ); + + debug_handler("Added new s2s node..."); + chatdbg(cs); + + return 0; +} + + +/* commence SAX handling code */ + +int osrfChatPushData( osrfChatServer* server, osrfChatNode* node, char* data ) { + if(!(node && data)) return -1; + + chatdbg(server); + + debug_handler("pushing data into xml parser for node %d:\n%s", node->sockid, data); + node->inparse = 1; + xmlParseChunk(node->parserCtx, data, strlen(data), 0); + node->inparse = 0; + + if(__osrfChatXMLErrorOcurred) { + __osrfChatXMLErrorOcurred = 0; + return -1; + } + + /* we can't do cleanup of the XML handlers while in the middle of a + data push, so set flags in the data push and doe the cleanup here */ + /* + if(__osrfChatClientSentDisconnect) { + __osrfChatClientSentDisconnect = 0; + osrfChatNodeFinish( server, node ); + } + */ + + return 0; +} + + +void osrfChatStartStream( void* blob ) { + debug_handler("Starting new client stream..."); +} + + +void osrfChatStartElement( void* blob, const xmlChar *name, const xmlChar **atts ) { + if(!(blob && name)) return; + osrfChatNode* node = (osrfChatNode*) blob; + + int status = -1; + char* nm = (char*) name; + + debug_handler("Starting element %s with namespace %s", nm, xmlSaxAttr(atts, "xmlns") ); + + switch( node->state ) { + + case OSRF_CHAT_STATE_NONE: + status = osrfChatHandleNewConnection( node, nm, atts ); + break; + + case OSRF_CHAT_STATE_CONNECTING: + status = osrfChatHandleConnecting( node, nm, atts ); + break; + + case OSRF_CHAT_STATE_CONNECTED: + status = osrfChatHandleConnected( node, nm, atts ); + break; + + case OSRF_CHAT_STATE_S2S_CHALLENGE: + status = osrfChatHandleS2SChallenge( node, nm, atts ); + break; + + case OSRF_CHAT_STATE_S2S_RESPONSE: /* server waiting for client response to challenge */ + if(eq(nm, "db:result")) { + char* remote = xmlSaxAttr(atts, "from"); + if(remote) node->remote = strdup(remote); /* copy off the client's id */ + status = 0; + node->xmlstate |= OSRF_CHAT_STATE_INS2SRESULT; + } else status = -1; + break; + + case OSRF_CHAT_STATE_S2S_VERIFY: /* client : waiting for server verify message */ + if(eq(nm, "db:verify")) { + char* id = xmlSaxAttr( atts, "id" ); + if(id) { + char* xml = va_list_to_string( OSRF_CHAT_S2S_VERIFY_RESPONSE, + node->remote, node->domain, id ); + osrfChatSendRaw( node, xml ); + free(xml); + node->state = OSRF_CHAT_STATE_S2S_VERIFY_FINAL; + status = 0; + } + } + break; + + case OSRF_CHAT_STATE_S2S_VERIFY_RESPONSE: /* server waiting for client verify response */ + case OSRF_CHAT_STATE_S2S_VERIFY_FINAL: /* client waitig for final verify */ + status = osrfChatHandleS2SConnected( node, nm, atts ); + break; + + } + + if(status != 0) + osrfChatParseError( node, "We don't know how to handle the XML data received" ); +} + +#define CHAT_CHECK_VARS(x,y,z) if(!(x && y)) return -1; if(z) debug_handler(z); + + + +int osrfChatHandleS2SConnected( osrfChatNode* node, const char* name, const xmlChar**atts ) { + CHAT_CHECK_VARS(node, name, "osrfChatHandleS2SConnected" ); + + int status = -1; + + if(eq(name,"db:verify")) { /* server receives verify from client */ + char* xml = va_list_to_string(OSRF_CHAT_S2S_VERIFY_FINAL, node->domain, node->remote ); + osrfChatSendRaw(node, xml ); + free(xml); + status = 0; + } + + if(eq(name, "db:result")) { + /* send all the messages that we have queued for this server */ + node->state = OSRF_CHAT_STATE_CONNECTED; + osrfListIterator* itr = osrfNewListIterator(node->msgs); + + char* xml; + while( (xml = (char*) osrfListIteratorNext(itr)) ) { + xmlDocPtr doc = xmlParseMemory(xml, strlen(xml)); + if(doc) { + char* from = (char*) xmlGetProp(xmlDocGetRootElement(doc), BAD_CAST "from"); + char* to = (char*) xmlGetProp(xmlDocGetRootElement(doc), BAD_CAST "to"); + osrfChatSend( node->parent, node, to, from, xml ); + debug_handler("Sending cached message from %s to %s", from, to); + xmlFree(to); xmlFree(from); + xmlFreeDoc(doc); + } + } + + osrfListIteratorFree(itr); + osrfListFree(node->msgs); + node->msgs = NULL; + status = 0; + } + + if(status == 0) { + info_handler("Successfully made S2S connection to %s", node->remote ); + node->state = OSRF_CHAT_STATE_CONNECTED; + node->xmlstate = 0; + } + + return status; +} + + +/** check the namespace of the stream message to see if it's a server or client connection */ +int osrfChatHandleNewConnection( osrfChatNode* node, const char* name, const xmlChar** atts ) { + CHAT_CHECK_VARS(node, name, "osrfChatHandleNewConnection()"); + + if(!eq(name, "stream:stream")) return -1; + + node->authkey = osrfChatMkAuthKey(); + char* ns = xmlSaxAttr(atts, "xmlns"); + if(!ns) return -1; + + if(eq(ns, "jabber:client")) { /* client connection */ + + char* domain = xmlSaxAttr( atts, "to" ); + if(!domain) return -1; + + if(!eq(domain, node->domain)) { + warning_handler("Client attempting to connect to invalid domain"); + return -1; + } + + char* buf = va_list_to_string( OSRF_CHAT_START_STREAM, domain, node->authkey ); + node->state = OSRF_CHAT_STATE_CONNECTING; + + debug_handler("Server responding to connect message with\n%s\n", buf ); + osrfChatSendRaw( node, buf ); + free(buf); + return 0; + } + + /* server to server init */ + if(eq(ns, "jabber:server")) { /* client connection */ + info_handler("We received a new server 2 server connection, generating auth key..."); + char* xml = va_list_to_string( OSRF_CHAT_S2S_CHALLENGE, node->authkey ); + osrfChatSendRaw( node, xml ); + free(xml); + node->state = OSRF_CHAT_STATE_S2S_RESPONSE; /* the next message should be the response */ + node->type = 1; + return 0; + } + + return -1; +} + + + +char* osrfChatMkAuthKey() { + char keybuf[112]; + bzero(keybuf, 112); + snprintf(keybuf, 111, "%d%d%s", (int) time(NULL), getpid(), getenv("HOSTNAME")); + return strdup(shahash(keybuf)); +} + +int osrfChatHandleConnecting( osrfChatNode* node, const char* name, const xmlChar** atts ) { + CHAT_CHECK_VARS(node, name, "osrfChatHandleConnecting()"); + debug_handler("Handling connect node %s", name ); + + if(eq(name, "iq")) node->xmlstate |= OSRF_CHAT_STATE_INIQ; + else if(eq(name,"username")) node->xmlstate |= OSRF_CHAT_STATE_INUSERNAME; + else if(eq(name,"resource")) node->xmlstate |= OSRF_CHAT_STATE_INRESOURCE; + return 0; +} + +int osrfChatHandleConnected( osrfChatNode* node, const char* name, const xmlChar** atts ) { + CHAT_CHECK_VARS(node, name, "osrfChatHandleConnected()"); + + if(eq(name,"message")) { + + /* drop the old message and start with a new one */ + xmlNodePtr root = xmlNewNode(NULL, name); + xmlAddAttrs(root, atts); + xmlNodePtr oldRoot = xmlDocSetRootElement(node->msgDoc, root); + free(node->to); + + char* to = xmlSaxAttr(atts, "to"); + if(!to) to = ""; + + node->to = strdup(to); + if(oldRoot) xmlFreeNode(oldRoot); + node->xmlstate = OSRF_CHAT_STATE_INMESSAGE; + + } else { + + /* all non "message" nodes are simply added to the message */ + xmlNodePtr nodep = xmlNewNode(NULL, name); + xmlAddAttrs(nodep, atts); + xmlAddChild(xmlDocGetRootElement(node->msgDoc), nodep); + } + + return 0; +} + +/* takes s2s secret, hashdomain, and the s2s auth token */ +static char* osrfChatGenerateS2SKey( char* secret, char* hashdomain, char* authtoken ) { + if(!(secret && hashdomain && authtoken)) return NULL; + info_handler("Generating s2s key with auth token: %s", authtoken ); + char* a = shahash(secret); + debug_handler("S2S secret hash: %s", a); + char* b = va_list_to_string("%s%s", a, hashdomain); + char* c = shahash(b); + debug_handler("S2S intermediate hash: %s", c); + char* d = va_list_to_string("%s%s", c, authtoken); + char* e = strdup(shahash(d)); + free(b); free(d); + return e; +} + +int osrfChatHandleS2SChallenge( osrfChatNode* node, const char* name, const xmlChar** atts ) { + CHAT_CHECK_VARS(node, name, "osrfChatHandleS2SChallenge()"); + +/* here we respond to the stream challenge */ + if(eq(name, "stream:stream")) { + char* id = xmlSaxAttr(atts, "id"); + if(id) { + /* we use our domain in the s2s challenge hash */ + char* d = osrfChatGenerateS2SKey(node->parent->secret, node->domain, id ); + char* e = va_list_to_string(OSRF_CHAT_S2S_RESPONSE, node->remote, node->domain, d ); + info_handler("Answering s2s challenge with key: %s", e ); + osrfChatSendRaw( node, e ); + free(d); free(e); + node->state = OSRF_CHAT_STATE_S2S_VERIFY; + return 0; + } + } + + return -1; +} + +/* +int osrfChatHandleS2SResponse( osrfChatNode* node, const char* name, const xmlChar** atts ) { + CHAT_CHECK_VARS(node, name, "osrfChatHandleS2SResponse()"); + + if(eq(name, "db:result")) { + node->xmlstate |= OSRF_CHAT_STATE_INS2SRESULT; + return 0; + } + + return -1; +} +*/ + + + +void osrfChatEndElement( void* blob, const xmlChar* name ) { + if(!(blob && name)) return; + osrfChatNode* node = (osrfChatNode*) blob; + + char* nm = (char*) name; + + if(eq(nm,"stream:stream")) { + osrfChatNodeFinish( node->parent, node ); + return; + } + + if( node->state == OSRF_CHAT_STATE_CONNECTED ) { + if(eq(nm, "message")) { + + xmlNodePtr msg = xmlDocGetRootElement(node->msgDoc); + if(msg && node->type == 0) + xmlSetProp(msg, BAD_CAST "from", BAD_CAST node->remote ); + char* string = xmlDocToString(node->msgDoc, 0 ); + + char* from = (char*) xmlGetProp(msg, BAD_CAST "from"); + debug_handler( "Routing message to %s\n%s\n", node->to, from, string ); + osrfChatSend( node->parent, node, node->to, from, string ); + xmlFree(from); + free(string); + } + } + + if( node->state == OSRF_CHAT_STATE_CONNECTING ) { + if( node->xmlstate & OSRF_CHAT_STATE_INIQ ) { + + if(eq(nm, "iq")) { + node->xmlstate &= ~OSRF_CHAT_STATE_INIQ; + node->remote = va_list_to_string( + "%s@%s/%s", node->username, node->domain, node->resource ); + + debug_handler("Setting remote address to %s", node->remote ); + osrfChatSendRaw( node, OSRF_CHAT_LOGIN_OK ); + osrfHashSet( node->parent->nodeHash, node, node->remote ); + node->state = OSRF_CHAT_STATE_CONNECTED; + } + } + } +} + + +void osrfChatHandleCharacter( void* blob, const xmlChar *ch, int len) { + if(!(blob && ch && len)) return; + osrfChatNode* node = (osrfChatNode*) blob; + + /* + debug_handler("Char Handler: state %d, xmlstate %d, chardata %s", + node->state, node->xmlstate, (char*) ch ); + */ + + if( node->state == OSRF_CHAT_STATE_CONNECTING ) { + if( node->xmlstate & OSRF_CHAT_STATE_INIQ ) { + + if( node->xmlstate & OSRF_CHAT_STATE_INUSERNAME ) { + free(node->username); + node->username = strndup((char*) ch, len); + node->xmlstate &= ~OSRF_CHAT_STATE_INUSERNAME; + } + + if( node->xmlstate & OSRF_CHAT_STATE_INRESOURCE ) { + free(node->resource); + node->resource = strndup((char*) ch, len); + node->xmlstate &= ~OSRF_CHAT_STATE_INRESOURCE; + } + } + + return; + } + + if( node->state == OSRF_CHAT_STATE_CONNECTED ) { + xmlNodePtr last = xmlGetLastChild(xmlDocGetRootElement(node->msgDoc)); + xmlNodePtr txt = xmlNewTextLen(ch, len); + xmlAddChild(last, txt); + return; + } + + if( node->state == OSRF_CHAT_STATE_S2S_RESPONSE && + (node->xmlstate & OSRF_CHAT_STATE_INS2SRESULT) ) { + + char* key = strndup((char*) ch, len); + debug_handler("Got s2s key from %s : %s", node->remote, key ); + char* e = osrfChatGenerateS2SKey(node->parent->secret, node->remote, node->authkey ); + info_handler("\nReceived s2s key from server: %s\nKey should be: %s", key, e ); + + if(eq(key, e)) { + char* msg = va_list_to_string(OSRF_CHAT_S2S_VERIFY_REQUEST, + node->authkey, node->domain, node->remote, e ); + osrfChatSendRaw(node, msg ); + free(msg); + node->state = OSRF_CHAT_STATE_S2S_VERIFY_RESPONSE; + node->xmlstate = 0; + + } else { + warning_handler("Server2Server keys do not match!"); + } + + /* do the hash dance again */ + } + +} + + +void osrfChatParseError( void* blob, const char* msg, ... ) { + + __osrfChatXMLErrorOcurred = 1; +} + + + + diff --git a/OpenSRF/src/jserver/osrf_chat.h b/OpenSRF/src/jserver/osrf_chat.h new file mode 100644 index 0000000000..3e5446cbdd --- /dev/null +++ b/OpenSRF/src/jserver/osrf_chat.h @@ -0,0 +1,263 @@ +/* +Copyright (C) 2005 Georgia Public Library Service +Bill Erickson + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#ifndef OSRF_CHAT_H +#define OSRF_CHAT_H + + +/* opensrf headers */ +#include "opensrf/utils.h" +#include "opensrf/osrf_hash.h" +#include "opensrf/osrf_list.h" +#include "opensrf/logging.h" +#include "opensrf/xml_utils.h" +#include "opensrf/socket_bundle.h" +#include "opensrf/sha.h" +#include "opensrf/transport_message.h" + +/* libxml2 headers */ +#include +#include +#include +#include + +/* client to server XML */ +#define OSRF_CHAT_START_STREAM "" + +#define OSRF_CHAT_PARSE_ERROR ""\ + "" \ + "syntax error" + +#define OSRF_CHAT_LOGIN_OK "" + +#define OSRF_CHAT_NO_RECIPIENT ""\ + ""\ + "NOT ADDING BODY" + +/* ---------------------------------------------------------------------------------- */ +/* server to server XML */ + +// client to server init +#define OSRF_CHAT_S2S_INIT "" + +// server to client challenge +#define OSRF_CHAT_S2S_CHALLENGE "" + +// client to server challenge response +#define OSRF_CHAT_S2S_RESPONSE "%s" + +// server to client verify +#define OSRF_CHAT_S2S_VERIFY_REQUEST "%s" + +// client to server verify response +#define OSRF_CHAT_S2S_VERIFY_RESPONSE "" + +//server to client final verification +#define OSRF_CHAT_S2S_VERIFY_FINAL "" + + +/* c2s states */ +#define OSRF_CHAT_STATE_NONE 0 /* blank node */ +#define OSRF_CHAT_STATE_CONNECTING 1 /* we have received the opening stream */ +#define OSRF_CHAT_STATE_CONNECTED 2 /* we have sent the OK/result message */ + +/* s2s states */ +#define OSRF_CHAT_STATE_S2S_CHALLENGE 4 /* client : waiting for the challenge */ +#define OSRF_CHAT_STATE_S2S_RESPONSE 5 /* server : waiting for the challenge response */ +#define OSRF_CHAT_STATE_S2S_VERIFY 6 /* client : waiting for verify message */ +#define OSRF_CHAT_STATE_S2S_VERIFY_RESPONSE 7 /* server : waiting for verify response */ +#define OSRF_CHAT_STATE_S2S_VERIFY_FINAL 8 /* client : waiting for final verify response */ + +/* xml parser states */ +#define OSRF_CHAT_STATE_INMESSAGE 1 +#define OSRF_CHAT_STATE_INIQ 2 +#define OSRF_CHAT_STATE_INUSERNAME 4 +#define OSRF_CHAT_STATE_INRESOURCE 8 +#define OSRF_CHAT_STATE_INS2SRESULT 8 +#define OSRF_CHAT_STATE_INS2SVERIFY 8 + + +struct __osrfChatNodeStruct { + + int sockid; /* our socket id */ + + int type; /* 0 for client, 1 for server */ + + /* for clients this is the full JID of the client that connected to this server. + for servers it's the domain (network id) of the server we're connected to */ + char* remote; + + + int state; /* for the various stages of connectivity and parsing */ + int xmlstate; /* what part of the message are we currently parsing */ + int inparse; /* true if we are currently parsing a chunk of XML. If so, we can't + free the node. we have to cache it and free it later */ + + char* to; /* The JID where the current message is being routed */ + + char* domain; /* the domain, resource, and username of our connecting entity. */ + char* resource; /* for s2s nodes, resource and username will be empty . */ + char* username; + + char* authkey; /* when doing any auth negotiation, this is the auth seed hash */ + + osrfList* msgs; /* if we're a server node we may have a pool of messages waiting to be delivered */ + + xmlParserCtxtPtr parserCtx; + xmlDocPtr msgDoc; + struct __osrfChatServerStruct* parent; + +}; +typedef struct __osrfChatNodeStruct osrfChatNode; + +struct __osrfChatS2SMessageStruct { + char* toAddr; + char* msgXML; +}; +typedef struct __osrfChatS2SMessageStruct osrfChatS2SMessage; + +struct __osrfChatServerStruct { + osrfHash* nodeHash; /* sometimes we need hash (remote id) lookup, sometimes we need socket id lookup */ + osrfList* nodeList; + osrfList* deadNodes; /* collection of nodes to free when we get a chance */ + socket_manager* mgr; + char* secret; /* shared S2S secret */ + char* domain; /* the domain this server hosts */ + int s2sport; + int port; +}; + +typedef struct __osrfChatServerStruct osrfChatServer; + + +void osrfChatCacheS2SMessage( char* toAddr, char* msgXML, osrfChatNode* snode ); + +osrfChatNode* osrfNewChatS2SNode( char* domain, char* remote ); +osrfChatNode* osrfNewChatNode( int sockid, char* domain ); +void osrfChatNodeFree( void* node ); + +/* @param s2sSecret The Server to server secret. OK to leave NULL if no + server to server communication is expected + */ +osrfChatServer* osrfNewChatServer( char* domain, char* s2sSecret, int s2sport ); + +int osrfChatServerConnect( osrfChatServer* cs, int port, int s2sport, char* listenAddr ); + +int osrfChatServerWait( osrfChatServer* server ); +void osrfChatServerFree(osrfChatServer* cs); + +void osrfChatHandleData( void* cs, + socket_manager* mgr, int sockid, char* data, int parent_id ); + + +/* removes dead nodes that have been cached due to mid-parse removals */ +void osrfChatCleanupClients( osrfChatServer* server ); + + +osrfChatNode* osrfChatAddNode( osrfChatServer* server, int sockid ); + + +void osrfChatRemoveNode( osrfChatServer* server, osrfChatNode* node ); + +/** pushes new data into the nodes parser */ +int osrfChatPushData( osrfChatServer* server, osrfChatNode* node, char* data ); + + +void osrfChatSocketClosed( void* blob, int sockid ); + +/** + Sends msgXML to the client with remote 'toAddr'. if we have no connection + to 'toAddr' and the domain for 'toAddr' is different than our hosted domain + we attempt to send the message to the domain found in 'toAddr'. + */ +int osrfChatSend( osrfChatServer* cs, osrfChatNode* node, char* toAddr, char* fromAddr, char* msgXML ); + +int osrfChatSendRaw( osrfChatNode* node, char* xml ); + + +void osrfChatNodeFinish( osrfChatServer* server, osrfChatNode* node ); + +/* initializes the negotiation of a server to server connection */ +int osrfChatInitS2S( osrfChatServer* cs, char* remote, char* toAddr, char* msgXML ); + + +void osrfChatStartStream( void* blob ); +void osrfChatStartElement( void* blob, const xmlChar *name, const xmlChar **atts ); +void osrfChatEndElement( void* blob, const xmlChar* name ); +void osrfChatHandleCharacter(void* blob, const xmlChar *ch, int len); +void osrfChatParseError( void* blob, const char* msg, ... ); + +int osrfChatHandleNewConnection( osrfChatNode* node, const char* name, const xmlChar** atts ); +int osrfChatHandleConnecting( osrfChatNode* node, const char* name, const xmlChar** atts ); +int osrfChatHandleConnected( osrfChatNode* node, const char* name, const xmlChar** atts ); +int osrfChatHandleS2SInit( osrfChatNode* node, const char* name, const xmlChar** atts ); +int osrfChatHandleS2SChallenge( osrfChatNode* node, const char* name, const xmlChar** atts ); +int osrfChatHandleS2SResponse( osrfChatNode* node, const char* name, const xmlChar** atts ); + +int osrfChatHandleS2SConnected( osrfChatNode* node, const char* nm, const xmlChar**atts ); + +void osrfChatS2SMessageFree(void* n); + + + +/* generates a random sha1 hex key */ +char* osrfChatMkAuthKey(); + +static xmlSAXHandler osrfChatSaxHandlerStruct = { + NULL, /* internalSubset */ + NULL, /* isStandalone */ + NULL, /* hasInternalSubset */ + NULL, /* hasExternalSubset */ + NULL, /* resolveEntity */ + NULL, /* getEntity */ + NULL, /* entityDecl */ + NULL, /* notationDecl */ + NULL, /* attributeDecl */ + NULL, /* elementDecl */ + NULL, /* unparsedEntityDecl */ + NULL, /* setDocumentLocator */ + osrfChatStartStream, /* startDocument */ + NULL, /* endDocument */ + osrfChatStartElement, /* startElement */ + osrfChatEndElement, /* endElement */ + NULL, /* reference */ + osrfChatHandleCharacter, /* characters */ + NULL, /* ignorableWhitespace */ + NULL, /* processingInstruction */ + NULL, /* comment */ + osrfChatParseError, /* xmlParserWarning */ + osrfChatParseError, /* xmlParserError */ + NULL, /* xmlParserFatalError : unused */ + NULL, /* getParameterEntity */ + NULL, /* cdataBlock; */ + NULL, /* externalSubset; */ + 1, + NULL, + NULL, /* startElementNs */ + NULL, /* endElementNs */ + NULL /* xmlStructuredErrorFunc */ +}; + +static const xmlSAXHandlerPtr osrfChatSaxHandler = &osrfChatSaxHandlerStruct; + + +#endif + + diff --git a/OpenSRF/src/jserver/osrf_chat_main.c b/OpenSRF/src/jserver/osrf_chat_main.c new file mode 100644 index 0000000000..64ae958535 --- /dev/null +++ b/OpenSRF/src/jserver/osrf_chat_main.c @@ -0,0 +1,56 @@ +#include "osrf_chat.h" +#include "opensrf/osrfConfig.h" +#include +#include "opensrf/logging.h" + + +int main( int argc, char* argv[] ) { + + if( argc < 3 ) { + fprintf( stderr, "Usage: %s \n", argv[0] ); + exit(0); + } + + osrfConfig* cfg = osrfConfigInit( argv[1], argv[2] ); + + init_proc_title( argc, argv ); + set_proc_title( "ChopChop" ); + + char* domain = osrfConfigGetValue(cfg, "/domain"); + char* secret = osrfConfigGetValue(cfg, "/secret"); + char* sport = osrfConfigGetValue(cfg, "/port"); + char* s2sport = osrfConfigGetValue(cfg, "/s2sport"); + char* listenaddr = osrfConfigGetValue(cfg, "/listen_address"); + char* llevel = osrfConfigGetValue(cfg, "/loglevel"); + char* lfile = osrfConfigGetValue(cfg, "/logfile"); + + if(!(domain && secret && sport && listenaddr && llevel && lfile && s2sport)) { + fprintf(stderr, "Configuration error for ChopChop - missing key ingredient\n"); + return -1; + } + + int port = atoi(sport); + int s2port = atoi(s2sport); + int level = atoi(llevel); + log_init(level, lfile); + + fprintf(stderr, "Attempting to launch ChopChop with:\n" + "domain: %s\nport: %s\nlisten address: %s\nlog level: %s\nlog file: %s\n", + domain, sport, listenaddr, llevel, lfile ); + + osrfChatServer* server = osrfNewChatServer(domain, secret, s2port); + + if( osrfChatServerConnect( server, port, s2port, listenaddr ) != 0 ) + return fatal_handler("ChopChop unable to bind to port %d on %s", port, listenaddr); + + daemonize(); + osrfChatServerWait( server ); + + osrfChatServerFree( server ); + log_free(); + osrfConfigFree(cfg); + + return 0; + +} + -- 2.43.2