diff --git a/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/xml/DocumentHandler.java b/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/xml/DocumentHandler.java
new file mode 100644
index 000000000..fecae7f72
--- /dev/null
+++ b/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/xml/DocumentHandler.java
@@ -0,0 +1,183 @@
+package net.sourceforge.guacamole.net.basic.xml;
+
+/*
+ * Guacamole - Clientless Remote Desktop
+ * Copyright (C) 2010 Michael Jumper
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+import java.util.LinkedList;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * A simple ContentHandler implementation which digests SAX document events and
+ * produces simpler tag-level events, maintaining its own stack for the
+ * convenience of the tag handlers.
+ *
+ * @author Mike Jumper
+ */
+public class DocumentHandler extends DefaultHandler {
+
+ /**
+ * The name of the root element of the document.
+ */
+ private String rootElementName;
+
+ /**
+ * The handler which will be used to handle element events for the root
+ * element of the document.
+ */
+ private TagHandler root;
+
+ /**
+ * The stack of all states applicable to the current parser state. Each
+ * element of the stack references the TagHandler for the element being
+ * parsed at that level of the document, where the current element is
+ * last in the stack, and the root element is first.
+ */
+ private LinkedList stack =
+ new LinkedList();
+
+ /**
+ * Creates a new DocumentHandler which will use the given TagHandler
+ * to handle the root element.
+ *
+ * @param rootElementName The name of the root element of the document
+ * being handled.
+ * @param root The TagHandler to use for the root element.
+ */
+ public DocumentHandler(String rootElementName, TagHandler root) {
+ this.root = root;
+ this.rootElementName = rootElementName;
+ }
+
+ /**
+ * Returns the current element state. The current element state is the
+ * state of the element the parser is currently within.
+ *
+ * @return The current element state.
+ */
+ private DocumentHandlerState getCurrentState() {
+ return stack.getLast();
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName,
+ Attributes attributes) throws SAXException {
+
+ // Get current state
+ DocumentHandlerState current = getCurrentState();
+
+ // Handler for tag just read
+ TagHandler handler;
+
+ // If no stack, use root handler
+ if (current == null) {
+
+ // Validate element name
+ if (!localName.equals(rootElementName))
+ throw new SAXException("Root element must be '" + rootElementName + "'");
+
+ handler = root;
+ }
+
+ // Otherwise, get handler from parent
+ else {
+ TagHandler parent_handler = current.getTagHandler();
+ handler = parent_handler.childElement(localName, attributes);
+ }
+
+ // Append new element state to stack
+ stack.addLast(new DocumentHandlerState(handler));
+
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName)
+ throws SAXException {
+
+ // Pop last element from stack
+ DocumentHandlerState completed = stack.removeLast();
+
+ // Finish element by sending text content
+ completed.getTagHandler().complete(
+ completed.getTextContent().toString());
+
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length)
+ throws SAXException {
+
+ // Append received chunk to text content
+ getCurrentState().getTextContent().append(ch, start, length);
+
+ }
+
+ /**
+ * The current state of the DocumentHandler.
+ */
+ private class DocumentHandlerState {
+
+ /**
+ * The current text content of the current element being parsed.
+ */
+ private StringBuilder textContent = new StringBuilder();
+
+ /**
+ * The TagHandler which must handle document events related to the
+ * element currently being parsed.
+ */
+ private TagHandler tagHandler;
+
+ /**
+ * Creates a new DocumentHandlerState which will maintain the state
+ * of parsing of the current element, as well as contain the TagHandler
+ * which will receive events related to that element.
+ *
+ * @param tagHandler The TagHandler which should receive any events
+ * related to the element being parsed.
+ */
+ public DocumentHandlerState(TagHandler tagHandler) {
+ this.tagHandler = tagHandler;
+ }
+
+ /**
+ * Returns the mutable StringBuilder which contains the current text
+ * content of the element being parsed.
+ *
+ * @return The mutable StringBuilder which contains the current text
+ * content of the element being parsed.
+ */
+ public StringBuilder getTextContent() {
+ return textContent;
+ }
+
+ /**
+ * Returns the TagHandler which must handle any events relating to the
+ * element being parsed.
+ *
+ * @return The TagHandler which must handle any events relating to the
+ * element being parsed.
+ */
+ public TagHandler getTagHandler() {
+ return tagHandler;
+ }
+
+ }
+
+}
diff --git a/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/xml/TagHandler.java b/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/xml/TagHandler.java
new file mode 100644
index 000000000..0aefe9047
--- /dev/null
+++ b/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/xml/TagHandler.java
@@ -0,0 +1,57 @@
+package net.sourceforge.guacamole.net.basic.xml;
+
+/*
+ * Guacamole - Clientless Remote Desktop
+ * Copyright (C) 2010 Michael Jumper
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+/**
+ * A simple element-level event handler for events triggered by the
+ * SAX-driven DocumentHandler parser.
+ *
+ * @author Mike Jumper
+ */
+public interface TagHandler {
+
+ /**
+ * Called when a child element of the current element is parsed.
+ *
+ * @param localName The local name of the child element seen.
+ * @param attributes The attributes of the child element seen.
+ * @return The TagHandler which should handle all element-level events
+ * related to the child element.
+ * @throws SAXException If the child element being parsed was not expected,
+ * or some other error prevents a proper TagHandler
+ * from being constructed for the child element.
+ */
+ public TagHandler childElement(String localName, Attributes attributes)
+ throws SAXException;
+
+ /**
+ * Called when this element, and all child elements, have been fully parsed,
+ * and the entire text content of this element (if any) is available.
+ *
+ * @param textContent The full text content of this element, if any.
+ * @throws SAXException If the text content received is not valid for any
+ * reason, or the child elements parsed are not
+ * correct.
+ */
+ public void complete(String textContent) throws SAXException;
+
+}