Chop Chop, Jabber
authorerickson <erickson@9efc2488-bf62-4759-914b-345cdb29e865>
Sun, 19 Jun 2005 16:17:47 +0000 (16:17 +0000)
committererickson <erickson@9efc2488-bf62-4759-914b-345cdb29e865>
Sun, 19 Jun 2005 16:17:47 +0000 (16:17 +0000)
custom jabber server.  functional, but needs more pounding and testing

git-svn-id: svn://svn.open-ils.org/OpenSRF/trunk@349 9efc2488-bf62-4759-914b-345cdb29e865

src/jserver/Makefile [new file with mode: 0644]
src/jserver/jserver-c.c [new file with mode: 0644]
src/jserver/jserver-c.h [new file with mode: 0644]
src/jserver/jserver-c_main.c [new file with mode: 0644]
src/jserver/jserver-c_session.c [new file with mode: 0644]
src/jserver/jserver-c_session.h [new file with mode: 0644]

diff --git a/src/jserver/Makefile b/src/jserver/Makefile
new file mode 100644 (file)
index 0000000..9198f6c
--- /dev/null
@@ -0,0 +1,26 @@
+LD_OPTS        += -lc_utils -lxml2
+OBJECTS                = jserver-c.o jserver-c_main.o jserver-c_session.o
+
+
+all: jserver-c 
+
+jserver-c: $(OBJECTS)
+       $(CC) $(LD_OPTS) $(OBJECTS) -o $@
+
+jserver-c_main.o: jserver-c_main.c 
+       $(CC) -c $(CC_OPTS) jserver-c_main.c -o $@
+
+jserver-c.o: jserver-c.c jserver-c.h
+       $(CC) -c $(CC_OPTS) jserver-c.c -o $@
+
+jserver-c_session.o: jserver-c_session.c jserver-c_session.h
+       $(CC) -c $(CC_OPTS) jserver-c_session.c -o $@
+
+
+install: 
+       cp jserver-c $(BINDIR)
+
+
+clean:
+       /bin/rm -f *.o jserver-c 
+
diff --git a/src/jserver/jserver-c.c b/src/jserver/jserver-c.c
new file mode 100644 (file)
index 0000000..c8d447e
--- /dev/null
@@ -0,0 +1,281 @@
+#include "jserver-c.h"
+
+/* ------------------------------------------------
+       some pre-packaged Jabber XML
+       ------------------------------------------------ */
+static const char* xml_parse_error = "<stream:stream xmlns:stream='http://etherx.jabber.org/streams'" 
+       "version='1.0'><stream:error xmlns:stream='http://etherx.jabber.org/streams'>"
+       "<xml-not-well-formed xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>"
+       "<text xmlns='urn:ietf:params:xml:ns:xmpp-streams'>syntax error</text></stream:error></stream:stream>";
+
+static const char* xml_login_ok = "<iq xmlns='jabber:client' id='asdfjkl' type='result'/>";
+
+
+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(js, js->client->addr);
+               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;
+       jclient_node* node = jserver_find_client_id(js, sock_id);
+       if(node) {
+               info_handler("Removing client %d - %s remote "
+                               "site closed socket", sock_id, node->addr);
+               _jserver_remove_client(js, node->addr);
+       }
+
+}
+
+/* opens the inet and unix sockets that we're listening on */
+int jserver_connect(jserver* js, int port, 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);
+               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, "</stream:stream>");
+       _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);
+}
+
+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;
+               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;
+                       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, "<message xmlns='jabber:client' type='error' from='%s' "
+                                       "to='%s'><error type='cancel' code='404'><item-not-found "
+                                       "xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error>"
+                                       "<body>NOT ADDING BODY</body></message>", 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_remove_client(js, node->addr);         
+
+       } else {
+               debug_handler("Client data successfully parsed");
+       }
+
+}
+
+
+
+
diff --git a/src/jserver/jserver-c.h b/src/jserver/jserver-c.h
new file mode 100644 (file)
index 0000000..e292756
--- /dev/null
@@ -0,0 +1,81 @@
+#include "utils.h"
+#include "logging.h"
+#include "socket_bundle.h"
+#include "jserver-c_session.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 
+       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* 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);
+
+/* 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/src/jserver/jserver-c_main.c b/src/jserver/jserver-c_main.c
new file mode 100644 (file)
index 0000000..8b34f3d
--- /dev/null
@@ -0,0 +1,97 @@
+#include "jserver-c.h"
+#include <signal.h>
+#include <fcntl.h>
+
+/* config vars */
+jserver* js                                    = NULL;
+int            port                            = -1;
+char*          unix_sock_file = NULL;
+int            log_level               = -1;
+char*          log_file                        = 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");
+
+       log_free();
+       jserver_free(js);
+       unlink(unix_sock_file);
+
+       launch_server();
+       return; 
+}
+
+/* die gracefully */
+void sig_int_handler( int a ) { 
+       warning_handler(" +++ Shutting down because of user signal");
+       log_free();
+       jserver_free(js);
+       unlink(unix_sock_file);
+       exit(0); 
+}
+
+
+
+/* loads the command line settings and launches the server */
+int main(int argc, char* argv[]) {
+
+       signal(SIGHUP, &sig_hup_handler);
+       signal(SIGINT, &sig_int_handler);
+       signal(SIGTERM, &sig_int_handler);
+
+       char* prog                      = argv[0];
+       char* sport                     = argv[1];      
+       unix_sock_file          = argv[2];      
+       char* slog_level        = argv[3];
+       log_file                                = argv[4];
+
+       if(!sport || !unix_sock_file || !slog_level) {
+               fprintf(stderr, 
+                       "usage: %s <port> <path_to_unix_sock_file> <log_level [1-4]"
+                       "(4 is the highest)> [log_file (optional, goes to stderr otherwise)]\n"
+                       "e.g: %s 5222 /tmp/server.sock 1 /tmp/server.log\n",
+                       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 );
+
+       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);
+
+       js = jserver_init();
+       if(jserver_connect(js, port, unix_sock_file) < 0)
+               fatal_handler("Could not connect...");
+
+       jserver_wait(js);
+}
+
+
+
+
+
diff --git a/src/jserver/jserver-c_session.c b/src/jserver/jserver-c_session.c
new file mode 100644 (file)
index 0000000..5e86a5f
--- /dev/null
@@ -0,0 +1,309 @@
+#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[512];
+                       memset(buf,0,512);
+
+                       /* reply with the stock jabber login response */
+                       sprintf(buf, "<?xml version='1.0'?><stream:stream xmlns:stream='http://etherx.jabber.org/streams' " 
+                               "xmlns='jabber:client' from='%s' to='%s' version='1.0' id='d253et09iw1fv8a2noqc38f28sb0y5fc7kfmegvx'>",
+                               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) {
+                               /* 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 ) {
+       
+       int                     bufsize;
+       xmlChar*                xmlbuf;
+       xmlDocDumpFormatMemory( doc, &xmlbuf, &bufsize, 0 );
+
+       char* xml = strdup(xmlbuf);
+       xmlFree(xmlbuf);
+
+       /*** remove the XML declaration */
+       int len = strlen(xml);
+       char tmp[len];
+       memset( tmp, 0, len );
+       int i;
+       int found_at = 0;
+                                               
+       /* when we reach the first >, take everything after it */
+       for( i = 0; i!= len; i++ ) {
+               if( xml[i] == 62) { /* ascii > */
+       
+                       /* found_at holds the starting index of the rest of the doc*/
+                       found_at = i + 1; 
+                       break;
+               }
+       }
+
+       if( found_at ) {
+
+               /* move the shortened doc into the tmp buffer */
+               strncpy( tmp, xml + found_at, len - found_at );
+               /* move the tmp buffer back into the allocated space */
+               memset( xml, 0, len );
+               strcpy( xml, tmp );
+       }
+
+       int l = strlen(xml)-1;
+       if( xml[l] == 10 || xml[l] == 13 )
+               xml[l] = '\0';
+
+       return xml;
+
+}
+
diff --git a/src/jserver/jserver-c_session.h b/src/jserver/jserver-c_session.h
new file mode 100644 (file)
index 0000000..c4abea2
--- /dev/null
@@ -0,0 +1,117 @@
+#include "utils.h"
+#include "logging.h"
+
+#include <string.h>
+#include <stdio.h>
+
+#include <libxml/globals.h>
+#include <libxml/xmlerror.h>
+#include <libxml/parser.h>
+#include <libxml/parserInternals.h> /* only for xmlNewInputFromFile() */
+#include <libxml/tree.h>
+#include <libxml/debugXML.h>
+#include <libxml/xmlmemory.h>
+
+
+/* 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;