Browse Source

quickly ported the cc library from the experiments branch

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/f2f200910@232 e5f2f494-b856-4b98-b285-d166d9295462
JINMEI Tatuya 15 years ago
parent
commit
bd2469e99d

+ 2 - 0
configure.ac

@@ -49,6 +49,8 @@ AC_CONFIG_FILES([Makefile
                  src/bin/host/Makefile
                  src/bin/parkinglot/Makefile
                  src/lib/Makefile
+                 src/lib/cc/Makefile
+                 src/lib/cc/cpp/Makefile
                  src/lib/dns/Makefile
                ])
 AC_OUTPUT([src/bin/bind-cfgd/bind-cfgd])

+ 1 - 1
src/lib/Makefile.am

@@ -1 +1 @@
-SUBDIRS = dns
+SUBDIRS = dns cc

+ 1 - 0
src/lib/cc/Makefile.am

@@ -0,0 +1 @@
+SUBDIRS = cpp

+ 4 - 0
src/lib/cc/cpp/Makefile.am

@@ -0,0 +1,4 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib/cc/cpp -I$(top_srcdir)/ext
+
+lib_LIBRARIES = libcc.a
+libcc_a_SOURCES = data.cc data.h session.cc session.h

+ 831 - 0
src/lib/cc/cpp/data.cc

@@ -0,0 +1,831 @@
+
+#include "data.h"
+
+#include <cstdio>
+#include <iostream>
+#include <sstream>
+
+#include <boost/algorithm/string.hpp>
+
+using namespace std;
+using namespace ISC::Data;
+
+const unsigned char PROTOCOL_VERSION[4] = { 0x53, 0x6b, 0x61, 0x6e };
+
+const unsigned char ITEM_DATA = 0x01;
+const unsigned char ITEM_HASH = 0x02;
+const unsigned char ITEM_LIST = 0x03;
+const unsigned char ITEM_NULL = 0x04;
+const unsigned char ITEM_MASK = 0x0f;
+
+const unsigned char ITEM_LENGTH_32   = 0x00;
+const unsigned char ITEM_LENGTH_16   = 0x10;
+const unsigned char ITEM_LENGTH_8    = 0x20;
+const unsigned char ITEM_LENGTH_MASK = 0x30;
+
+std::ostream& operator <<(std::ostream &out, const ISC::Data::ElementPtr& e) {
+    return out << e->str();
+}
+
+//
+// factory functions
+//
+ElementPtr
+Element::create(const int i)
+{
+    try {
+        return ElementPtr(new IntElement(i));
+    } catch (std::bad_alloc) {
+        return ElementPtr();
+    }
+}
+
+ElementPtr
+Element::create(const double d)
+{
+    try {
+        return ElementPtr(new DoubleElement(d));
+    } catch (std::bad_alloc) {
+        return ElementPtr();
+    }
+}
+
+ElementPtr
+Element::create(const std::string& s)
+{
+    try {
+        return ElementPtr(new StringElement(s));
+    } catch (std::bad_alloc) {
+        return ElementPtr();
+    }
+}
+
+ElementPtr
+Element::create(const bool b)
+{
+    try {
+        cout << "creating boolelement" << endl;
+        return ElementPtr(new BoolElement(b));
+    } catch (std::bad_alloc) {
+        return ElementPtr();
+    }
+}
+
+ElementPtr
+Element::create(const std::vector<ElementPtr>& v)
+{
+    try {
+        return ElementPtr(new ListElement(v));
+    } catch (std::bad_alloc) {
+        return ElementPtr();
+    }
+}
+
+ElementPtr
+Element::create(const std::map<std::string, ElementPtr>& m)
+{
+    try {
+        return ElementPtr(new MapElement(m));
+    } catch (std::bad_alloc) {
+        return ElementPtr();
+    }
+}
+
+
+//
+// helper functions for create_from_string factory
+// these should probably also be moved to member functions
+//
+
+static bool
+char_in(char c, const char *chars)
+{
+    for (size_t i = 0; i < strlen(chars); i++) {
+        if (chars[i] == c) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static void
+skip_chars(std::stringstream &in, const char *chars)
+{
+    char c = in.peek();
+    while (char_in(c, chars) && c != EOF) {
+        in.get();
+        c = in.peek();
+    }
+}
+
+// skip on the input stream to one of the characters in chars
+// if another character is found this function returns false
+// unles that character is specified in the optional may_skip
+//
+// the character found is left on the stream
+static bool
+skip_to(std::stringstream &in, const char *chars, const char *may_skip="")
+{
+    char c = in.get();
+    while (c != EOF) {
+        if (char_in(c, may_skip)) {
+            c = in.get();
+        } else if (char_in(c, chars)) {
+            while(char_in(in.peek(), may_skip)) {
+                in.get();
+            }
+            in.putback(c);
+            return true;
+        } else {
+            // TODO: provide feeback mechanism?
+            cout << "error, '" << c << "' read; one of \"" << chars << "\" expected" << endl;
+            in.putback(c);
+            return false;
+        }
+    }
+    // TODO: provide feeback mechanism?
+    cout << "error, EOF read; one of \"" << chars << "\" expected" << endl;
+            in.putback(c);
+    return false;
+}
+
+static std::string
+str_from_stringstream(std::stringstream &in)
+{
+    char c = 0;
+    std::stringstream ss;
+    c = in.get();
+    if (c == '"') {
+        c = in.get();
+    } else {
+        return "badstring";
+    }
+    while (c != EOF && c != '"') {
+        ss << c;
+        if (c == '\\' && in.peek() == '"') {
+            ss << in.get();
+        }
+        c = in.get();
+    }
+    return ss.str();
+}
+
+static std::string
+word_from_stringstream(std::stringstream &in)
+{
+    std::stringstream ss;
+    while (isalpha(in.peek())) {
+        ss << (char) in.get();
+    }
+    return ss.str();
+}
+
+
+static ElementPtr
+from_stringstream_int_or_double(std::stringstream &in)
+{
+    int i;
+    in >> i;
+    if (in.peek() == '.') {
+        double d;
+        in >> d;
+        d += i;
+        return Element::create(d);
+    } else {
+        return Element::create(i);
+    }
+}
+
+static ElementPtr
+from_stringstream_bool(std::stringstream &in)
+{
+    std::string word = word_from_stringstream(in);
+    if (boost::iequals(word, "True")) {
+        return Element::create(true);
+    } else if (boost::iequals(word, "False")) {
+        return Element::create(false);
+    } else {
+        return ElementPtr();
+    }
+}
+
+static ElementPtr
+from_stringstream_string(std::stringstream &in)
+{
+    return Element::create(str_from_stringstream(in));
+}
+
+static ElementPtr
+from_stringstream_list(std::stringstream &in)
+{
+    char c = 0;
+    std::vector<ElementPtr> v;
+    ElementPtr cur_list_element;
+
+    skip_chars(in, " \t\n");
+    while (c != EOF && c != ']') {
+        cur_list_element = Element::create_from_string(in);
+        v.push_back(cur_list_element);
+        if (!skip_to(in, ",]", " \t\n")) {
+            return ElementPtr();
+        }
+        c = in.get();
+    }
+    return Element::create(v);
+}
+
+static ElementPtr
+from_stringstream_map(std::stringstream &in)
+{
+    char c = 0;
+    std::map<std::string, ElementPtr> m;
+    std::pair<std::string, ElementPtr> p;
+    std::string cur_map_key;
+    ElementPtr cur_map_element;
+    skip_chars(in, " \t\n");
+    while (c != EOF && c != '}') {
+        p.first = str_from_stringstream(in);
+        if (!skip_to(in, ":", " \t\n")) {
+            return ElementPtr();
+        } else {
+            // skip the :
+            in.get();
+        }
+        p.second = Element::create_from_string(in);
+        if (!p.second) { return ElementPtr(); };
+        m.insert(p);
+        skip_to(in, ",}", " \t\n");
+        c = in.get();
+    }
+    return Element::create(m);
+}
+
+ElementPtr
+Element::create_from_string(std::stringstream &in)
+{
+    char c = 0;
+    ElementPtr element;
+    bool el_read = false;
+    skip_chars(in, " \n\t");
+    while (c != EOF && !el_read) {
+        c = in.get();
+        switch(c) {
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            case '0':
+                in.putback(c);
+                element = from_stringstream_int_or_double(in);
+                el_read = true;
+                break;
+            case 't':
+            case 'T':
+            case 'f':
+            case 'F':
+                in.putback(c);
+                element = from_stringstream_bool(in);
+                el_read = true;
+                break;
+            case '"':
+                in.putback('"');
+                element = from_stringstream_string(in);
+                el_read = true;
+                break;
+            case '[':
+                element = from_stringstream_list(in);
+                el_read = true;
+                break;
+            case '{':
+                element = from_stringstream_map(in);
+                el_read = true;
+                break;
+            default:
+                // TODO this might not be a fatal error
+                // provide feedback mechanism?
+                cout << "error: unexpected '" << c << "'" << endl;
+                return ElementPtr();
+                break;
+        }
+    }
+    if (el_read) {
+        return element;
+    } else {
+        // throw exception?
+        return ElementPtr();
+    }
+}
+
+//
+// a general to_str() function
+//
+std::string
+IntElement::str()
+{
+    std::stringstream ss;
+    ss << int_value();
+    return ss.str();
+}
+
+std::string
+DoubleElement::str()
+{
+    std::stringstream ss;
+    ss << double_value();
+    return ss.str();
+}
+
+std::string
+BoolElement::str()
+{
+    if (b) {
+        return "True";
+    } else {
+        return "False";
+    }
+}
+
+std::string
+StringElement::str()
+{
+    std::stringstream ss;
+    ss << "\"";
+    ss << string_value();
+    ss << "\"";
+    return ss.str();
+}
+
+std::string
+ListElement::str()
+{
+    std::stringstream ss;
+    std::vector<ElementPtr> v;
+    ss << "[ ";
+    v = list_value();
+    for (std::vector<ElementPtr>::iterator it = v.begin(); it != v.end(); ++it) {
+        if (it != v.begin()) {
+            ss << ", ";
+        }
+        ss << (*it)->str();
+    }
+    ss << " ]";
+    return ss.str();
+}
+
+std::string
+MapElement::str()
+{
+    std::stringstream ss;
+    std::map<std::string, ElementPtr> m;
+    ss << "{";
+    m = map_value();
+    for (std::map<std::string, ElementPtr>::iterator it = m.begin(); it != m.end(); ++it) {
+        if (it != m.begin()) {
+            ss << ", ";
+        }
+        ss << "\"" << (*it).first << "\": ";
+        ss << (*it).second->str();
+    }
+    ss << "}";
+    return ss.str();
+}
+
+//
+// helpers for str_xml() functions
+//
+
+// prefix with 'prefix' number of spaces
+static void
+pre(std::ostream &out, size_t prefix)
+{
+    for (size_t i = 0; i < prefix; i++) {
+        out << " ";
+    }
+}
+
+std::string
+IntElement::str_xml(size_t prefix)
+{
+    std::stringstream ss;
+    pre(ss, prefix);
+    ss << str();
+    return ss.str();
+}
+
+std::string
+DoubleElement::str_xml(size_t prefix)
+{
+    std::stringstream ss;
+    pre(ss, prefix);
+    ss << str();
+    return ss.str();
+}
+
+std::string
+BoolElement::str_xml(size_t prefix)
+{
+    std::stringstream ss;
+    pre(ss, prefix);
+    ss << str();
+    return ss.str();
+}
+
+std::string
+StringElement::str_xml(size_t prefix)
+{
+    std::stringstream ss;
+    pre(ss, prefix);
+    ss << string_value();
+    return ss.str();
+}
+
+std::string
+ListElement::str_xml(size_t prefix)
+{
+    std::stringstream ss;
+    std::vector<ElementPtr> v;
+    pre(ss, prefix);
+    ss << "<list>" << endl;;
+    v = list_value();
+    for (std::vector<ElementPtr>::iterator it = v.begin(); it != v.end(); ++it) {
+        pre(ss, prefix + 4);
+        ss << "<listitem>" << endl;
+        ss << (*it)->str_xml(prefix + 8) << endl;
+        pre(ss, prefix + 4);
+        ss << "</listitem>" << endl;
+    }
+    pre(ss, prefix);
+    ss << "</list>";
+    return ss.str();
+}
+
+std::string
+MapElement::str_xml(size_t prefix)
+{
+    std::stringstream ss;
+    std::map<std::string, ElementPtr> m;
+    m = map_value();
+    pre(ss, prefix);
+    ss << "<map>" << endl;
+    for (std::map<std::string, ElementPtr>::iterator it = m.begin(); it != m.end(); ++it) {
+        pre(ss, prefix + 4);
+        ss << "<mapitem name=\"" << (*it).first << "\">" << endl;
+        pre(ss, prefix);
+        ss << (*it).second->str_xml(prefix+8) << endl;
+        pre(ss, prefix + 4);
+        ss << "</mapitem>" << endl;
+    }
+    pre(ss, prefix);
+    ss << "</map>";
+    return ss.str();
+}
+
+// currently throws when one of the types in the path (except the one
+// we're looking for) is not a MapElement
+// returns 0 if it could simply not be found
+// should that also be an exception?
+ElementPtr
+MapElement::find(const std::string& id)
+{
+    if (get_type() != map) {
+        throw TypeError();
+    }
+    size_t sep = id.find('/');
+    if (sep == std::string::npos) {
+        return get(id);
+    } else {
+        ElementPtr ce = get(id.substr(0, sep));
+        if (ce) {
+            return ce->find(id.substr(sep+1));
+        } else {
+            return ElementPtr();
+        }
+    }
+}
+
+//
+// Decode from wire format.
+//
+
+ElementPtr decode_element(std::stringstream& in, int& in_length);
+
+static unsigned char
+get_byte(std::stringstream& in)
+{
+    int c = in.get();
+    if (c == EOF) {
+        throw DecodeError("End of data while decoding wire format message");
+    }
+
+    return c;
+}
+
+std::string
+decode_tag(std::stringstream& in, int& item_length)
+{
+    char buf[256];
+
+    int len = get_byte(in);
+    item_length--;
+
+    in.read(buf, len);
+    if (in.fail()) {
+        throw DecodeError();
+    }
+    buf[len] = 0;
+    item_length -= len;
+
+    return std::string(buf, len);
+}
+
+ElementPtr
+decode_data(std::stringstream& in, int& item_length)
+{
+    char *buf = new char[item_length + 1];
+
+    in.read(buf, item_length);
+    if (in.fail()) {
+        throw DecodeError();
+    }
+    buf[item_length] = 0;
+
+    std::string s = std::string(buf, item_length);
+    item_length -= item_length;
+
+    delete [] buf;
+    return Element::create(s);
+}
+
+ElementPtr
+decode_hash(std::stringstream& in, int& item_length)
+{
+    std::map<std::string, ElementPtr> m;
+    std::pair<std::string, ElementPtr> p;
+
+    while (item_length > 0) {
+        p.first = decode_tag(in, item_length);
+        p.second = decode_element(in, item_length);
+        m.insert(p);
+    }
+
+    return Element::create(m);
+}
+
+ElementPtr
+decode_list(std::stringstream& in, int& item_length)
+{
+    std::vector<ElementPtr> v;
+
+    while (item_length > 0) {
+        v.push_back(decode_element(in, item_length));
+    }
+    return Element::create(v);
+}
+
+ElementPtr
+decode_null(std::stringstream& in, int& item_length)
+{
+    return Element::create("NULL");
+}
+
+ElementPtr
+decode_element(std::stringstream& in, int& in_length)
+{
+    ElementPtr element;
+
+    unsigned char type_and_length = get_byte(in);
+    unsigned char type = type_and_length & ITEM_MASK;
+    unsigned char lenbytes = type_and_length & ITEM_LENGTH_MASK;
+    in_length--;
+
+    int item_length = 0;
+    switch (lenbytes) {
+    case ITEM_LENGTH_32:
+        item_length |= get_byte(in);
+        item_length <<= 8;
+        item_length |= get_byte(in);
+        item_length <<= 8;
+        in_length -= 2;  // only 2 here, we will get more later
+    case ITEM_LENGTH_16:
+        item_length |= get_byte(in);
+        item_length <<= 8;
+        in_length--;  // only 1 here
+    case ITEM_LENGTH_8:
+        item_length |= get_byte(in);
+        in_length--;
+    }
+
+    in_length -= item_length;
+
+    switch (type) {
+    case ITEM_DATA:
+        element = decode_data(in, item_length);
+        break;
+    case ITEM_HASH:
+        element = decode_hash(in, item_length);
+        break;
+    case ITEM_LIST:
+        element = decode_list(in, item_length);
+        break;
+    case ITEM_NULL:
+        element = decode_null(in, item_length);
+        break;
+    }
+
+    return (element);
+}
+
+ElementPtr
+Element::from_wire(const std::string& s)
+{
+    std::stringstream ss;
+    ss << s;
+    return from_wire(ss, s.length());
+}
+
+ElementPtr
+Element::from_wire(std::stringstream& in, int length)
+{
+    //
+    // Check protocol version
+    //
+    for (int i = 0 ; i < 4 ; i++) {
+        unsigned char version_byte = get_byte(in);
+        if (PROTOCOL_VERSION[i] != version_byte) {
+            throw DecodeError("Protocol version incorrect");
+        }
+    }
+    length -= 4;
+
+    ElementPtr element;
+    element = decode_hash(in, length);
+    return (element);
+}
+
+//
+// Encode into wire format.
+//
+
+std::string
+encode_length(unsigned int length, unsigned char type)
+{
+    std::stringstream ss;
+
+    if (length <= 0x000000ff) {
+        unsigned char val = (length & 0x000000ff);
+        type |= ITEM_LENGTH_8;
+        ss << type << val;
+    } else if (length <= 0x0000ffff) {
+        unsigned char val[2];
+        val[0] = (length & 0x0000ff00) >> 8;
+        val[1] = (length & 0x000000ff);
+        type |= ITEM_LENGTH_16;
+        ss << type << val;
+    } else {
+        unsigned char val[4];
+        val[0] = (length & 0xff000000) >> 24;
+        val[1] = (length & 0x00ff0000) >> 16;
+        val[2] = (length & 0x0000ff00) >> 8;
+        val[3] = (length & 0x000000ff);
+        type |= ITEM_LENGTH_32;
+        ss << type << val;
+    }
+    return ss.str();
+}
+
+std::string
+StringElement::to_wire(int omit_length)
+{
+    std::stringstream ss;
+
+    int length = string_value().length();
+    ss << encode_length(length, ITEM_DATA) << string_value();
+
+    return ss.str();
+}
+
+std::string
+IntElement::to_wire(int omit_length)
+{
+    std::stringstream ss;
+    std::stringstream text;
+
+    text << str();
+    int length = text.str().length();
+    ss << encode_length(length, ITEM_DATA) << text.str();
+
+    return ss.str();
+}
+
+std::string
+BoolElement::to_wire(int omit_length)
+{
+    std::stringstream ss;
+    std::stringstream text;
+
+    text << str();
+    int length = text.str().length();
+    ss << encode_length(length, ITEM_DATA) << text.str();
+
+    return ss.str();
+}
+
+std::string
+DoubleElement::to_wire(int omit_length)
+{
+    std::stringstream ss;
+    std::stringstream text;
+
+    text << str();
+    int length = text.str().length();
+    ss << encode_length(length, ITEM_DATA) << text.str();
+
+    return ss.str();
+}
+
+std::string
+ListElement::to_wire(int omit_length)
+{
+    std::stringstream ss;
+    std::vector<ElementPtr> v;
+    v = list_value();
+    for (std::vector<ElementPtr>::iterator it = v.begin() ;
+         it != v.end() ; ++it) {
+        ss << (*it)->to_wire(0);
+    }
+
+    if (omit_length) {
+        return ss.str();
+    } else {
+        std::stringstream ss_len;
+        ss_len << encode_length(ss.str().length(), ITEM_LIST);
+        ss_len << ss.str();
+        return ss_len.str();
+    }
+}
+
+std::string
+encode_tag(const std::string &s)
+{
+    std::stringstream ss;
+    int length = s.length();
+    unsigned char val = length & 0x000000ff;
+
+    ss << val << s;
+
+    return ss.str();
+}
+
+std::string
+MapElement::to_wire(int omit_length)
+{
+    std::stringstream ss;
+    std::map<std::string, ElementPtr> m;
+
+    //
+    // If we don't want the length, we will want the protocol header
+    //
+    if (omit_length) {
+        ss << PROTOCOL_VERSION[0] << PROTOCOL_VERSION[1];
+        ss << PROTOCOL_VERSION[2] << PROTOCOL_VERSION[3];
+    }
+
+    m = map_value();
+    for (std::map<std::string, ElementPtr>::iterator it = m.begin() ;
+         it != m.end() ; ++it) {
+        ss << encode_tag((*it).first);
+        ss << (*it).second->to_wire(0);
+    }
+
+    //
+    // add length if needed
+    //
+    if (omit_length) {
+        return ss.str();
+    } else {
+        std::stringstream ss_len;
+        ss_len << encode_length(ss.str().length(), ITEM_HASH);
+        ss_len << ss.str();
+        return ss_len.str();
+    }
+}
+
+bool
+MapElement::find(const std::string& id, ElementPtr& t) {
+    ElementPtr p;
+    try {
+        p = find(id);
+        if (p) {
+            t = p;
+            return true;
+        }
+    } catch (TypeError e) {
+        // ignore
+    }
+    return false;
+}

+ 247 - 0
src/lib/cc/cpp/data.h

@@ -0,0 +1,247 @@
+#ifndef _ISC_DATA_H
+#define _ISC_DATA_H 1
+
+#include <string>
+#include <vector>
+#include <map>
+#include <boost/shared_ptr.hpp>
+
+#include <iostream>
+
+namespace ISC { namespace Data {
+
+    // todo: include types and called function in the exception
+    class TypeError : public std::exception {
+    public:
+        TypeError(std::string m = "Attempt to use function on wrong Element type") : msg(m) {}
+        ~TypeError() throw() {}
+        const char* what() const throw() { return msg.c_str(); }
+    private:
+        std::string msg;
+    };
+
+    class DecodeError : public std::exception {
+    public:
+        DecodeError(std::string m = "Wire-format data is invalid") : msg(m) {}
+        ~DecodeError() throw() {}
+        const char* what() const throw() { return msg.c_str(); }
+    private:
+        std::string msg;
+    };
+    
+    class Element;
+    typedef boost::shared_ptr<Element> ElementPtr;
+
+    class Element {
+        
+    private:
+        // technically the type could be omitted; is it useful?
+        // should we remove it or replace it with a pure virtual
+        // function get_type?
+        int type;
+
+    protected:
+        Element(int t) { type = t; }
+
+    public:
+        enum types { integer, real, boolean, string, list, map };
+        // base class; make dtor virtual
+        virtual ~Element() {};
+
+        int get_type() { return type; };
+        // pure virtuals, every derived class must implement these
+        virtual std::string str() = 0;
+        virtual std::string str_xml(size_t prefix = 0) = 0;
+        virtual std::string to_wire(int omit_length = 1) = 0;
+
+        // virtual function templates must match, so we
+        // need separate getters for all subclassed types
+        // since all derived types only implement their own specific
+        // ones, we provide a default implementation
+        // These should probably throw an exception
+        // These could also be removed if we want to force the user
+        // to use get_value()
+        virtual int int_value() { throw TypeError(); };
+        virtual double double_value() { throw TypeError(); };
+        virtual bool bool_value() { throw TypeError(); };
+        virtual std::string string_value() { throw TypeError(); };
+        virtual const std::vector<boost::shared_ptr<Element> >& list_value() { throw TypeError(); }; // replace with real exception or empty vector?
+        virtual const std::map<std::string, boost::shared_ptr<Element> >& map_value() { throw TypeError(); }; // replace with real exception or empty map?
+
+        // hmm, these are only for specific subtypes, but we would
+        // like to call them on the ElementPtr...
+
+        // for lists
+        // TODO: what to do as default implementation (ie. when element of wrong type)?
+        virtual ElementPtr get(const int i) { throw TypeError(); };
+        virtual void set(const int i, ElementPtr element) { throw TypeError(); };
+        virtual void add(ElementPtr element) { throw TypeError(); };
+        virtual void remove(ElementPtr element) { throw TypeError(); };
+
+        // for maps
+        // TODO: what to do as default implementation (ie. when element of wrong type)?
+        virtual ElementPtr get(const std::string& name) { throw TypeError(); } ;
+        virtual void set(const std::string& name, ElementPtr element) { throw TypeError(); };
+        virtual void remove(const std::string& name) { throw TypeError(); };
+        virtual ElementPtr find(const std::string& identifier) { throw TypeError(); };
+        virtual bool find(const std::string& id, ElementPtr& t) { return false; };
+
+        //
+        // the _value() functions return false if the given reference
+        // is of another type than the element contains
+        // By default it always returns false; the derived classes
+        // should override the function for their type, copying their
+        // data to the given reference and returning true
+        //
+        virtual bool get_value(int& t) { return false; };
+        virtual bool get_value(double& t) { return false; };
+        virtual bool get_value(bool& t) { return false; };
+        virtual bool get_value(std::string& t) { return false; };
+        virtual bool get_value(std::vector<ElementPtr>& t) { return false; };
+        virtual bool get_value(std::map<std::string, ElementPtr>& t) { return false; };
+
+        virtual bool set_value(const int v) { return false; };
+        virtual bool set_value(const double v) { return false; };
+        virtual bool set_value(const bool t) { return false; };
+        virtual bool set_value(const std::string& v) { return false; };
+        virtual bool set_value(const std::vector<ElementPtr>& v) { return false; };
+        virtual bool set_value(const std::map<std::string, ElementPtr>& v) { return false; };
+
+        // should we move all factory functions to a different class
+        // so as not to burden the Element base with too many functions?
+        // and/or perhaps even to a separate header?
+
+        // direct factory functions; will simply wrap
+        // the argument in a shared Element pointer object
+        // these return a NULL shared pointer if no memory could be
+        // allocated
+        static ElementPtr create(const int i);
+        static ElementPtr create(const double d);
+        static ElementPtr create(const bool b);
+        static ElementPtr create(const std::string& s);
+        // need both std:string and char *, since c++ will match
+        // bool before std::string when you pass it a char *
+        static ElementPtr create(const char *s) { return create(std::string(s)); }; 
+        static ElementPtr create(const std::vector<ElementPtr>& v);
+        static ElementPtr create(const std::map<std::string, ElementPtr>& m);
+
+        // compound factory functions
+        // return a NULL ElementPtr if there is a parse error or
+        // the memory could not be allocated
+        static ElementPtr create_from_string(std::stringstream& in);
+        //static ElementPtr create_from_xml(std::stringstream& in);
+
+        static ElementPtr from_wire(std::stringstream& in, int length);
+        static ElementPtr from_wire(const std::string& s);
+    };
+
+    class IntElement : public Element {
+        int i;
+
+    public:
+        IntElement(int v) : Element(integer), i(v) { };
+        int int_value() { return i; }
+        bool get_value(int& t) { t = i; return true; };
+        bool set_value(const int v) { i = v; return true; };
+        std::string str();
+        std::string str_xml(size_t prefix = 0);
+        std::string to_wire(int omit_length = 1);
+    };
+
+    class DoubleElement : public Element {
+        double d;
+
+    public:
+        DoubleElement(double v) : Element(real), d(v) {};
+        double double_value() { return d; }
+        bool get_value(double& t) { t = d; return true; };
+        bool set_value(const double v) { d = v; return true; };
+        std::string str();
+        std::string str_xml(size_t prefix = 0);
+        std::string to_wire(int omit_length = 1);
+    };
+
+    class BoolElement : public Element {
+        bool b;
+		
+    public:
+        BoolElement(const bool v) : Element(boolean), b(v) {};
+        bool bool_value() { return b; }
+        bool get_value(bool& t) { t = b; return true; };
+        bool set_value(const bool v) { b = v; return true; };
+        std::string str();
+        std::string str_xml(size_t prefix = 0);
+        std::string to_wire(int omit_length = 1);
+    };
+	
+    class StringElement : public Element {
+        std::string s;
+
+    public:
+        StringElement(std::string v) : Element(string), s(v) {};
+        std::string string_value() { return s; };
+        bool get_value(std::string& t) { t = s; return true; };
+        bool set_value(const std::string& v) { s = v; return true; };
+        std::string str();
+        std::string str_xml(size_t prefix = 0);
+        std::string to_wire(int omit_length = 1);
+    };
+
+    class ListElement : public Element {
+        std::vector<ElementPtr> l;
+
+    public:
+        ListElement(std::vector<ElementPtr> v) : Element(list), l(v) {};
+        const std::vector<ElementPtr>& list_value() { return l; }
+        bool get_value(std::vector<ElementPtr>& t) { t = l; return true; };
+        bool set_value(const std::vector<ElementPtr>& v) { l = v; return true; };
+        ElementPtr get(int i) { return l[i]; };
+        void set(int i, ElementPtr e) { l[i] = e; };
+        void add(ElementPtr e) { l.push_back(e); };
+        std::string str();
+        std::string str_xml(size_t prefix = 0);
+        std::string to_wire(int omit_length = 1);
+    };
+
+    class MapElement : public Element {
+        std::map<std::string, ElementPtr> m;
+
+    public:
+        MapElement(std::map<std::string, ElementPtr> v) : Element(map), m(v) {};
+        const std::map<std::string, ElementPtr>& map_value() { return m; }
+        bool get_value(std::map<std::string, ElementPtr>& t) { t = m; return true; };
+        bool set_value(std::map<std::string, ElementPtr>& v) { m = v; return true; };
+        ElementPtr get(const std::string& s) { return m[s]; };
+        void set(const std::string& s, ElementPtr p) { m[s] = p; };
+        void remove(const std::string& s) { m.erase(s); }
+        std::string str();
+        std::string str_xml(size_t prefix = 0);
+        std::string to_wire(int omit_length = 1);
+        
+        //
+        // Encode into the CC wire format.
+        //
+	void to_wire(std::ostream& ss);
+
+        // we should name the two finds better...
+        // find the element at id; raises TypeError if one of the
+        // elements at path except the one we're looking for is not a
+        // mapelement.
+        // returns an empty element if the item could not be found
+        ElementPtr find(const std::string& id);
+
+        // find the Element at 'id', and store the element pointer in t
+        // returns true if found, or false if not found (either because
+        // it doesnt exist or one of the elements in the path is not
+        // a MapElement)
+        bool find(const std::string& id, ElementPtr& t);
+    };
+
+} }
+
+// add a << operator for output streams so we can do
+// ElementPtr foo = ...
+// cout << "Element: " << foo;
+std::ostream& operator <<(std::ostream &out, const ISC::Data::ElementPtr& e);
+
+#endif // _ISC_DATA_H

+ 185 - 0
src/lib/cc/cpp/session.cc

@@ -0,0 +1,185 @@
+
+#include "data.h"
+#include "session.h"
+
+#include <cstdio>
+#include <iostream>
+#include <sstream>
+
+using namespace std;
+using namespace ISC::CC;
+using namespace ISC::Data;
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+Session::Session()
+{
+    sock = -1;
+}
+
+void
+Session::disconnect()
+{
+    close(sock);
+    sock = -1;
+}
+
+void
+Session::establish()
+{
+    int ret;
+    struct sockaddr_in sin;
+
+    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    if (sock < -1)
+        throw SessionError("socket() failed");
+
+    sin.sin_len = sizeof(struct sockaddr_in);
+    sin.sin_family = AF_INET;
+    sin.sin_port = htons(9912);
+    sin.sin_addr.s_addr = INADDR_ANY;
+    ret = connect(sock, (struct sockaddr *)&sin, sizeof(sin));
+    if (ret < 0)
+        throw SessionError("connect() failed");
+
+    //
+    // send a request for our local name, and wait for a response
+    //
+    std::string get_lname_str = "{ \"type\": \"getlname\" }";
+    std::stringstream get_lname_stream;
+    get_lname_stream.str(get_lname_str);
+    ElementPtr get_lname_msg = Element::create_from_string(get_lname_stream);
+    sendmsg(get_lname_msg);
+
+    ElementPtr msg;
+    recvmsg(msg, false);
+
+    lname = msg->get("lname")->string_value();
+    cout << "My local name is:  " << lname << endl;
+}
+
+//
+// Convert to wire format and send this on the TCP stream with its length prefix
+//
+void
+Session::sendmsg(ElementPtr& msg)
+{
+    std::string wire = msg->to_wire();
+    unsigned int length = wire.length();
+    unsigned int length_net = htonl(length);
+    unsigned int ret;
+
+    ret = write(sock, &length_net, 4);
+    if (ret != 4)
+        throw SessionError("Short write");
+
+    ret = write(sock, wire.c_str(), length);
+    if (ret != length)
+        throw SessionError("Short write");
+}
+
+bool
+Session::recvmsg(ElementPtr& msg, bool nonblock)
+{
+    unsigned int length_net;
+    unsigned int ret;
+
+    ret = read(sock, &length_net, 4);
+    if (ret != 4)
+        throw SessionError("Short read");
+
+    unsigned int length = ntohl(length_net);
+    char *buffer = new char[length];
+    ret = read(sock, buffer, length);
+    if (ret != length)
+        throw SessionError("Short read");
+
+    std::string wire = std::string(buffer, length);
+    delete [] buffer;
+
+    std::stringstream wire_stream;
+    wire_stream <<wire;
+
+    msg = Element::from_wire(wire_stream, length);
+
+    return (true);
+    // XXXMLG handle non-block here, and return false for short reads
+}
+
+void
+Session::subscribe(std::string group, std::string instance, std::string subtype)
+{
+    ElementPtr env = Element::create(std::map<std::string, ElementPtr>());
+
+    env->set("type", Element::create("subscribe"));
+    env->set("group", Element::create(group));
+    env->set("instance", Element::create(instance));
+    env->set("subtype", Element::create(subtype));
+
+    sendmsg(env);
+}
+
+void
+Session::unsubscribe(std::string group, std::string instance)
+{
+    ElementPtr env = Element::create(std::map<std::string, ElementPtr>());
+
+    env->set("type", Element::create("unsubscribe"));
+    env->set("group", Element::create(group));
+    env->set("instance", Element::create(instance));
+
+    sendmsg(env);
+}
+
+unsigned int
+Session::group_sendmsg(ElementPtr& msg, std::string group, std::string instance, std::string to)
+{
+    ElementPtr env = Element::create(std::map<std::string, ElementPtr>());
+
+    env->set("type", Element::create("send"));
+    env->set("from", Element::create(lname));
+    env->set("to", Element::create(to));
+    env->set("group", Element::create(group));
+    env->set("instance", Element::create(instance));
+    env->set("seq", Element::create(sequence));
+    env->set("msg", Element::create(msg->to_wire()));
+
+    sendmsg(env);
+
+    return (sequence++);
+}
+
+bool
+Session::group_recvmsg(ElementPtr& envelope, ElementPtr& msg, bool nonblock)
+{
+    bool got_message = recvmsg(envelope, nonblock);
+    if (!got_message) {
+        return false;
+    }
+
+    msg = Element::from_wire(envelope->get("msg")->string_value());
+    envelope->remove("msg");
+
+    return (true);
+}
+
+unsigned int
+Session::reply(ElementPtr& envelope, ElementPtr& newmsg)
+{
+    ElementPtr env = Element::create(std::map<std::string, ElementPtr>());
+
+    env->set("type", Element::create("send"));
+    env->set("from", Element::create(lname));
+    env->set("to", Element::create(envelope->get("from")->string_value()));
+    env->set("group", Element::create(envelope->get("group")->string_value()));
+    env->set("instance", Element::create(envelope->get("instance")->string_value()));
+    env->set("seq", Element::create(sequence));
+    env->set("msg", Element::create(newmsg->to_wire()));
+    env->set("reply", Element::create(envelope->get("seq")->string_value()));
+
+    sendmsg(env);
+
+    return (sequence++);
+}

+ 56 - 0
src/lib/cc/cpp/session.h

@@ -0,0 +1,56 @@
+#ifndef _ISC_SESSION_H
+#define _ISC_SESSION_H 1
+
+#include <string>
+#include <vector>
+#include <map>
+
+#include <iostream>
+
+#include "data.h"
+
+namespace ISC {
+    namespace CC {
+        class SessionError : public std::exception {
+        public:
+            SessionError(std::string m = "CC Session Error") : msg(m) {}
+            ~SessionError() throw() {}
+            const char* what() const throw() { return msg.c_str(); }
+        private:
+            std::string msg;
+        };
+
+        class Session {
+        private:
+            int sock;
+            int sequence; // the next sequence number to use
+
+        public:
+            std::string lname;
+
+            Session();
+
+            void establish();
+            void disconnect();
+            void sendmsg(ISC::Data::ElementPtr& msg);
+            bool recvmsg(ISC::Data::ElementPtr& msg,
+                         bool nonblock = true);
+            void subscribe(std::string group,
+                           std::string instance = "*",
+                           std::string subtype = "normal");
+            void unsubscribe(std::string group,
+                             std::string instance = "*");
+            unsigned int group_sendmsg(ISC::Data::ElementPtr& msg,
+                                       std::string group,
+                                       std::string instance = "*",
+                                       std::string to = "*");
+            bool group_recvmsg(ISC::Data::ElementPtr& envelope,
+                               ISC::Data::ElementPtr& msg,
+                               bool nonblock = true);
+            unsigned int reply(ISC::Data::ElementPtr& envelope,
+                               ISC::Data::ElementPtr& newmsg);
+        };
+    } // namespace CC
+} // namespace ISC
+
+#endif // _ISC_SESSION_H