From 5f57b21ae980576fd4017f993dc7932ef3922d2f Mon Sep 17 00:00:00 2001 From: erickson Date: Sun, 19 Jun 2005 16:17:47 +0000 Subject: [PATCH 1/1] Chop Chop, Jabber 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 | 26 +++ src/jserver/jserver-c.c | 281 +++++++++++++++++++++++++++++ src/jserver/jserver-c.h | 81 +++++++++ src/jserver/jserver-c_main.c | 97 ++++++++++ src/jserver/jserver-c_session.c | 309 ++++++++++++++++++++++++++++++++ src/jserver/jserver-c_session.h | 117 ++++++++++++ 6 files changed, 911 insertions(+) create mode 100644 src/jserver/Makefile create mode 100644 src/jserver/jserver-c.c create mode 100644 src/jserver/jserver-c.h create mode 100644 src/jserver/jserver-c_main.c create mode 100644 src/jserver/jserver-c_session.c create mode 100644 src/jserver/jserver-c_session.h diff --git a/src/jserver/Makefile b/src/jserver/Makefile new file mode 100644 index 0000000..9198f6c --- /dev/null +++ b/src/jserver/Makefile @@ -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 index 0000000..c8d447e --- /dev/null +++ b/src/jserver/jserver-c.c @@ -0,0 +1,281 @@ +#include "jserver-c.h" + +/* ------------------------------------------------ + some pre-packaged Jabber XML + ------------------------------------------------ */ +static const char* xml_parse_error = "" + "" + "syntax error"; + +static const char* xml_login_ok = ""; + + +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, ""); + _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, "" + "NOT ADDING BODY", 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 index 0000000..e292756 --- /dev/null +++ b/src/jserver/jserver-c.h @@ -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 index 0000000..8b34f3d --- /dev/null +++ b/src/jserver/jserver-c_main.c @@ -0,0 +1,97 @@ +#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; + +/* 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 [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 index 0000000..5e86a5f --- /dev/null +++ b/src/jserver/jserver-c_session.c @@ -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, "", + 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 index 0000000..c4abea2 --- /dev/null +++ b/src/jserver/jserver-c_session.h @@ -0,0 +1,117 @@ +#include "utils.h" +#include "logging.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; -- 2.43.2