Code

:) Wrong line Josh :D
[inkscape.git] / src / pedro / pedroxmpp.cpp
index e64a568f059e76786efecbd3e4d14460cd66e524..4082881875bbf237dafebcf3430647dd3477e419 100644 (file)
@@ -4,7 +4,7 @@
  * Authors:
  *   Bob Jamison
  *
- * Copyright (C) 2005 Bob Jamison
+ * Copyright (C) 2005-2006 Bob Jamison
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
 
 #include <sys/stat.h>
 
-#include "pedroxmpp.h"
-#include "pedrodom.h"
-
-#include <map>
-
-#ifdef __WIN32__
-
-#include <windows.h>
-
-#else /* UNIX */
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netdb.h>
-#include <unistd.h>
-#include <sys/ioctl.h>
-
-#include <pthread.h>
-
-#endif
-
-#ifdef HAVE_SSL
-#include <openssl/ssl.h>
-#include <openssl/err.h>
-#endif
-
-
-namespace Pedro
-{
-
-//########################################################################
-//########################################################################
-//### U T I L I T Y
-//########################################################################
-//########################################################################
-
-
-//########################################################################
-//# B A S E    6 4
-//########################################################################
-
-//#################
-//# ENCODER
-//#################
-
-
-static char *base64encode =
-    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-/**
- * This class is for Base-64 encoding
- */
-class Base64Encoder
-{
-
-public:
-
-    Base64Encoder()
-        {
-        reset();
-        }
-
-    virtual ~Base64Encoder()
-        {}
-
-    virtual void reset()
-        {
-        outBuf   = 0L;
-        bitCount = 0;
-        buf = "";
-        }
-
-    virtual void append(int ch);
-
-    virtual void append(char *str);
-
-    virtual void append(unsigned char *str, int len);
-
-    virtual void append(const DOMString &str);
-
-    virtual DOMString finish();
-
-    static DOMString encode(const DOMString &str);
-
-
-private:
-
-
-    unsigned long outBuf;
-
-    int bitCount;
-
-    DOMString buf;
-
-};
-
-
-
-/**
- * Writes the specified byte to the output buffer
- */
-void Base64Encoder::append(int ch)
-{
-    outBuf   <<=  8;
-    outBuf   |=  (ch & 0xff);
-    bitCount +=  8;
-    if (bitCount >= 24)
-        {
-        int indx  = (int)((outBuf & 0x00fc0000L) >> 18);
-        int obyte = (int)base64encode[indx & 63];
-        buf.push_back(obyte);
-
-        indx      = (int)((outBuf & 0x0003f000L) >> 12);
-        obyte     = (int)base64encode[indx & 63];
-        buf.push_back(obyte);
-
-        indx      = (int)((outBuf & 0x00000fc0L) >>  6);
-        obyte     = (int)base64encode[indx & 63];
-        buf.push_back(obyte);
-
-        indx      = (int)((outBuf & 0x0000003fL)      );
-        obyte     = (int)base64encode[indx & 63];
-        buf.push_back(obyte);
-
-        bitCount = 0;
-        outBuf   = 0L;
-        }
-}
-
-/**
- * Writes the specified string to the output buffer
- */
-void Base64Encoder::append(char *str)
-{
-    while (*str)
-        append((int)*str++);
-}
-
-/**
- * Writes the specified string to the output buffer
- */
-void Base64Encoder::append(unsigned char *str, int len)
-{
-    while (len>0)
-        {
-        append((int)*str++);
-        len--;
-        }
-}
-
-/**
- * Writes the specified string to the output buffer
- */
-void Base64Encoder::append(const DOMString &str)
-{
-    append((char *)str.c_str());
-}
-
-/**
- * Closes this output stream and releases any system resources
- * associated with this stream.
- */
-DOMString Base64Encoder::finish()
-{
-    //get any last bytes (1 or 2) out of the buffer
-    if (bitCount == 16)
-        {
-        outBuf <<= 2;  //pad to make 18 bits
-
-        int indx  = (int)((outBuf & 0x0003f000L) >> 12);
-        int obyte = (int)base64encode[indx & 63];
-        buf.push_back(obyte);
-
-        indx      = (int)((outBuf & 0x00000fc0L) >>  6);
-        obyte     = (int)base64encode[indx & 63];
-        buf.push_back(obyte);
-
-        indx      = (int)((outBuf & 0x0000003fL)      );
-        obyte     = (int)base64encode[indx & 63];
-        buf.push_back(obyte);
-
-        buf.push_back('=');
-        }
-    else if (bitCount == 8)
-        {
-        outBuf <<= 4; //pad to make 12 bits
-
-        int indx  = (int)((outBuf & 0x00000fc0L) >>  6);
-        int obyte = (int)base64encode[indx & 63];
-        buf.push_back(obyte);
-
-        indx      = (int)((outBuf & 0x0000003fL)      );
-        obyte     = (int)base64encode[indx & 63];
-        buf.push_back(obyte);
-
-        buf.push_back('=');
-        buf.push_back('=');
-        }
-
-    DOMString ret = buf;
-    reset();
-    return ret;
-}
-
-
-DOMString Base64Encoder::encode(const DOMString &str)
-{
-    Base64Encoder encoder;
-    encoder.append(str);
-    DOMString ret = encoder.finish();
-    return ret;
-}
-
-
-
-//#################
-//# DECODER
-//#################
-
-static int base64decode[] =
-{
-/*00*/    -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-/*08*/    -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-/*10*/    -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-/*18*/    -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-/*20*/    -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-/*28*/    -1,   -1,   -1,   62,   -1,   -1,   -1,   63,
-/*30*/    52,   53,   54,   55,   56,   57,   58,   59,
-/*38*/    60,   61,   -1,   -1,   -1,   -1,   -1,   -1,
-/*40*/    -1,    0,    1,    2,    3,    4,    5,    6,
-/*48*/     7,    8,    9,   10,   11,   12,   13,   14,
-/*50*/    15,   16,   17,   18,   19,   20,   21,   22,
-/*58*/    23,   24,   25,   -1,   -1,   -1,   -1,   -1,
-/*60*/    -1,   26,   27,   28,   29,   30,   31,   32,
-/*68*/    33,   34,   35,   36,   37,   38,   39,   40,
-/*70*/    41,   42,   43,   44,   45,   46,   47,   48,
-/*78*/    49,   50,   51,   -1,   -1,   -1,   -1,   -1
-};
-
-class Base64Decoder
-{
-public:
-    Base64Decoder()
-        {
-        reset();
-        }
-
-    virtual ~Base64Decoder()
-        {}
-
-    virtual void reset()
-        {
-        inCount = 0;
-        buf.clear();
-        }
-
-
-    virtual void append(int ch);
-
-    virtual void append(char *str);
-
-    virtual void append(const DOMString &str);
-
-    std::vector<unsigned char> finish();
-
-    static std::vector<unsigned char> decode(const DOMString &str);
-
-    static DOMString decodeToString(const DOMString &str);
-
-private:
-
-    int inBytes[4];
-    int inCount;
-    std::vector<unsigned char> buf;
-};
-
-/**
- * Appends one char to the decoder
- */
-void Base64Decoder::append(int ch)
-{
-    if (isspace(ch))
-        return;
-    else if (ch == '=') //padding
-        {
-        inBytes[inCount++] = 0;
-        }
-    else
-        {
-        int byteVal = base64decode[ch & 0x7f];
-        //printf("char:%c %d\n", ch, byteVal);
-        if (byteVal < 0)
-            {
-            //Bad lookup value
-            }
-        inBytes[inCount++] = byteVal;
-        }
-
-    if (inCount >=4 )
-        {
-        unsigned char b0 = ((inBytes[0]<<2) & 0xfc) | ((inBytes[1]>>4) & 0x03);
-        unsigned char b1 = ((inBytes[1]<<4) & 0xf0) | ((inBytes[2]>>2) & 0x0f);
-        unsigned char b2 = ((inBytes[2]<<6) & 0xc0) | ((inBytes[3]   ) & 0x3f);
-        buf.push_back(b0);
-        buf.push_back(b1);
-        buf.push_back(b2);
-        inCount = 0;
-        }
-
-}
-
-void Base64Decoder::append(char *str)
-{
-    while (*str)
-        append((int)*str++);
-}
-
-void Base64Decoder::append(const DOMString &str)
-{
-    append((char *)str.c_str());
-}
-
-std::vector<unsigned char> Base64Decoder::finish()
-{
-    std::vector<unsigned char> ret = buf;
-    reset();
-    return ret;
-}
-
-std::vector<unsigned char> Base64Decoder::decode(const DOMString &str)
-{
-    Base64Decoder decoder;
-    decoder.append(str);
-    std::vector<unsigned char> ret = decoder.finish();
-    return ret;
-}
-
-DOMString Base64Decoder::decodeToString(const DOMString &str)
-{
-    Base64Decoder decoder;
-    decoder.append(str);
-    std::vector<unsigned char> ret = decoder.finish();
-    DOMString buf;
-    for (unsigned int i=0 ; i<ret.size() ; i++)
-        buf.push_back(ret[i]);
-    return buf;
-}
-
-
-
-//########################################################################
-//# S H A   1
-//########################################################################
-
-class Sha1
-{
-public:
-
-    /**
-     *
-     */
-    Sha1()
-        { init(); }
-
-    /**
-     *
-     */
-    virtual ~Sha1()
-        {}
-
-
-    /**
-     * Static convenience method.  This would be the most commonly used
-     * version;
-     * @parm digest points to a bufer of 20 unsigned chars
-     */
-    static void hash(unsigned char *dataIn, int len, unsigned char *digest);
-
-    /**
-     * Static convenience method.  This will fill a string with the hex
-     * codex string.
-     */
-    static DOMString hashHex(unsigned char *dataIn, int len);
-
-    /**
-     *  Initialize the context (also zeroizes contents)
-     */
-    virtual void init();
-
-    /**
-     *
-     */
-    virtual void append(unsigned char *dataIn, int len);
-
-    /**
-     *
-     * @parm digest points to a bufer of 20 unsigned chars
-     */
-    virtual void finish(unsigned char *digest);
-
-
-private:
-
-    void hashblock();
-
-    unsigned long H[5];
-    unsigned long W[80];
-    unsigned long sizeHi,sizeLo;
-    int lenW;
-
-};
-
-
-
-void Sha1::hash(unsigned char *dataIn, int len, unsigned char *digest)
-{
-    Sha1 sha1;
-    sha1.append(dataIn, len);
-    sha1.finish(digest);
-}
-
-static char *sha1hex = "0123456789abcdef";
-
-DOMString Sha1::hashHex(unsigned char *dataIn, int len)
-{
-    unsigned char hashout[20];
-    hash(dataIn, len, hashout);
-    DOMString ret;
-    for (int i=0 ; i<20 ; i++)
-        {
-        unsigned char ch = hashout[i];
-        ret.push_back(sha1hex[ (ch>>4) & 15 ]);
-        ret.push_back(sha1hex[ ch      & 15 ]);
-        }
-    return ret;
-}
-
-
-void Sha1::init()
-{
-
-    lenW   = 0;
-    sizeHi = 0;
-    sizeLo = 0;
-
-    // Initialize H with the magic constants (see FIPS180 for constants)
-    H[0] = 0x67452301L;
-    H[1] = 0xefcdab89L;
-    H[2] = 0x98badcfeL;
-    H[3] = 0x10325476L;
-    H[4] = 0xc3d2e1f0L;
-
-    for (int i = 0; i < 80; i++)
-        W[i] = 0;
-}
-
-
-void Sha1::append(unsigned char *dataIn, int len)
-{
-    // Read the data into W and process blocks as they get full
-    for (int i = 0; i < len; i++)
-        {
-        W[lenW / 4] <<= 8;
-        W[lenW / 4] |= (unsigned long)dataIn[i];
-        if ((++lenW) % 64 == 0)
-            {
-            hashblock();
-            lenW = 0;
-            }
-        sizeLo += 8;
-        sizeHi += (sizeLo < 8);
-        }
-}
-
-
-void Sha1::finish(unsigned char hashout[20])
-{
-    unsigned char pad0x80 = 0x80;
-    unsigned char pad0x00 = 0x00;
-    unsigned char padlen[8];
-
-    // Pad with a binary 1 (e.g. 0x80), then zeroes, then length
-    padlen[0] = (unsigned char)((sizeHi >> 24) & 255);
-    padlen[1] = (unsigned char)((sizeHi >> 16) & 255);
-    padlen[2] = (unsigned char)((sizeHi >>  8) & 255);
-    padlen[3] = (unsigned char)((sizeHi >>  0) & 255);
-    padlen[4] = (unsigned char)((sizeLo >> 24) & 255);
-    padlen[5] = (unsigned char)((sizeLo >> 16) & 255);
-    padlen[6] = (unsigned char)((sizeLo >>  8) & 255);
-    padlen[7] = (unsigned char)((sizeLo >>  0) & 255);
-
-    append(&pad0x80, 1);
-
-    while (lenW != 56)
-        append(&pad0x00, 1);
-    append(padlen, 8);
-
-    // Output hash
-    for (int i = 0; i < 20; i++)
-        {
-        hashout[i] = (unsigned char)(H[i / 4] >> 24);
-        H[i / 4] <<= 8;
-        }
-
-    // Re-initialize the context (also zeroizes contents)
-    init();
-}
-
-
-#define SHA_ROTL(X,n) ((((X) << (n)) | ((X) >> (32-(n)))) & 0xffffffffL)
-
-void Sha1::hashblock()
-{
-
-    for (int t = 16; t <= 79; t++)
-        W[t] = SHA_ROTL(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1);
-
-    unsigned long A = H[0];
-    unsigned long B = H[1];
-    unsigned long C = H[2];
-    unsigned long D = H[3];
-    unsigned long E = H[4];
-
-    unsigned long TEMP;
-
-    for (int t = 0; t <= 19; t++)
-        {
-        TEMP = (SHA_ROTL(A,5) + (((C^D)&B)^D) +
-                E + W[t] + 0x5a827999L) & 0xffffffffL;
-        E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
-        }
-    for (int t = 20; t <= 39; t++)
-        {
-        TEMP = (SHA_ROTL(A,5) + (B^C^D) +
-                E + W[t] + 0x6ed9eba1L) & 0xffffffffL;
-        E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
-        }
-    for (int t = 40; t <= 59; t++)
-        {
-        TEMP = (SHA_ROTL(A,5) + ((B&C)|(D&(B|C))) +
-                E + W[t] + 0x8f1bbcdcL) & 0xffffffffL;
-        E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
-        }
-    for (int t = 60; t <= 79; t++)
-        {
-        TEMP = (SHA_ROTL(A,5) + (B^C^D) +
-                E + W[t] + 0xca62c1d6L) & 0xffffffffL;
-        E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
-        }
-
-    H[0] += A;
-    H[1] += B;
-    H[2] += C;
-    H[3] += D;
-    H[4] += E;
-}
-
-
-
-
-
-//########################################################################
-//# M D  5
-//########################################################################
-
-class Md5
-{
-public:
-
-    /**
-     *
-     */
-    Md5()
-        { init(); }
-
-    /**
-     *
-     */
-    virtual ~Md5()
-        {}
-
-    /**
-     * Static convenience method.
-     * @parm digest points to an buffer of 16 unsigned chars
-     */
-    static void hash(unsigned char *dataIn,
-                     unsigned long len, unsigned char *digest);
-
-    static DOMString Md5::hashHex(unsigned char *dataIn, unsigned long len);
-
-    /**
-     *  Initialize the context (also zeroizes contents)
-     */
-    virtual void init();
-
-    /**
-     *
-     */
-    virtual void append(unsigned char *dataIn, unsigned long len);
-
-    /**
-     *
-     */
-    virtual void append(const DOMString &str);
-
-    /**
-     * Finalize and output the hash.
-     * @parm digest points to an buffer of 16 unsigned chars
-     */
-    virtual void finish(unsigned char *digest);
-
-
-    /**
-     * Same as above , but hex to an output String
-     */
-    virtual DOMString finishHex();
-
-private:
-
-    void transform(unsigned long *buf, unsigned long *in);
-
-    unsigned long buf[4];
-    unsigned long bits[2];
-    unsigned char in[64];
-
-};
-
-
-
-
-void Md5::hash(unsigned char *dataIn, unsigned long len, unsigned char *digest)
-{
-    Md5 md5;
-    md5.append(dataIn, len);
-    md5.finish(digest);
-}
-
-DOMString Md5::hashHex(unsigned char *dataIn, unsigned long len)
-{
-    Md5 md5;
-    md5.append(dataIn, len);
-    DOMString ret = md5.finishHex();
-    return ret;
-}
-
-
-
-/*
- * Note: this code is harmless on little-endian machines.
- */
-/*
-static void byteReverse(unsigned char *buf, unsigned long longs)
-{
-    do
-        {
-        unsigned long t = (unsigned long)
-            ((unsigned) buf[3] << 8 | buf[2]) << 16 |
-            ((unsigned) buf[1] << 8 | buf[0]);
-        *(unsigned long *) buf = t;
-        buf += 4;
-        } while (--longs);
-}
-*/
-
-static void md5_memcpy(void *dest, void *src, int n)
-{
-    unsigned char *s1 = (unsigned char *)dest;
-    unsigned char *s2 = (unsigned char *)src;
-    while (n--)
-        *s1++ = *s2++;
-}
-
-static void md5_memset(void *dest, char v, int n)
-{
-    unsigned char *s = (unsigned char *)dest;
-    while (n--)
-        *s++ = v;
-}
-
-/**
- * Initialize MD5 polynomials and storage
- */
-void Md5::init()
-{
-    buf[0]  = 0x67452301;
-    buf[1]  = 0xefcdab89;
-    buf[2]  = 0x98badcfe;
-    buf[3]  = 0x10325476;
-
-    bits[0] = 0;
-    bits[1] = 0;
-}
-
-/*
- * Update context to reflect the concatenation of another buffer full
- * of bytes.
- */
-void Md5::append(unsigned char *source, unsigned long len)
-{
-
-    // Update bitcount
-    unsigned long t = bits[0];
-    if ((bits[0] = t + ((unsigned long) len << 3)) < t)
-           bits[1]++;// Carry from low to high
-    bits[1] += len >> 29;
-
-       //Bytes already in shsInfo->data
-    t = (t >> 3) & 0x3f;
-
-
-    // Handle any leading odd-sized chunks
-    if (t)
-        {
-        unsigned char *p = (unsigned char *) in + t;
-        t = 64 - t;
-        if (len < t)
-            {
-            md5_memcpy(p, source, len);
-            return;
-            }
-        md5_memcpy(p, source, t);
-        //byteReverse(in, 16);
-        transform(buf, (unsigned long *) in);
-        source += t;
-        len    -= t;
-        }
-
-    // Process data in 64-byte chunks
-    while (len >= 64)
-        {
-        md5_memcpy(in, source, 64);
-        //byteReverse(in, 16);
-        transform(buf, (unsigned long *) in);
-        source += 64;
-        len    -= 64;
-        }
-
-    // Handle any remaining bytes of data.
-    md5_memcpy(in, source, len);
-}
-
-/*
- * Update context to reflect the concatenation of another string
- */
-void Md5::append(const DOMString &str)
-{
-    append((unsigned char *)str.c_str(), str.size());
-}
-
-/*
- * Final wrapup - pad to 64-byte boundary with the bit pattern
- * 1 0* (64-bit count of bits processed, MSB-first)
- */
-void Md5::finish(unsigned char *digest)
-{
-    // Compute number of bytes mod 64
-    unsigned int count = (bits[0] >> 3) & 0x3F;
-
-    // Set the first char of padding to 0x80.
-    // This is safe since there is always at least one byte free
-    unsigned char *p = in + count;
-    *p++ = 0x80;
-
-    // Bytes of padding needed to make 64 bytes
-    count = 64 - 1 - count;
-
-    // Pad out to 56 mod 64
-    if (count < 8)
-        {
-           // Two lots of padding:  Pad the first block to 64 bytes
-           md5_memset(p, 0, count);
-           //byteReverse(in, 16);
-           transform(buf, (unsigned long *) in);
-
-           // Now fill the next block with 56 bytes
-           md5_memset(in, 0, 56);
-        }
-    else
-        {
-        // Pad block to 56 bytes
-        md5_memset(p, 0, count - 8);
-        }
-    //byteReverse(in, 14);
-
-    // Append length in bits and transform
-    ((unsigned long *) in)[14] = bits[0];
-    ((unsigned long *) in)[15] = bits[1];
-
-    transform(buf, (unsigned long *) in);
-    //byteReverse((unsigned char *) buf, 4);
-    md5_memcpy(digest, buf, 16);
-    init();  // Security!  ;-)
-}
-
-static char *md5hex = "0123456789abcdef";
-
-DOMString Md5::finishHex()
-{
-    unsigned char hashout[16];
-    finish(hashout);
-    DOMString ret;
-    for (int i=0 ; i<16 ; i++)
-        {
-        unsigned char ch = hashout[i];
-        ret.push_back(md5hex[ (ch>>4) & 15 ]);
-        ret.push_back(md5hex[ ch      & 15 ]);
-        }
-    return ret;
-}
-
-
-
-//#  The four core functions - F1 is optimized somewhat
-
-//  #define F1(x, y, z) (x & y | ~x & z)
-#define F1(x, y, z) (z ^ (x & (y ^ z)))
-#define F2(x, y, z) F1(z, x, y)
-#define F3(x, y, z) (x ^ y ^ z)
-#define F4(x, y, z) (y ^ (x | ~z))
-
-// ## This is the central step in the MD5 algorithm.
-#define MD5STEP(f, w, x, y, z, data, s) \
-       ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
-
-/*
- * The core of the MD5 algorithm, this alters an existing MD5 hash to
- * reflect the addition of 16 longwords of new data.  MD5Update blocks
- * the data and converts bytes into longwords for this routine.
- * @parm buf points to an array of 4 unsigned longs
- * @parm in points to an array of 16 unsigned longs
- */
-void Md5::transform(unsigned long *buf, unsigned long *in)
-{
-    unsigned long a = buf[0];
-    unsigned long b = buf[1];
-    unsigned long c = buf[2];
-    unsigned long d = buf[3];
-
-    MD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478,  7);
-    MD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12);
-    MD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17);
-    MD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22);
-    MD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf,  7);
-    MD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12);
-    MD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17);
-    MD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22);
-    MD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8,  7);
-    MD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12);
-    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
-    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
-    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122,  7);
-    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
-    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
-    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
-
-    MD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562,  5);
-    MD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340,  9);
-    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
-    MD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20);
-    MD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d,  5);
-    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453,  9);
-    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
-    MD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20);
-    MD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6,  5);
-    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6,  9);
-    MD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14);
-    MD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20);
-    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905,  5);
-    MD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8,  9);
-    MD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14);
-    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
-
-    MD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942,  4);
-    MD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11);
-    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
-    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
-    MD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44,  4);
-    MD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11);
-    MD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16);
-    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
-    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6,  4);
-    MD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11);
-    MD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16);
-    MD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23);
-    MD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039,  4);
-    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
-    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
-    MD5STEP(F3, b, c, d, a, in[ 2] + 0xc4ac5665, 23);
-
-    MD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244,  6);
-    MD5STEP(F4, d, a, b, c, in[ 7] + 0x432aff97, 10);
-    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
-    MD5STEP(F4, b, c, d, a, in[ 5] + 0xfc93a039, 21);
-    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3,  6);
-    MD5STEP(F4, d, a, b, c, in[ 3] + 0x8f0ccc92, 10);
-    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
-    MD5STEP(F4, b, c, d, a, in[ 1] + 0x85845dd1, 21);
-    MD5STEP(F4, a, b, c, d, in[ 8] + 0x6fa87e4f,  6);
-    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
-    MD5STEP(F4, c, d, a, b, in[ 6] + 0xa3014314, 15);
-    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
-    MD5STEP(F4, a, b, c, d, in[ 4] + 0xf7537e82,  6);
-    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
-    MD5STEP(F4, c, d, a, b, in[ 2] + 0x2ad7d2bb, 15);
-    MD5STEP(F4, b, c, d, a, in[ 9] + 0xeb86d391, 21);
-
-    buf[0] += a;
-    buf[1] += b;
-    buf[2] += c;
-    buf[3] += d;
-}
-
-
-
-
-
-
-
-//########################################################################
-//########################################################################
-//### T H R E A D
-//########################################################################
-//########################################################################
-
-
-//########################################################################
-//### T H R E A D
-//########################################################################
-
-/**
- * This is the interface for a delegate class which can
- * be run by a Thread.
- * Thread thread(runnable);
- * thread.start();
- */
-class Runnable
-{
-public:
-
-    Runnable()
-        {}
-    virtual ~Runnable()
-        {}
-
-    /**
-     * The method of a delegate class which can
-     * be run by a Thread.  Thread is completed when this
-     * method is done.
-     */
-    virtual void run() = 0;
-
-};
-
-
-
-/**
- *  A simple wrapper of native threads in a portable class.
- *  It can be used either to execute its own run() method, or
- *  delegate to a Runnable class's run() method.
- */
-class Thread
-{
-public:
-
-    /**
-     *  Create a thread which will execute its own run() method.
-     */
-    Thread()
-        { runnable = NULL ; started = false; }
-
-    /**
-     * Create a thread which will run a Runnable class's run() method.
-     */
-    Thread(const Runnable &runner)
-        { runnable = (Runnable *)&runner; started = false; }
-
-    /**
-     *  This does not kill a spawned thread.
-     */
-    virtual ~Thread()
-        {}
-
-    /**
-     *  Static method to pause the current thread for a given
-     *  number of milliseconds.
-     */
-    static void sleep(unsigned long millis);
-
-    /**
-     *  This method will be executed if the Thread was created with
-     *  no delegated Runnable class.  The thread is completed when
-     *  the method is done.
-     */
-    virtual void run()
-        {}
-
-    /**
-     *  Starts the thread.
-     */
-    virtual void start();
-
-    /**
-     *  Calls either this class's run() method, or that of a Runnable.
-     *  A user would normally not call this directly.
-     */
-    virtual void execute()
-        {
-        started = true;
-        if (runnable)
-            runnable->run();
-        else
-            run();
-        }
-
-private:
-
-    Runnable *runnable;
-
-    bool started;
-
-};
-
-
-
-
-
-#ifdef __WIN32__
-
-
-static DWORD WINAPI WinThreadFunction(LPVOID context)
-{
-    Thread *thread = (Thread *)context;
-    thread->execute();
-    return 0;
-}
-
-
-void Thread::start()
-{
-    DWORD dwThreadId;
-    HANDLE hThread = CreateThread(NULL, 0, WinThreadFunction,
-               (LPVOID)this,  0,  &dwThreadId);
-    //Make sure the thread is started before 'this' is deallocated
-    while (!started)
-        sleep(10);
-    CloseHandle(hThread);
-}
-
-void Thread::sleep(unsigned long millis)
-{
-    Sleep(millis);
-}
-
-#else /* UNIX */
-
-
-void *PthreadThreadFunction(void *context)
-{
-    Thread *thread = (Thread *)context;
-    thread->execute();
-    return NULL;
-}
-
-
-void Thread::start()
-{
-    pthread_t thread;
-
-    int ret = pthread_create(&thread, NULL,
-            PthreadThreadFunction, (void *)this);
-    if (ret != 0)
-        printf("Thread::start: thread creation failed: %s\n", strerror(ret));
-
-    //Make sure the thread is started before 'this' is deallocated
-    while (!started)
-        sleep(10);
-
-}
-
-void Thread::sleep(unsigned long millis)
-{
-    timespec requested;
-    requested.tv_sec = millis / 1000;
-    requested.tv_nsec = (millis % 1000 ) * 1000000L;
-    nanosleep(&requested, NULL);
-}
-
-#endif
-
-
-//########################################################################
-//########################################################################
-//### S O C K E T
-//########################################################################
-//########################################################################
-
-
-
-
-
-class TcpSocket
-{
-public:
-
-    TcpSocket();
-
-    TcpSocket(const std::string &hostname, int port);
-
-    TcpSocket(const char *hostname, int port);
-
-    TcpSocket(const TcpSocket &other);
-
-    virtual ~TcpSocket();
-
-    bool isConnected();
-
-    void enableSSL(bool val);
-
-    bool getEnableSSL();
-
-    bool connect(const std::string &hostname, int portno);
-
-    bool connect(const char *hostname, int portno);
-
-    bool startTls();
-
-    bool connect();
-
-    bool disconnect();
-
-    bool setReceiveTimeout(unsigned long millis);
-
-    long available();
-
-    bool write(int ch);
-
-    bool write(char *str);
-
-    bool write(const std::string &str);
-
-    int read();
-
-    std::string readLine();
-
-private:
-    void init();
-
-    std::string hostname;
-    int  portno;
-    int  sock;
-    bool connected;
-
-    bool sslEnabled;
-
-    unsigned long receiveTimeout;
-
-#ifdef HAVE_SSL
-    SSL_CTX *sslContext;
-    SSL *sslStream;
-#endif
-
-};
-
-
-
-//#########################################################################
-//# U T I L I T Y
-//#########################################################################
-
-static void mybzero(void *s, size_t n)
-{
-    unsigned char *p = (unsigned char *)s;
-    while (n > 0)
-        {
-        *p++ = (unsigned char)0;
-        n--;
-        }
-}
-
-static void mybcopy(void *src, void *dest, size_t n)
-{
-    unsigned char *p = (unsigned char *)dest;
-    unsigned char *q = (unsigned char *)src;
-    while (n > 0)
-        {
-        *p++ = *q++;
-        n--;
-        }
-}
-
-
-
-//#########################################################################
-//# T C P    C O N N E C T I O N
-//#########################################################################
-
-TcpSocket::TcpSocket()
-{
-    init();
-}
-
-
-TcpSocket::TcpSocket(const char *hostnameArg, int port)
-{
-    init();
-    hostname  = hostnameArg;
-    portno    = port;
-}
-
-TcpSocket::TcpSocket(const std::string &hostnameArg, int port)
-{
-    init();
-    hostname  = hostnameArg;
-    portno    = port;
-}
-
-
-#ifdef HAVE_SSL
-
-static void cryptoLockCallback(int mode, int type, const char *file, int line)
-{
-    //printf("########### LOCK\n");
-    static int modes[CRYPTO_NUM_LOCKS]; /* = {0, 0, ... } */
-    const char *errstr = NULL;
-
-    int rw = mode & (CRYPTO_READ|CRYPTO_WRITE);
-    if (!((rw == CRYPTO_READ) || (rw == CRYPTO_WRITE)))
-        {
-        errstr = "invalid mode";
-        goto err;
-        }
-
-    if (type < 0 || type >= CRYPTO_NUM_LOCKS)
-        {
-        errstr = "type out of bounds";
-        goto err;
-        }
-
-    if (mode & CRYPTO_LOCK)
-        {
-        if (modes[type])
-            {
-            errstr = "already locked";
-            /* must not happen in a single-threaded program
-             * (would deadlock)
-             */
-            goto err;
-            }
-
-        modes[type] = rw;
-        }
-    else if (mode & CRYPTO_UNLOCK)
-        {
-        if (!modes[type])
-            {
-             errstr = "not locked";
-             goto err;
-             }
-
-        if (modes[type] != rw)
-            {
-            errstr = (rw == CRYPTO_READ) ?
-                  "CRYPTO_r_unlock on write lock" :
-                  "CRYPTO_w_unlock on read lock";
-            }
-
-        modes[type] = 0;
-        }
-    else
-        {
-        errstr = "invalid mode";
-        goto err;
-        }
-
-    err:
-    if (errstr)
-        {
-        /* we cannot use bio_err here */
-        fprintf(stderr, "openssl (lock_dbg_cb): %s (mode=%d, type=%d) at %s:%d\n",
-                errstr, mode, type, file, line);
-        }
-}
-
-static unsigned long cryptoIdCallback()
-{
-#ifdef __WIN32__
-    unsigned long ret = (unsigned long) GetCurrentThreadId();
-#else
-    unsigned long ret = (unsigned long) pthread_self();
-#endif
-    return ret;
-}
-
-#endif
-
-
-TcpSocket::TcpSocket(const TcpSocket &other)
-{
-    init();
-    sock      = other.sock;
-    hostname  = other.hostname;
-    portno    = other.portno;
-}
-
-static bool tcp_socket_inited = false;
-
-void TcpSocket::init()
-{
-    if (!tcp_socket_inited)
-        {
-#ifdef __WIN32__
-        WORD wVersionRequested = MAKEWORD( 2, 2 );
-        WSADATA wsaData;
-        WSAStartup( wVersionRequested, &wsaData );
-#endif
-#ifdef HAVE_SSL
-        sslStream  = NULL;
-        sslContext = NULL;
-           CRYPTO_set_locking_callback(cryptoLockCallback);
-        CRYPTO_set_id_callback(cryptoIdCallback);
-        SSL_library_init();
-        SSL_load_error_strings();
-#endif
-        tcp_socket_inited = true;
-        }
-    sock           = -1;
-    connected      = false;
-    hostname       = "";
-    portno         = -1;
-    sslEnabled     = false;
-    receiveTimeout = 0;
-}
-
-TcpSocket::~TcpSocket()
-{
-    disconnect();
-}
-
-bool TcpSocket::isConnected()
-{
-    if (!connected || sock < 0)
-        return false;
-    return true;
-}
-
-void TcpSocket::enableSSL(bool val)
-{
-    sslEnabled = val;
-}
-
-bool TcpSocket::getEnableSSL()
-{
-    return sslEnabled;
-}
-
-
-bool TcpSocket::connect(const char *hostnameArg, int portnoArg)
-{
-    hostname = hostnameArg;
-    portno   = portnoArg;
-    return connect();
-}
-
-bool TcpSocket::connect(const std::string &hostnameArg, int portnoArg)
-{
-    hostname = hostnameArg;
-    portno   = portnoArg;
-    return connect();
-}
-
-
-
-#ifdef HAVE_SSL
-/*
-static int password_cb(char *buf, int bufLen, int rwflag, void *userdata)
-{
-    char *password = "password";
-    if (bufLen < (int)(strlen(password)+1))
-        return 0;
-
-    strcpy(buf,password);
-    int ret = strlen(password);
-    return ret;
-}
-
-static void infoCallback(const SSL *ssl, int where, int ret)
-{
-    switch (where)
-        {
-        case SSL_CB_ALERT:
-            {
-            printf("## %d SSL ALERT: %s\n",  where, SSL_alert_desc_string_long(ret));
-            break;
-            }
-        default:
-            {
-            printf("## %d SSL: %s\n",  where, SSL_state_string_long(ssl));
-            break;
-            }
-        }
-}
-*/
-#endif
-
-
-bool TcpSocket::startTls()
-{
-#ifdef HAVE_SSL
-    sslStream  = NULL;
-    sslContext = NULL;
-
-    //SSL_METHOD *meth = SSLv23_method();
-    //SSL_METHOD *meth = SSLv3_client_method();
-    SSL_METHOD *meth = TLSv1_client_method();
-    sslContext = SSL_CTX_new(meth);
-    //SSL_CTX_set_info_callback(sslContext, infoCallback);
-
-#if 0
-    char *keyFile  = "client.pem";
-    char *caList   = "root.pem";
-    /* Load our keys and certificates*/
-    if (!(SSL_CTX_use_certificate_chain_file(sslContext, keyFile)))
-        {
-        fprintf(stderr, "Can't read certificate file\n");
-        disconnect();
-        return false;
-        }
-
-    SSL_CTX_set_default_passwd_cb(sslContext, password_cb);
-
-    if (!(SSL_CTX_use_PrivateKey_file(sslContext, keyFile, SSL_FILETYPE_PEM)))
-        {
-        fprintf(stderr, "Can't read key file\n");
-        disconnect();
-        return false;
-        }
-
-    /* Load the CAs we trust*/
-    if (!(SSL_CTX_load_verify_locations(sslContext, caList, 0)))
-        {
-        fprintf(stderr, "Can't read CA list\n");
-        disconnect();
-        return false;
-        }
-#endif
-
-    /* Connect the SSL socket */
-    sslStream  = SSL_new(sslContext);
-    SSL_set_fd(sslStream, sock);
-
-    int ret = SSL_connect(sslStream);
-    if (ret == 0)
-        {
-        fprintf(stderr, "SSL connection not successful\n");
-        disconnect();
-        return false;
-        }
-    else if (ret < 0)
-        {
-        int err = SSL_get_error(sslStream, ret);
-        fprintf(stderr, "SSL connect error %d\n", err);
-        disconnect();
-        return false;
-        }
-
-    sslEnabled = true;
-#endif /*HAVE_SSL*/
-    return true;
-}
-
-
-bool TcpSocket::connect()
-{
-    if (hostname.size()<1)
-        {
-        fprintf(stderr, "open: null hostname\n");
-        return false;
-        }
-
-    if (portno<1)
-        {
-        fprintf(stderr, "open: bad port number\n");
-        return false;
-        }
-
-    sock = socket(PF_INET, SOCK_STREAM, 0);
-    if (sock < 0)
-        {
-        fprintf(stderr, "open: error creating socket\n");
-        return false;
-        }
-
-    char *c_hostname = (char *)hostname.c_str();
-    struct hostent *server = gethostbyname(c_hostname);
-    if (!server)
-        {
-        fprintf(stderr, "open: could not locate host '%s'\n", c_hostname);
-        return false;
-        }
-
-    struct sockaddr_in serv_addr;
-    mybzero((char *) &serv_addr, sizeof(serv_addr));
-    serv_addr.sin_family = AF_INET;
-    mybcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr,
-         server->h_length);
-    serv_addr.sin_port = htons(portno);
-
-    int ret = ::connect(sock, (const sockaddr *)&serv_addr, sizeof(serv_addr));
-    if (ret < 0)
-        {
-        fprintf(stderr, "open: could not connect to host '%s'\n", c_hostname);
-        return false;
-        }
-
-     if (sslEnabled)
-        {
-        if (!startTls())
-            return false;
-        }
-    connected = true;
-    return true;
-}
-
-bool TcpSocket::disconnect()
-{
-    bool ret  = true;
-    connected = false;
-#ifdef HAVE_SSL
-    if (sslEnabled)
-        {
-        if (sslStream)
-            {
-            int r = SSL_shutdown(sslStream);
-            switch(r)
-                {
-                case 1:
-                    break; /* Success */
-                case 0:
-                case -1:
-                default:
-                    //printf("Shutdown failed");
-                    ret = false;
-                }
-            SSL_free(sslStream);
-            }
-        if (sslContext)
-            SSL_CTX_free(sslContext);
-        }
-    sslStream  = NULL;
-    sslContext = NULL;
-#endif /*HAVE_SSL*/
-
-#ifdef __WIN32__
-    closesocket(sock);
-#else
-    ::close(sock);
-#endif
-    sock = -1;
-    sslEnabled = false;
-
-    return ret;
-}
-
-
-
-bool TcpSocket::setReceiveTimeout(unsigned long millis)
-{
-    receiveTimeout = millis;
-    return true;
-}
-
-/**
- * For normal sockets, return the number of bytes waiting to be received.
- * For SSL, just return >0 when something is ready to be read.
- */
-long TcpSocket::available()
-{
-    if (!isConnected())
-        return -1;
-
-    long count = 0;
-#ifdef __WIN32__
-    if (ioctlsocket(sock, FIONREAD, (unsigned long *)&count) != 0)
-        return -1;
-#else
-    if (ioctl(sock, FIONREAD, &count) != 0)
-        return -1;
-#endif
-    if (count<=0 && sslEnabled)
-        {
-#ifdef HAVE_SSL
-        return SSL_pending(sslStream);
-#endif
-        }
-    return count;
-}
-
-
-
-bool TcpSocket::write(int ch)
-{
-    if (!isConnected())
-        {
-        fprintf(stderr, "write: socket closed\n");
-        return false;
-        }
-    unsigned char c = (unsigned char)ch;
-
-    if (sslEnabled)
-        {
-#ifdef HAVE_SSL
-        int r = SSL_write(sslStream, &c, 1);
-        if (r<=0)
-            {
-            switch(SSL_get_error(sslStream, r))
-                {
-                default:
-                    fprintf(stderr, "SSL write problem");
-                    return -1;
-                }
-            }
-#endif
-        }
-    else
-        {
-        if (send(sock, (const char *)&c, 1, 0) < 0)
-        //if (send(sock, &c, 1, 0) < 0)
-            {
-            fprintf(stderr, "write: could not send data\n");
-            return false;
-            }
-        }
-    return true;
-}
-
-bool TcpSocket::write(char *str)
-{
-   if (!isConnected())
-        {
-        fprintf(stderr, "write(str): socket closed\n");
-        return false;
-        }
-    int len = strlen(str);
-
-    if (sslEnabled)
-        {
-#ifdef HAVE_SSL
-        int r = SSL_write(sslStream, (unsigned char *)str, len);
-        if (r<=0)
-            {
-            switch(SSL_get_error(sslStream, r))
-                {
-                default:
-                    fprintf(stderr, "SSL write problem");
-                    return -1;
-                }
-            }
-#endif
-        }
-    else
-        {
-        if (send(sock, str, len, 0) < 0)
-        //if (send(sock, &c, 1, 0) < 0)
-            {
-            fprintf(stderr, "write: could not send data\n");
-            return false;
-            }
-        }
-    return true;
-}
-
-bool TcpSocket::write(const std::string &str)
-{
-    return write((char *)str.c_str());
-}
+#include <time.h>
 
-int TcpSocket::read()
-{
-    if (!isConnected())
-        return -1;
+#include "pedroxmpp.h"
+#include "pedrodom.h"
+#include "pedroutil.h"
 
-    //We'll use this loop for timeouts, so that SSL and plain sockets
-    //will behave the same way
-    if (receiveTimeout > 0)
-        {
-        unsigned long tim = 0;
-        while (true)
-            {
-            int avail = available();
-            if (avail > 0)
-                break;
-            if (tim >= receiveTimeout)
-                return -2;
-            Thread::sleep(20);
-            tim += 20;
-            }
-        }
+#include <map>
 
-    //check again
-    if (!isConnected())
-        return -1;
 
-    unsigned char ch;
-    if (sslEnabled)
-        {
-#ifdef HAVE_SSL
-        if (!sslStream)
-            return -1;
-        int r = SSL_read(sslStream, &ch, 1);
-        unsigned long err = SSL_get_error(sslStream, r);
-        switch (err)
-            {
-            case SSL_ERROR_NONE:
-                 break;
-            case SSL_ERROR_ZERO_RETURN:
-                return -1;
-            case SSL_ERROR_SYSCALL:
-                fprintf(stderr, "SSL read problem(syscall) %s\n",
-                     ERR_error_string(ERR_get_error(), NULL));
-                return -1;
-            default:
-                fprintf(stderr, "SSL read problem %s\n",
-                     ERR_error_string(ERR_get_error(), NULL));
-                return -1;
-            }
-#endif
-        }
-    else
-        {
-        if (recv(sock, (char *)&ch, 1, 0) <= 0)
-            {
-            fprintf(stderr, "read: could not receive data\n");
-            disconnect();
-            return -1;
-            }
-        }
-    return (int)ch;
-}
 
-std::string TcpSocket::readLine()
+namespace Pedro
 {
-    std::string ret;
-
-    while (isConnected())
-        {
-        int ch = read();
-        if (ch<0)
-            return ret;
-        if (ch=='\r' || ch=='\n')
-            return ret;
-        ret.push_back((char)ch);
-        }
-
-    return ret;
-}
 
 
 //########################################################################
-//########################################################################
-//### X M P P
-//########################################################################
-//########################################################################
-
-
-
-
 //########################################################################
 //# X M P P    E V E N T
 //########################################################################
+//########################################################################
 
 
 XmppEvent::XmppEvent(int type)
@@ -1999,9 +248,20 @@ void XmppEvent::setUserList(const std::vector<XmppUser> &val)
     userList = val;
 }
 
+
+
+
+
+
+
+
+
+//########################################################################
 //########################################################################
 //# X M P P    E V E N T    T A R G E T
 //########################################################################
+//########################################################################
+
 
 //###########################
 //# CONSTRUCTORS
@@ -2028,18 +288,26 @@ XmppEventTarget::~XmppEventTarget()
 //# M E S S A G E S
 //###########################
 
+/**
+ *  Print a printf()-like formatted error message
+ */
 void XmppEventTarget::error(char *fmt, ...)
 {
     va_list args;
     va_start(args,fmt);
     vsnprintf(targetWriteBuf, targetWriteBufLen, fmt, args);
     va_end(args) ;
-    printf("Error:%s\n", targetWriteBuf);
+    fprintf(stderr, "Error:%s\n", targetWriteBuf);
     XmppEvent evt(XmppEvent::EVENT_ERROR);
     evt.setData(targetWriteBuf);
     dispatchXmppEvent(evt);
 }
 
+
+
+/**
+ *  Print a printf()-like formatted trace message
+ */
 void XmppEventTarget::status(char *fmt, ...)
 {
     va_list args;
@@ -2121,9 +389,15 @@ XmppEvent XmppEventTarget::eventQueuePop()
 }
 
 
+
+
+
+//########################################################################
 //########################################################################
 //# X M P P    S T R E A M
 //########################################################################
+//########################################################################
+
 
 /**
  *
@@ -2135,84 +409,145 @@ public:
     /**
      *
      */
-    XmppStream();
+    XmppStream()
+        { reset(); }
+
+    /**
+     *
+     */
+    XmppStream(const XmppStream &other)
+        { assign(other); }
+
+    /**
+     *
+     */
+    XmppStream &operator=(const XmppStream &other)
+        { assign(other); return *this; }
 
     /**
      *
      */
-    virtual ~XmppStream();
+    virtual ~XmppStream()
+        {}
 
     /**
      *
      */
-    virtual void reset();
+    virtual void reset()
+        {
+        state     = XmppClient::STREAM_AVAILABLE;
+        seqNr     = 0;
+        messageId = "";
+        sourceId  = "";
+        data.clear();
+        }
 
     /**
      *
      */
-    virtual int getState();
+    virtual int getState()
+        { return state; }
 
     /**
      *
      */
-    virtual void setState(int val);
+    virtual void setState(int val)
+        { state = val; }
 
     /**
      *
      */
-    virtual DOMString getStreamId();
+    virtual DOMString getStreamId()
+        { return streamId; }
 
     /**
      *
      */
-    void setStreamId(const DOMString &val);
+    void setStreamId(const DOMString &val)
+        { streamId = val; }
 
     /**
      *
      */
-    virtual DOMString getIqId();
+    virtual DOMString getMessageId()
+        { return messageId; }
 
     /**
      *
      */
-    void setIqId(const DOMString &val);
+    void setMessageId(const DOMString &val)
+        { messageId = val; }
 
     /**
      *
      */
-    virtual int getSeqNr();
+    virtual int getSeqNr()
+        {
+        seqNr++;
+        if (seqNr >= 65535)
+            seqNr = 0;
+        return seqNr;
+        }
 
     /**
      *
      */
-    virtual DOMString getPeerId();
+    virtual DOMString getPeerId()
+        { return sourceId; }
 
     /**
      *
      */
-    virtual void setPeerId(const DOMString &val);
+    virtual void setPeerId(const DOMString &val)
+        { sourceId = val; }
 
     /**
      *
      */
-    int available();
+    int available()
+        { return data.size(); }
 
     /**
      *
      */
-    void receiveData(std::vector<unsigned char> &newData);
+    void receiveData(std::vector<unsigned char> &newData)
+        {
+        std::vector<unsigned char>::iterator iter;
+        for (iter=newData.begin() ; iter!=newData.end() ; iter++)
+            data.push_back(*iter);
+        }
 
     /**
      *
      */
-    std::vector<unsigned char> read();
+    std::vector<unsigned char> read()
+        {
+        if (state != XmppClient::STREAM_OPEN)
+            {
+            std::vector<unsigned char>dummy;
+            return dummy;
+            }
+        std::vector<unsigned char> ret = data;
+        data.clear();
+        return ret;
+        }
 
 private:
 
+    void assign(const XmppStream &other)
+       {
+       streamId  = other.streamId;
+       messageId = other.messageId;
+       sourceId  = other.sourceId;
+       state     = other.state;
+       seqNr     = other.seqNr;
+       data      = other.data;
+       }
+
 
     DOMString streamId;
 
-    DOMString iqId;
+    DOMString messageId;
 
     DOMString sourceId;
 
@@ -2224,149 +559,20 @@ private:
 };
 
 
-/**
- *
- */
-XmppStream::XmppStream()
-{
-    reset();
-}
-
-/**
- *
- */
-XmppStream::~XmppStream()
-{
-    reset();
-}
-
-/**
- *
- */
-void XmppStream::reset()
-{
-    state = XmppClient::STREAM_AVAILABLE;
-    seqNr = 0;
-    data.clear();
-}
-
-/**
- *
- */
-int XmppStream::getState()
-{
-    return state;
-}
-
-/**
- *
- */
-void XmppStream::setState(int val)
-{
-    state = val;
-}
-
-/**
- *
- */
-DOMString XmppStream::getStreamId()
-{
-    return streamId;
-}
-
-/**
- *
- */
-void XmppStream::setStreamId(const DOMString &val)
-{
-    streamId = val;
-}
-
-/**
- *
- */
-DOMString XmppStream::getIqId()
-{
-    return iqId;
-}
-
-
-/**
- *
- */
-void XmppStream::setIqId(const DOMString &val)
-{
-    iqId = val;
-}
-
-/**
- *  Source or destination JID
- */
-void XmppStream::setPeerId(const DOMString &val)
-{
-    sourceId = val;
-}
-
-/**
- *  Source or destination JID
- */
-DOMString XmppStream::getPeerId()
-{
-    return sourceId;
-}
-
-/**
- *  Stream packet sequence number
- */
-int XmppStream::getSeqNr()
-{
-    seqNr++;
-    if (seqNr >= 65535)
-        seqNr = 0;
-    return seqNr;
-}
-
-/**
- *
- */
-int XmppStream::available()
-{
-    return data.size();
-}
 
-/**
- *
- */
-void XmppStream::receiveData(std::vector<unsigned char> &newData)
-{
-    std::vector<unsigned char>::iterator iter;
-    for (iter=newData.begin() ; iter!=newData.end() ; iter++)
-        data.push_back(*iter);
-}
 
-/**
- *
- */
-std::vector<unsigned char> XmppStream::read()
-{
-    if (state != XmppClient::STREAM_OPEN)
-        {
-        std::vector<unsigned char>dummy;
-        return dummy;
-        }
-    std::vector<unsigned char> ret = data;
-    data.clear();
-    return ret;
-}
 
 
 
 
 
 
+//########################################################################
 //########################################################################
 //# X M P P    C L I E N T
 //########################################################################
+//########################################################################
+
 class ReceiverThread : public Runnable
 {
 public:
@@ -2384,9 +590,12 @@ private:
 };
 
 
-//###########################
+
+
+
+//########################################################################
 //# CONSTRUCTORS
-//###########################
+//########################################################################
 
 XmppClient::XmppClient()
 {
@@ -2412,6 +621,7 @@ void XmppClient::assign(const XmppClient &other)
     connected     = other.connected;
     doRegister    = other.doRegister;
     groupChats    = other.groupChats;
+    streamPacket  = other.streamPacket;
 }
 
 
@@ -2421,43 +631,32 @@ void XmppClient::init()
     msgId         = 0;
     connected     = false;
     doRegister    = false;
+    streamPacket  = "message";
 
-    for (int i=0 ; i<outputStreamCount ; i++)
-        {
-        outputStreams[i] = new XmppStream();
-        }
-    for (int i=0 ; i<inputStreamCount ; i++)
-        {
-        inputStreams[i] = new XmppStream();
-        }
-    for (int i=0 ; i<fileSendCount ; i++)
-        {
-        fileSends[i] = new XmppStream();
-        }
 }
 
 XmppClient::~XmppClient()
 {
     disconnect();
     delete sock;
-    for (int i=0 ; i<outputStreamCount ; i++)
-        {
-        delete outputStreams[i];
-        }
-    for (int i=0 ; i<inputStreamCount ; i++)
-        {
-        delete inputStreams[i];
-        }
-    for (int i=0 ; i<fileSendCount ; i++)
-        {
-        delete fileSends[i];
-        }
+    std::map<DOMString, XmppStream *>::iterator iter;
+    for (iter = outputStreams.begin(); iter!=outputStreams.end() ; iter++)
+        delete iter->second;
+    for (iter = inputStreams.begin(); iter!=inputStreams.end() ; iter++)
+        delete iter->second;
+    for (iter = fileSends.begin(); iter!=fileSends.end() ; iter++)
+        delete iter->second;
     groupChatsClear();
 }
 
-//##############################################
+
+
+
+
+
+//########################################################################
 //# UTILILY
-//##############################################
+//########################################################################
 
 /**
  *
@@ -2472,7 +671,7 @@ bool XmppClient::pause(unsigned long millis)
 static int strIndex(const DOMString &str, char *key)
 {
     unsigned int p = str.find(key);
-    if (p == DOMString::npos)
+    if (p == str.npos)
         return -1;
     return p;
 }
@@ -2483,6 +682,8 @@ DOMString XmppClient::toXml(const DOMString &str)
     return Parser::encode(str);
 }
 
+
+
 static DOMString trim(const DOMString &str)
 {
     unsigned int i;
@@ -2499,9 +700,14 @@ static DOMString trim(const DOMString &str)
     return str.substr(start, end);
 }
 
-//##############################################
+
+
+
+
+//########################################################################
 //# VARIABLES  (ones that need special handling)
-//##############################################
+//########################################################################
+
 /**
  *
  */
@@ -2528,15 +734,21 @@ void XmppClient::setUsername(const DOMString &val)
        }
 }
 
-//##############################################
-//# CONNECTION
-//##############################################
 
-//#######################
+
+
+
+
+
+
+//########################################################################
 //# RECEIVING
-//#######################
+//########################################################################
+
+
 DOMString XmppClient::readStanza()
 {
+
     int  openCount    = 0;
     bool inTag        = false;
     bool slashSeen    = false;
@@ -2546,6 +758,9 @@ DOMString XmppClient::readStanza()
     bool textSeen     = false;
     DOMString buf;
 
+
+    time_t timeout = time((time_t *)0) + 180;
+
     while (true)
         {
         int ch = sock->read();
@@ -2557,6 +772,17 @@ DOMString XmppClient::readStanza()
                 //Since we are timed out, let's assume that we
                 //are between chunks of text.  Let's reset all states.
                 //printf("-----#### Timeout\n");
+                time_t currentTime = time((time_t *)0);
+                if (currentTime > timeout)
+                    {
+                    timeout = currentTime + 180;
+                    if (!write("\n"))
+                        {
+                        error("ping send error");
+                        disconnect();
+                        return "";
+                        }
+                    }
                 continue;
                 }
             else
@@ -2662,15 +888,20 @@ static bool isGroupChat(Element *root)
 static bool parseJid(const DOMString &fullJid,
              DOMString &jid, DOMString &resource)
 {
-    int p = strIndex(fullJid, "/");
-    if (p < 0)
+    DOMString str = fullJid;
+    jid.clear();
+    resource.clear();
+    unsigned int p = str.size();
+    unsigned int p2 = str.rfind('/', p);
+    if (p2 != str.npos)
         {
-        jid = fullJid;
-        resource = "";
-        return true;
+        resource = str.substr(p2+1, p-(p2+1));
+        str = str.substr(0, p);
+        p = p2;
         }
-    jid = fullJid.substr(0, p);
-    resource = fullJid.substr(p+1, fullJid.size()-p-1);
+    jid = str.substr(0, p);
+    printf("fullJid:%s jid:%s rsrc:%s\n",
+        fullJid.c_str(), jid.c_str(), resource.c_str());
     return true;
 }
 
@@ -2684,37 +915,13 @@ bool XmppClient::processMessage(Element *root)
     DOMString type    = root->getTagAttribute("message", "type");
 
     //####Check for embedded namespaces here
-    //# IN BAND BYTESTREAMS
-    DOMString ibbNamespace = "http://jabber.org/protocol/ibb";
-    if (root->getTagAttribute("data", "xmlns") == ibbNamespace)
-        {
-        DOMString streamId = root->getTagAttribute("data", "sid");
-        if (streamId.size() > 0)
-            {
-            for (int i=0 ; i<inputStreamCount ; i++)
-                {
-                XmppStream *ins = inputStreams[i];
-                //printf("##ins:%s  streamid:%s\n",
-                //    ins->getStreamId().c_str(),  streamId.c_str());
-                if (ins->getStreamId() == streamId)
-                    {
-                    //# We have a winner
-                    if (ins->getState() != STREAM_OPEN)
-                        {
-                        XmppEvent event(XmppEvent::EVENT_ERROR);
-                        event.setFrom(from);
-                        event.setData("received unrequested stream data");
-                        dispatchXmppEvent(event);
-                        return true;
-                        }
-                    DOMString data = root->getTagValue("data");
-                    std::vector<unsigned char>binData =
-                               Base64Decoder::decode(data);
-                    ins->receiveData(binData);
-                    }
-                }
-            }
-        }
+    //### FILE TRANSFERS
+    if (processFileMessage(root))
+        return true;
+
+    //### STREAMS
+    if (processInBandByteStreamMessage(root))
+        return true;
 
 
     //#### NORMAL MESSAGES
@@ -2722,8 +929,8 @@ bool XmppClient::processMessage(Element *root)
     DOMString body    = root->getTagValue("body");
     DOMString thread  = root->getTagValue("thread");
     //##rfc 3921, para 2.4.  ignore if no recognizable info
-    if (subject.size() < 1 && body.size()<1 && thread.size()<1)
-        return true;
+    //if (subject.size() < 1 && thread.size()<1)
+    //    return true;
 
     if (type == "groupchat")
         {
@@ -2754,11 +961,11 @@ bool XmppClient::processMessage(Element *root)
                 {
                 printf("LOCKED!! ;)\n");
                 char *fmt =
-                "<iq from='%s' id='create%d' to='%s' type='set'>"
+                "<iq id='create%d' to='%s' type='set'>"
                 "<query xmlns='http://jabber.org/protocol/muc#owner'>"
                 "<x xmlns='jabber:x:data' type='submit'/>"
                 "</query></iq>\n";
-                if (!write(fmt, jid.c_str(), msgId++, fromGid.c_str()))
+                if (!write(fmt, msgId++, fromGid.c_str()))
                     return false;
                 }
             }
@@ -2798,7 +1005,7 @@ bool XmppClient::processPresence(Element *root)
         //printf("fromGid:%s  fromNick:%s\n",
         //        fromGid.c_str(), fromNick.c_str());
         DOMString item_jid = root->getTagAttribute("item", "jid");
-        if (item_jid == jid) //Me
+        if (item_jid == jid || item_jid == to) //Me
             {
             if (presence)
                 {
@@ -2884,130 +1091,13 @@ bool XmppClient::processIq(Element *root)
     //printf("###IQ xmlns:%s\n", xmlns.c_str());
 
     //### FILE TRANSFERS
-    DOMString siNamespace = "http://jabber.org/protocol/si";
-    if (root->getTagAttribute("si", "xmlns") == siNamespace)
-        {
-        if (type == "set")
-            {
-            DOMString streamId = root->getTagAttribute("si", "id");
-            DOMString fname    = root->getTagAttribute("file", "name");
-            DOMString sizeStr  = root->getTagAttribute("file", "size");
-            DOMString hash     = root->getTagAttribute("file", "hash");
-            XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_RECEIVE);
-            event.setFrom(from);
-            event.setIqId(id);
-            event.setStreamId(streamId);
-            event.setFileName(fname);
-            event.setFileHash(hash);
-            event.setFileSize(atol(sizeStr.c_str()));
-            dispatchXmppEvent(event);
-            }
-        else  //result
-            {
-            printf("Got result\n");
-            for (int i=0 ; i<fileSendCount ; i++)
-                {
-                XmppStream *outf = fileSends[i];
-                if (outf->getIqId() == id &&
-                    from == outf->getPeerId())
-                    {
-                    if (type == "error")
-                        {
-                        outf->setState(STREAM_ERROR);
-                        error("user '%s' rejected file", from.c_str());
-                        return true;
-                        }
-                    else if (type == "result")
-                        {
-                        if (outf->getState() == STREAM_OPENING)
-                            {
-                            XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_ACCEPTED);
-                            event.setFrom(from);
-                            dispatchXmppEvent(event);
-                            outf->setState(STREAM_OPEN);
-                            }
-                        else if (outf->getState() == STREAM_CLOSING)
-                            {
-                            outf->setState(STREAM_CLOSED);
-                            }
-                        return true;
-                        }
-                    }
-                }
-            }
+    if (processFileMessage(root))
         return true;
-        }
 
-
-    //### IN-BAND BYTESTREAMS
-    //### Incoming stream requests
-    DOMString ibbNamespace = "http://jabber.org/protocol/ibb";
-    if (root->getTagAttribute("open", "xmlns") == ibbNamespace)
-        {
-        DOMString streamId = root->getTagAttribute("open", "sid");
-        XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_INIT);
-        dispatchXmppEvent(event);
-        if (streamId.size()>0)
-            {
-            for (int i=0 ; i<inputStreamCount ; i++)
-                {
-                XmppStream *ins = inputStreams[i];
-                if (ins->getStreamId() == streamId)
-                    {
-                    ins->setState(STREAM_OPENING);
-                    ins->setIqId(id);
-                    return true;
-                    }
-                }
-            }
-        return true;
-        }
-    else if (root->getTagAttribute("close", "xmlns") == ibbNamespace)
-        {
-        XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_CLOSE);
-        dispatchXmppEvent(event);
-        DOMString streamId = root->getTagAttribute("close", "sid");
-        if (streamId.size()>0)
-            {
-            for (int i=0 ; i<inputStreamCount ; i++)
-                {
-                XmppStream *ins = inputStreams[i];
-                if (ins->getStreamId() == streamId &&
-                    from == ins->getPeerId())
-                    {
-                    ins->setState(STREAM_CLOSING);
-                    ins->setIqId(id);
-                    return true;
-                    }
-                }
-            }
+    //### STREAMS
+    if (processInBandByteStreamMessage(root))
         return true;
-        }
-    //### Responses to outgoing requests
-    for (int i=0 ; i<outputStreamCount ; i++)
-        {
-        XmppStream *outs = outputStreams[i];
-        if (outs->getIqId() == id)
-            {
-            if (type == "error")
-                {
-                outs->setState(STREAM_ERROR);
-                return true;
-                }
-            else if (type == "result")
-                {
-                if (outs->getState() == STREAM_OPENING)
-                    {
-                    outs->setState(STREAM_OPEN);
-                    }
-                else if (outs->getState() == STREAM_CLOSING)
-                    {
-                    outs->setState(STREAM_CLOSED);
-                    }
-                return true;
-                }
-            }
-        }
+        
 
     //###Normal Roster stuff
     if (root->getTagAttribute("query", "xmlns") == "jabber:iq:roster")
@@ -3030,11 +1120,93 @@ bool XmppClient::processIq(Element *root)
         dispatchXmppEvent(event);
         }
 
+    else if (id.find("regnew") != id.npos)
+        {
+
+        }
+
+    else if (id.find("regpass") != id.npos)
+        {
+        std::vector<Element *> list = root->findElements("error");
+        if (list.size()==0)
+            {
+            XmppEvent evt(XmppEvent::EVENT_REGISTRATION_CHANGE_PASS);
+            evt.setTo(username);
+            evt.setFrom(host);
+            dispatchXmppEvent(evt);
+            return true;
+            }
+
+        Element *errElem = list[0];
+        DOMString errMsg = "Password change error: ";
+        if (errElem->findElements("bad-request").size()>0)
+            {
+            errMsg.append("password change does not contain complete information");
+            }
+        else if (errElem->findElements("not-authorized").size()>0)
+            {
+            errMsg.append("server does not consider the channel safe "
+                          "enough to enable a password change");
+            }
+        else if (errElem->findElements("not-allowed").size()>0)
+            {
+            errMsg.append("server does not allow password changes");
+            }
+        else if (errElem->findElements("unexpected-request").size()>0)
+            {
+            errMsg.append(
+             "IQ set does not contain a 'from' address because "
+             "the entity is not registered with the server");
+            }
+        error((char *)errMsg.c_str());
+        }
+
+    else if (id.find("regcancel") != id.npos)
+        {
+        std::vector<Element *> list = root->findElements("error");
+        if (list.size()==0)
+            {
+            XmppEvent evt(XmppEvent::EVENT_REGISTRATION_CANCEL);
+            evt.setTo(username);
+            evt.setFrom(host);
+            dispatchXmppEvent(evt);
+            return true;
+            }
+
+        Element *errElem = list[0];
+        DOMString errMsg = "Registration cancel error: ";
+        if (errElem->findElements("bad-request").size()>0)
+            {
+            errMsg.append("The <remove/> element was not the only child element of the <query/> element.");
+            }
+        else if (errElem->findElements("forbidden").size()>0)
+            {
+            errMsg.append("sender does not have sufficient permissions to cancel the registration");
+            }
+        else if (errElem->findElements("not-allowed").size()>0)
+            {
+            errMsg.append("not allowed to cancel registrations in-band");
+            }
+        else if (errElem->findElements("registration-required").size()>0)
+            {
+            errMsg.append("not previously registered");
+            }
+        else if (errElem->findElements("unexpected-request").size()>0)
+            {
+            errMsg.append(
+                 "IQ set does not contain a 'from' address because "
+                 "the entity is not registered with the server");
+            }
+        error((char *)errMsg.c_str());
+        }
+
     return true;
 }
 
 
 
+
+
 bool XmppClient::receiveAndProcess()
 {
     if (!keepGoing)
@@ -3059,87 +1231,243 @@ bool XmppClient::receiveAndProcess()
         return true;
         }
 
-    //#### MESSAGE
-    std::vector<Element *>elems = root->findElements("message");
-    if (elems.size()>0)
+    //#### MESSAGE
+    std::vector<Element *>elems = root->findElements("message");
+    if (elems.size()>0)
+        {
+        if (!processMessage(root))
+            return false;
+        }
+
+    //#### PRESENCE
+    elems = root->findElements("presence");
+    if (elems.size()>0)
+        {
+        if (!processPresence(root))
+            return false;
+        }
+
+    //#### INFO
+    elems = root->findElements("iq");
+    if (elems.size()>0)
+        {
+        if (!processIq(root))
+            return false;
+        }
+
+    delete root;
+
+    return true;
+}
+
+
+bool XmppClient::receiveAndProcessLoop()
+{
+    keepGoing = true;
+    while (true)
+        {
+        if (!keepGoing)
+            {
+            status("Abort requested");
+            break;
+            }
+        if (!receiveAndProcess())
+            return false;
+        }
+    return true;
+}
+
+
+
+
+//########################################################################
+//# SENDING
+//########################################################################
+
+
+bool XmppClient::write(char *fmt, ...)
+{
+    va_list args;
+    va_start(args,fmt);
+    vsnprintf((char *)writeBuf, writeBufLen, fmt,args);
+    va_end(args) ;
+    status("SEND: %s", writeBuf);
+    if (!sock->write((char *)writeBuf))
+        {
+        error("Cannot write to socket");
+        return false;
+        }
+    return true;
+}
+
+
+
+
+
+
+//########################################################################
+//# R E G I S T R A T I O N
+//########################################################################
+
+/**
+ * Perform JEP-077 In-Band Registration.  Performed synchronously after SSL,
+ * before authentication
+ */
+bool XmppClient::inBandRegistrationNew()
+{
+    Parser parser;
+
+    char *fmt =
+     "<iq type='get' id='regnew%d'>"
+         "<query xmlns='jabber:iq:register'/>"
+         "</iq>\n\n";
+    if (!write(fmt, msgId++))
+        return false;
+
+    DOMString recbuf = readStanza();
+    status("RECV reg: %s", recbuf.c_str());
+    Element *elem = parser.parse(recbuf);
+    //elem->print();
+
+    //# does the entity send the newer "instructions" tag?
+    std::vector<Element *> fields = elem->findElements("field");
+    std::vector<DOMString> fnames;
+    for (unsigned int i=0; i<fields.size() ; i++)
         {
-        if (!processMessage(root))
-            return false;
+        DOMString fname = fields[i]->getAttribute("var");
+        if (fname == "FORM_TYPE")
+            continue;
+        fnames.push_back(fname);
+        status("field name:%s", fname.c_str());
         }
 
-    //#### PRESENCE
-    elems = root->findElements("presence");
-    if (elems.size()>0)
+    //Do we have any fields?
+    if (fnames.size() == 0)
         {
-        if (!processPresence(root))
+        //If no fields, maybe the older method was offered
+        if (elem->findElements("username").size() == 0 ||
+            elem->findElements("password").size() == 0)
+            {
+            error("server did not offer registration");
+            delete elem;
             return false;
+            }
         }
 
-    //#### INFO
-    elems = root->findElements("iq");
-    if (elems.size()>0)
-        {
-        if (!processIq(root))
-            return false;
-        }
+    delete elem;
 
-    delete root;
+    fmt =
+     "<iq type='set' id='regnew%d'>"
+         "<query xmlns='jabber:iq:register'>"
+         "<username>%s</username>"
+         "<password>%s</password>"
+         "<email/><name/>"
+         "</query>"
+         "</iq>\n\n";
+    if (!write(fmt, msgId++, toXml(username).c_str(),
+                    toXml(password).c_str() ))
+        return false;
 
-    return true;
-}
 
+    recbuf = readStanza();
+    status("RECV reg: %s", recbuf.c_str());
+    elem = parser.parse(recbuf);
+    //elem->print();
 
-bool XmppClient::receiveAndProcessLoop()
-{
-    keepGoing = true;
-    while (true)
+    std::vector<Element *> list = elem->findElements("error");
+    if (list.size()>0)
         {
-        if (!keepGoing)
+        Element *errElem = list[0];
+        DOMString code = errElem->getAttribute("code");
+        DOMString errMsg = "Registration error: ";
+        if (code == "409")
             {
-            printf("Abort requested\n");
-            break;
+            errMsg.append("conflict with existing user name");
             }
-        if (!receiveAndProcess())
-            return false;
+        else if (code == "406")
+            {
+            errMsg.append("some registration information was not provided");
+            }
+        error((char *)errMsg.c_str());
+        delete elem;
+        return false;
         }
+
+    delete elem;
+
+    XmppEvent evt(XmppEvent::EVENT_REGISTRATION_NEW);
+    evt.setTo(username);
+    evt.setFrom(host);
+    dispatchXmppEvent(evt);
+
     return true;
 }
 
-//#######################
-//# SENDING
-//#######################
 
-bool XmppClient::write(char *fmt, ...)
+/**
+ * Perform JEP-077 In-Band Registration.  Performed asynchronously, after login.
+ * See processIq() for response handling.
+ */
+bool XmppClient::inBandRegistrationChangePassword(const DOMString &newpassword)
 {
-    va_list args;
-    va_start(args,fmt);
-    vsnprintf((char *)writeBuf, writeBufLen, fmt,args);
-    va_end(args) ;
-    status("SEND: %s", writeBuf);
-    if (!sock->write((char *)writeBuf))
-        {
-        error("Cannot write to socket");
+    Parser parser;
+
+    //# Let's try it form-style to allow the common old/new password thing
+    char *fmt =
+      "<iq type='set' id='regpass%d' to='%s'>"
+      "  <query xmlns='jabber:iq:register'>"
+      "    <x xmlns='jabber:x:data' type='form'>"
+      "      <field type='hidden' var='FORM_TYPE'>"
+      "        <value>jabber:iq:register:changepassword</value>"
+      "      </field>"
+      "      <field type='text-single' var='username'>"
+      "        <value>%s</value>"
+      "      </field>"
+      "      <field type='text-private' var='old_password'>"
+      "        <value>%s</value>"
+      "      </field>"
+      "      <field type='text-private' var='password'>"
+      "        <value>%s</value>"
+      "      </field>"
+      "    </x>"
+      "  </query>"
+      "</iq>\n\n";
+
+    if (!write(fmt, msgId++, host.c_str(),
+             username.c_str(), password.c_str(), newpassword.c_str()))
         return false;
-        }
+
     return true;
+
 }
 
-//#######################
-//# CONNECT
-//#######################
 
-bool XmppClient::checkConnect()
+/**
+ * Perform JEP-077 In-Band Registration.  Performed asynchronously, after login.
+ * See processIq() for response handling.
+ */
+bool XmppClient::inBandRegistrationCancel()
 {
-    if (!connected)
-        {
-        XmppEvent evt(XmppEvent::EVENT_ERROR);
-        evt.setData("Attempted operation while disconnected");
-        dispatchXmppEvent(evt);
+    Parser parser;
+
+    char *fmt =
+     "<iq type='set' id='regcancel%d'>"
+          "<query xmlns='jabber:iq:register'><remove/></query>"
+          "</iq>\n\n";  
+    if (!write(fmt, msgId++))
         return false;
-        }
+
     return true;
 }
 
+
+
+
+
+//########################################################################
+//# A U T H E N T I C A T E
+//########################################################################
+
 bool XmppClient::iqAuthenticate(const DOMString &streamId)
 {
     Parser parser;
@@ -3220,8 +1548,11 @@ bool XmppClient::iqAuthenticate(const DOMString &streamId)
 }
 
 
-static bool
-saslParse(const DOMString &s, std::map<DOMString, DOMString> &vals)
+/**
+ * Parse a sasl challenge to retrieve all of its key=value pairs
+ */
+static bool saslParse(const DOMString &s, 
+                      std::map<DOMString, DOMString> &vals)
 {
 
     vals.clear();
@@ -3272,14 +1603,15 @@ saslParse(const DOMString &s, std::map<DOMString, DOMString> &vals)
 
 
 
-
-
+/**
+ * Attempt suthentication using the MD5 SASL mechanism
+ */
 bool XmppClient::saslMd5Authenticate()
 {
     Parser parser;
     char *fmt =
     "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
-    "mechanism='DIGEST-MD5'/>\n";
+        "mechanism='DIGEST-MD5'/>\n";
     if (!write(fmt))
         return false;
 
@@ -3296,7 +1628,7 @@ bool XmppClient::saslMd5Authenticate()
         return false;
         }
     DOMString challenge = Base64Decoder::decodeToString(b64challenge);
-    status("challenge:'%s'", challenge.c_str());
+    status("md5 challenge:'%s'", challenge.c_str());
 
     std::map<DOMString, DOMString> attrs;
     if (!saslParse(challenge, attrs))
@@ -3315,8 +1647,9 @@ bool XmppClient::saslMd5Authenticate()
     DOMString realm = attrs["realm"];
     if (realm.size()==0)
         {
-        error("login: no SASL realm sent by server");
-        return false;
+        //Apparently this is not a problem
+        //error("login: no SASL realm sent by server");
+        //return false;
         }
 
     status("SASL recv nonce: '%s' realm:'%s'\n", nonce.c_str(), realm.c_str());
@@ -3403,7 +1736,7 @@ bool XmppClient::saslMd5Authenticate()
         }
 
     challenge = Base64Decoder::decodeToString(b64challenge);
-    status("challenge: '%s'", challenge.c_str());
+    status("md5 challenge: '%s'", challenge.c_str());
 
     if (!saslParse(challenge, attrs))
         {
@@ -3434,6 +1767,12 @@ bool XmppClient::saslMd5Authenticate()
     return success;
 }
 
+
+
+/**
+ *  Attempt to authentication using the SASL PLAIN mechanism.  This
+ *  is used most commonly my Google Talk.
+ */
 bool XmppClient::saslPlainAuthenticate()
 {
     Parser parser;
@@ -3466,6 +1805,10 @@ bool XmppClient::saslPlainAuthenticate()
 
 
 
+/**
+ * Handshake with SASL, and use one of its offered mechanisms to
+ * authenticate.
+ */
 bool XmppClient::saslAuthenticate()
 {
     Parser parser;
@@ -3486,7 +1829,9 @@ bool XmppClient::saslAuthenticate()
             status("login: STARTTLS available");
         }
 
-    if (wantStartTls && !sock->getEnableSSL())
+    //# do we want TLS, are we not already running SSL, and can
+    //# the client actually do an ssl connection?
+    if (wantStartTls && !sock->getEnableSSL() && sock->getHaveSSL())
         {
         delete elem;
         char *fmt =
@@ -3533,12 +1878,15 @@ bool XmppClient::saslAuthenticate()
         recbuf = readStanza();
         status("RECV: '%s'\n", recbuf.c_str());
         elem = parser.parse(recbuf);
+
+        XmppEvent event(XmppEvent::EVENT_SSL_STARTED);
+        dispatchXmppEvent(event);
         }
 
     //register, if user requests
     if (doRegister)
         {
-        if (!inBandRegistration())
+        if (!inBandRegistrationNew())
             return false;
         }
 
@@ -3592,94 +1940,36 @@ bool XmppClient::saslAuthenticate()
 }
 
 
-/**
- * Perform JEP-077 In-Band Registration
- */
-bool XmppClient::inBandRegistration()
-{
-    Parser parser;
-
-    char *fmt =
-     "<iq type='get' id='reg1'>"
-         "<query xmlns='jabber:iq:register'/>"
-         "</iq>\n\n";
-    if (!write(fmt))
-        return false;
-
-    DOMString recbuf = readStanza();
-    status("RECV reg: %s", recbuf.c_str());
-    Element *elem = parser.parse(recbuf);
-    //elem->print();
-
-    //# does the entity send the "instructions" tag?
-    std::vector<Element *> fields = elem->findElements("field");
-    std::vector<DOMString> fnames;
-    for (unsigned int i=0; i<fields.size() ; i++)
-        {
-        DOMString fname = fields[i]->getAttribute("var");
-        if (fname == "FORM_TYPE")
-            continue;
-        fnames.push_back(fname);
-        status("field name:%s", fname.c_str());
-        }
-
-    delete elem;
 
-    if (fnames.size() == 0)
-        {
-        error("server did not offer registration");
-        return false;
-        }
 
 
-    fmt =
-     "<iq type='set' id='reg2'>"
-         "<query xmlns='jabber:iq:register'>"
-         "<username>%s</username>"
-         "<password>%s</password>"
-         "<email/><name/>"
-         "</query>"
-         "</iq>\n\n";
-    if (!write(fmt, toXml(username).c_str(),
-                    toXml(password).c_str() ))
-        return false;
 
+//########################################################################
+//# CONNECT
+//########################################################################
 
-    recbuf = readStanza();
-    status("RECV reg: %s", recbuf.c_str());
-    elem = parser.parse(recbuf);
-    //elem->print();
 
-    std::vector<Element *> list = elem->findElements("error");
-    if (list.size()>0)
+/**
+ * Check if we are connected, and fail with an error if we are not
+ */
+bool XmppClient::checkConnect()
+{
+    if (!connected)
         {
-        Element *errElem = list[0];
-        DOMString code = errElem->getAttribute("code");
-        DOMString errMsg = "Registration error. ";
-        if (code == "409")
-            {
-            errMsg.append("conflict with existing user name");
-            }
-        if (code == "406")
-            {
-            errMsg.append("some registration information was not provided");
-            }
-        error((char *)errMsg.c_str());
-        delete elem;
+        XmppEvent evt(XmppEvent::EVENT_ERROR);
+        evt.setData("Attempted operation while disconnected");
+        dispatchXmppEvent(evt);
         return false;
         }
-
-    delete elem;
-
-    XmppEvent evt(XmppEvent::EVENT_REGISTRATION_NEW);
-    evt.setTo(username);
-    evt.setFrom(host);
-    dispatchXmppEvent(evt);
-
     return true;
 }
 
 
+
+/**
+ * Create an XMPP session with a server.  This
+ * is basically the transport layer of XMPP.
+ */
 bool XmppClient::createSession()
 {
 
@@ -3691,6 +1981,12 @@ bool XmppClient::createSession()
         return false;
         }
 
+    if (sock->getEnableSSL())
+        {
+        XmppEvent event(XmppEvent::EVENT_SSL_STARTED);
+        dispatchXmppEvent(event);
+        }
+
     char *fmt =
      "<stream:stream "
           "to='%s' "
@@ -3769,6 +2065,7 @@ bool XmppClient::createSession()
     //elem->print();
     DOMString bindType = elem->getTagAttribute("iq", "type");
     //printf("##bindType:%s\n", bindType.c_str());
+    DOMString givenFullJid = elem->getTagValue("jid");
     delete elem;
 
     if (bindType != "result")
@@ -3776,6 +2073,17 @@ bool XmppClient::createSession()
         error("no binding with server failed");
         return false;
         }
+        
+    //The server sent us a JID.  We need to listen.
+    if (givenFullJid.size()>0)
+        {
+        DOMString givenJid, givenResource;
+        parseJid(givenFullJid, givenJid, givenResource);
+        status("given user: %s realm: %s, rsrc: %s",
+           givenJid.c_str(), givenResource.c_str());
+        setResource(givenResource);
+        }
+        
 
     fmt =
     "<iq type='set' id='sess%d'>"
@@ -3855,6 +2163,11 @@ bool XmppClient::createSession()
     return true;
 }
 
+
+
+/**
+ * Public call to connect
+ */
 bool XmppClient::connect()
 {
     if (!createSession())
@@ -3866,6 +2179,9 @@ bool XmppClient::connect()
 }
 
 
+/**
+ * Public call to connect
+ */
 bool XmppClient::connect(DOMString hostArg, int portArg,
                          DOMString usernameArg,
                          DOMString passwordArg,
@@ -3883,17 +2199,22 @@ bool XmppClient::connect(DOMString hostArg, int portArg,
     return ret;
 }
 
+
+
+/**
+ * Public call to disconnect
+ */
 bool XmppClient::disconnect()
 {
     if (connected)
         {
         char *fmt =
-        "<presence from='%s' type='unavailable'/>\n";
-        write(fmt, jid.c_str());
+        "<presence type='unavailable'/>\n";
+        write(fmt);
         }
     keepGoing = false;
     connected = false;
-    Thread::sleep(3000); //allow receiving thread to quit
+    Thread::sleep(2000); //allow receiving thread to quit
     sock->disconnect();
     roster.clear();
     groupChatsClear();
@@ -3903,10 +2224,17 @@ bool XmppClient::disconnect()
     return true;
 }
 
-//#######################
+
+
+
+
+//########################################################################
 //# ROSTER
-//#######################
+//########################################################################
 
+/**
+ *  Add an XMPP id to your roster
+ */
 bool XmppClient::rosterAdd(const DOMString &rosterGroup,
                            const DOMString &otherJid,
                            const DOMString &name)
@@ -3914,11 +2242,11 @@ bool XmppClient::rosterAdd(const DOMString &rosterGroup,
     if (!checkConnect())
         return false;
     char *fmt =
-    "<iq from='%s' type='set' id='roster_%d'>"
+    "<iq type='set' id='roster_%d'>"
     "<query xmlns='jabber:iq:roster'>"
     "<item jid='%s' name='%s'><group>%s</group></item>"
     "</query></iq>\n";
-    if (!write(fmt, jid.c_str(), msgId++, otherJid.c_str(),
+    if (!write(fmt, msgId++, otherJid.c_str(),
          name.c_str(), rosterGroup.c_str()))
         {
         return false;
@@ -3926,16 +2254,21 @@ bool XmppClient::rosterAdd(const DOMString &rosterGroup,
     return true;
 }
 
+
+
+/**
+ *  Delete an XMPP id from your roster.
+ */
 bool XmppClient::rosterDelete(const DOMString &otherJid)
 {
     if (!checkConnect())
         return false;
     char *fmt =
-    "<iq from='%s' type='set' id='roster_%d'>"
+    "<iq type='set' id='roster_%d'>"
     "<query xmlns='jabber:iq:roster'>"
     "<item jid='%s' subscription='remove'><group>%s</group></item>"
     "</query></iq>\n";
-    if (!write(fmt, jid.c_str(), msgId++, otherJid.c_str()))
+    if (!write(fmt, msgId++, otherJid.c_str()))
         {
         return false;
         }
@@ -3943,6 +2276,9 @@ bool XmppClient::rosterDelete(const DOMString &otherJid)
 }
 
 
+/**
+ *  Comparison method for sort() call below
+ */
 static bool xmppRosterCompare(const XmppUser& p1, const XmppUser& p2)
 {
     DOMString s1 = p1.group;
@@ -3965,6 +2301,12 @@ static bool xmppRosterCompare(const XmppUser& p1, const XmppUser& p2)
     return false;
 }
 
+
+
+/**
+ *  Sort and return the roster that has just been reported by
+ *  an XmppEvent::EVENT_ROSTER event.
+ */
 std::vector<XmppUser> XmppClient::getRoster()
 {
     std::vector<XmppUser> ros = roster;
@@ -3972,6 +2314,10 @@ std::vector<XmppUser> XmppClient::getRoster()
     return ros;
 }
 
+
+/**
+ *
+ */
 void XmppClient::rosterShow(const DOMString &jid, const DOMString &show)
 {
     DOMString theShow = show;
@@ -3986,10 +2332,18 @@ void XmppClient::rosterShow(const DOMString &jid, const DOMString &show)
         }
 }
 
-//#######################
+
+
+
+
+
+//########################################################################
 //# CHAT (individual)
-//#######################
+//########################################################################
 
+/**
+ * Send a message to an xmpp jid
+ */
 bool XmppClient::message(const DOMString &user, const DOMString &subj,
                          const DOMString &msg)
 {
@@ -4002,18 +2356,18 @@ bool XmppClient::message(const DOMString &user, const DOMString &subj,
     if (xmlSubj.size() > 0)
         {
         char *fmt =
-        "<message from='%s' to='%s' type='chat'>"
+        "<message to='%s' from='%s' type='chat'>"
         "<subject>%s</subject><body>%s</body></message>\n";
-        if (!write(fmt, jid.c_str(), user.c_str(),
+        if (!write(fmt, user.c_str(), jid.c_str(),
                 xmlSubj.c_str(), xmlMsg.c_str()))
             return false;
         }
     else
         {
         char *fmt =
-        "<message from='%s' to='%s'>"
+        "<message to='%s' from='%s'>"
         "<body>%s</body></message>\n";
-        if (!write(fmt, jid.c_str(), user.c_str(), xmlMsg.c_str()))
+        if (!write(fmt, user.c_str(), jid.c_str(), xmlMsg.c_str()))
             return false;
         }
     return true;
@@ -4021,6 +2375,9 @@ bool XmppClient::message(const DOMString &user, const DOMString &subj,
 
 
 
+/**
+ *
+ */
 bool XmppClient::message(const DOMString &user, const DOMString &msg)
 {
     return message(user, "", msg);
@@ -4028,6 +2385,9 @@ bool XmppClient::message(const DOMString &user, const DOMString &msg)
 
 
 
+/**
+ *
+ */
 bool XmppClient::presence(const DOMString &presence)
 {
     if (!checkConnect())
@@ -4036,16 +2396,24 @@ bool XmppClient::presence(const DOMString &presence)
     DOMString xmlPres = toXml(presence);
 
     char *fmt =
-    "<presence from='%s'><show>%s</show></presence>\n";
-    if (!write(fmt, jid.c_str(), xmlPres.c_str()))
+    "<presence><show>%s</show></presence>\n";
+    if (!write(fmt, xmlPres.c_str()))
         return false;
     return true;
 }
 
-//#######################
+
+
+
+
+
+//########################################################################
 //# GROUP  CHAT
-//#######################
+//########################################################################
 
+/**
+ *
+ */
 bool XmppClient::groupChatCreate(const DOMString &groupJid)
 {
     std::vector<XmppGroupChat *>::iterator iter;
@@ -4053,7 +2421,7 @@ bool XmppClient::groupChatCreate(const DOMString &groupJid)
         {
         if ((*iter)->getGroupJid() == groupJid)
             {
-            error("Group chat '%s' already exists", groupJid.c_str());
+            //error("Group chat '%s' already exists", groupJid.c_str());
             return false;
             }
         }
@@ -4062,6 +2430,8 @@ bool XmppClient::groupChatCreate(const DOMString &groupJid)
     return true;
 }
 
+
+
 /**
  *
  */
@@ -4081,6 +2451,8 @@ void XmppClient::groupChatDelete(const DOMString &groupJid)
         }
 }
 
+
+
 /**
  *
  */
@@ -4093,6 +2465,8 @@ bool XmppClient::groupChatExists(const DOMString &groupJid)
     return false;
 }
 
+
+
 /**
  *
  */
@@ -4105,6 +2479,8 @@ void XmppClient::groupChatsClear()
 }
 
 
+
+
 /**
  *
  */
@@ -4122,6 +2498,8 @@ void XmppClient::groupChatUserAdd(const DOMString &groupJid,
         }
 }
 
+
+
 /**
  *
  */
@@ -4139,6 +2517,9 @@ void XmppClient::groupChatUserShow(const DOMString &groupJid,
         }
 }
 
+
+
+
 /**
  *
  */
@@ -4155,6 +2536,11 @@ void XmppClient::groupChatUserDelete(const DOMString &groupJid,
         }
 }
 
+
+
+/**
+ *  Comparison method for the sort() below
+ */
 static bool xmppUserCompare(const XmppUser& p1, const XmppUser& p2)
 {
     DOMString s1 = p1.nick;
@@ -4170,6 +2556,10 @@ static bool xmppUserCompare(const XmppUser& p1, const XmppUser& p2)
 }
 
 
+
+/**
+ *  Return the user list for the named group
+ */
 std::vector<XmppUser> XmppClient::groupChatGetUserList(
                               const DOMString &groupJid)
 {
@@ -4193,6 +2583,12 @@ std::vector<XmppUser> XmppClient::groupChatGetUserList(
     return dummy;
 }
 
+
+
+
+/**
+ *  Try to join a group
+ */
 bool XmppClient::groupChatJoin(const DOMString &groupJid,
                                const DOMString &nick,
                                const DOMString &pass)
@@ -4213,6 +2609,11 @@ bool XmppClient::groupChatJoin(const DOMString &groupJid,
 }
 
 
+
+
+/**
+ * Leave a group
+ */
 bool XmppClient::groupChatLeave(const DOMString &groupJid,
                                 const DOMString &nick)
 {
@@ -4232,6 +2633,11 @@ bool XmppClient::groupChatLeave(const DOMString &groupJid,
 }
 
 
+
+
+/**
+ *  Send a message to a group
+ */
 bool XmppClient::groupChatMessage(const DOMString &groupJid,
                                   const DOMString &msg)
 {
@@ -4247,9 +2653,22 @@ bool XmppClient::groupChatMessage(const DOMString &groupJid,
     "<body>%s</body></message>\n";
     if (!write(fmt, jid.c_str(), groupJid.c_str(), xmlMsg.c_str()))
         return false;
+    /*
+    char *fmt =
+    "<message to='%s' type='groupchat'>"
+    "<body>%s</body></message>\n";
+    if (!write(fmt, groupJid.c_str(), xmlMsg.c_str()))
+        return false;
+    */
     return true;
 }
 
+
+
+
+/**
+ *  Send a message to an individual in a group
+ */
 bool XmppClient::groupChatPrivateMessage(const DOMString &groupJid,
                                          const DOMString &toNick,
                                          const DOMString &msg)
@@ -4259,15 +2678,29 @@ bool XmppClient::groupChatPrivateMessage(const DOMString &groupJid,
 
     DOMString xmlMsg = toXml(msg);
 
+    /*
     char *fmt =
     "<message from='%s' to='%s/%s' type='chat'>"
     "<body>%s</body></message>\n";
     if (!write(fmt, jid.c_str(), groupJid.c_str(),
                toNick.c_str(), xmlMsg.c_str()))
         return false;
+    */
+    char *fmt =
+    "<message to='%s/%s' type='chat'>"
+    "<body>%s</body></message>\n";
+    if (!write(fmt, groupJid.c_str(),
+               toNick.c_str(), xmlMsg.c_str()))
+        return false;
     return true;
 }
 
+
+
+
+/**
+ *  Change your presence within a group
+ */
 bool XmppClient::groupChatPresence(const DOMString &groupJid,
                                    const DOMString &myNick,
                                    const DOMString &presence)
@@ -4282,43 +2715,138 @@ bool XmppClient::groupChatPresence(const DOMString &groupJid,
     DOMString xmlPresence = toXml(presence);
 
     char *fmt =
-    "<presence from='%s' to='%s/%s' type='unavailable'>"
+    "<presence to='%s/%s' type='%s'>"
     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
-    if (!write(fmt, jid.c_str(), groupJid.c_str(), user.c_str(), xmlPresence.c_str()))
+    if (!write(fmt, groupJid.c_str(),
+               user.c_str(), xmlPresence.c_str()))
         return true;
     return true;
 }
 
 
 
-//#######################
+
+
+//########################################################################
 //# S T R E A M S
-//#######################
+//########################################################################
 
 
-/**
- *
- */
-int XmppClient::outputStreamOpen(const DOMString &destId,
-                                 const DOMString &streamIdArg)
+bool XmppClient::processInBandByteStreamMessage(Element *root)
 {
-    int i;
-    for (i=0; i<outputStreamCount ; i++)
-        if (outputStreams[i]->getState() == STREAM_AVAILABLE)
-            break;
-    if (i>=outputStreamCount)
+    DOMString from  = root->getAttribute("from");
+    DOMString id    = root->getAttribute("id");
+    DOMString type  = root->getAttribute("type");
+
+    //### Incoming stream requests
+    //Input streams are id's by stream id
+    DOMString ibbNamespace = "http://jabber.org/protocol/ibb";
+
+    if (root->getTagAttribute("open", "xmlns") == ibbNamespace)
         {
-        error("No available output streams");
-        return -1;
+        DOMString streamId = root->getTagAttribute("open", "sid");
+        XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_INIT);
+        dispatchXmppEvent(event);
+        std::map<DOMString, XmppStream *>::iterator iter =
+                     inputStreams.find(streamId);
+        if (iter != inputStreams.end())
+            {
+            XmppStream *ins = iter->second;
+            ins->setState(STREAM_OPENING);
+            ins->setMessageId(id);
+            return true;
+            }
+        return true;
         }
-    int streamNr = i;
-    XmppStream *outs = outputStreams[streamNr];
 
-    outs->setState(STREAM_OPENING);
+    else if (root->getTagAttribute("close", "xmlns") == ibbNamespace)
+        {
+        XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_CLOSE);
+        dispatchXmppEvent(event);
+        DOMString streamId = root->getTagAttribute("close", "sid");
+        std::map<DOMString, XmppStream *>::iterator iter =
+                     inputStreams.find(streamId);
+        if (iter != inputStreams.end())
+            {
+            XmppStream *ins = iter->second;
+            if (from == ins->getPeerId())
+                {
+                ins->setState(STREAM_CLOSING);
+                ins->setMessageId(id);
+                return true;
+                }
+            }
+        return true;
+        }
+
+    else if (root->getTagAttribute("data", "xmlns") == ibbNamespace)
+        {
+        DOMString streamId = root->getTagAttribute("data", "sid");
+        std::map<DOMString, XmppStream *>::iterator iter =
+                     inputStreams.find(streamId);
+        if (iter != inputStreams.end())
+            {
+            XmppStream *ins = iter->second;
+            if (ins->getState() != STREAM_OPEN)
+                {
+                XmppEvent event(XmppEvent::EVENT_ERROR);
+                event.setFrom(from);
+                event.setData("received unrequested stream data");
+                dispatchXmppEvent(event);
+                return true;
+                }
+            DOMString data = root->getTagValue("data");
+            std::vector<unsigned char>binData =
+                       Base64Decoder::decode(data);
+            ins->receiveData(binData);
+            }
+        }
+
+    //### Responses to outgoing requests
+    //Output streams are id's by message id
+    std::map<DOMString, XmppStream *>::iterator iter =
+                     outputStreams.find(id);
+    if (iter != outputStreams.end())
+        {
+        XmppStream *outs = iter->second;
+        if (type == "error")
+            {
+            outs->setState(STREAM_ERROR);
+            return true;
+            }
+        else if (type == "result")
+            {
+            if (outs->getState() == STREAM_OPENING)
+                {
+                outs->setState(STREAM_OPEN);
+                }
+            else if (outs->getState() == STREAM_CLOSING)
+                {
+                outs->setState(STREAM_CLOSED);
+                }
+            return true;
+            }
+        }
 
+    return false;
+}
+
+
+/**
+ *
+ */
+bool XmppClient::outputStreamOpen(const DOMString &destId,
+                                 const DOMString &streamIdArg)
+{
     char buf[32];
     snprintf(buf, 31, "inband%d", getMsgId());
-    DOMString iqId = buf;
+    DOMString messageId = buf;
+
+    //Output streams are id's by message id
+    XmppStream *outs = new XmppStream();
+    outputStreams[messageId] = outs;
+
+    outs->setState(STREAM_OPENING);
 
     DOMString streamId = streamIdArg;
     if (streamId.size()<1)
@@ -4326,17 +2854,20 @@ int XmppClient::outputStreamOpen(const DOMString &destId,
         snprintf(buf, 31, "stream%d", getMsgId());
         DOMString streamId = buf;
         }
-    outs->setIqId(iqId);
+    outs->setMessageId(messageId);
     outs->setStreamId(streamId);
     outs->setPeerId(destId);
 
+
     char *fmt =
-    "<iq type='set' from='%s' to='%s' id='%s'>"
+    "<%s type='set' to='%s' id='%s'>"
     "<open sid='%s' block-size='4096'"
-    " xmlns='http://jabber.org/protocol/ibb'/></iq>\n";
-    if (!write(fmt, jid.c_str(),
-              destId.c_str(), iqId.c_str(),
-              streamId.c_str()))
+    " xmlns='http://jabber.org/protocol/ibb'/></%s>\n";
+    if (!write(fmt,
+              streamPacket.c_str(),
+              destId.c_str(), messageId.c_str(),
+              streamId.c_str(),
+              streamPacket.c_str()))
         {
         outs->reset();
         return -1;
@@ -4351,7 +2882,7 @@ int XmppClient::outputStreamOpen(const DOMString &destId,
             {
             printf("ERROR\n");
             outs->reset();
-            return -1;
+            return false;
             }
         Thread::sleep(1000);
         state = outs->getState();
@@ -4363,34 +2894,38 @@ int XmppClient::outputStreamOpen(const DOMString &destId,
         return -1;
         }
 
-    return streamNr;
+    return true;
 }
 
 /**
  *
  */
-int XmppClient::outputStreamWrite(int streamNr,
-                      const unsigned char *buf, unsigned long len)
+bool XmppClient::outputStreamWrite(const DOMString &streamId,
+               const std::vector<unsigned char> &buf)
 {
-    XmppStream *outs = outputStreams[streamNr];
+    std::map<DOMString, XmppStream *>::iterator iter =
+        outputStreams.find(streamId);
+    if (iter == outputStreams.end())
+        return false;
+    XmppStream *outs = iter->second;
 
-    unsigned long outLen = 0;
-    unsigned char *p = (unsigned char *)buf;
+    unsigned int len = buf.size();
+    unsigned int pos = 0;
 
-    while (outLen < len)
+    while (pos < len)
         {
-        unsigned long chunksize = 1024;
-        if (chunksize + outLen > len)
-            chunksize = len - outLen;
+        unsigned int pos2 = pos + 1024;
+        if (pos2>len)
+            pos2 = len;
 
         Base64Encoder encoder;
-        encoder.append(p, chunksize);
+        for (unsigned int i=pos ; i<pos2 ; i++)
+            encoder.append(buf[i]);
         DOMString b64data = encoder.finish();
-        p      += chunksize;
-        outLen += chunksize;
+
 
         char *fmt =
-        "<message from='%s' to='%s' id='msg%d'>"
+        "<message to='%s' id='msg%d'>"
         "<data xmlns='http://jabber.org/protocol/ibb' sid='%s' seq='%d'>"
         "%s"
         "</data>"
@@ -4399,7 +2934,7 @@ int XmppClient::outputStreamWrite(int streamNr,
         "<rule condition='match-resource' value='exact' action='error'/>"
         "</amp>"
         "</message>\n";
-        if (!write(fmt, jid.c_str(),
+        if (!write(fmt,
               outs->getPeerId().c_str(),
               getMsgId(),
               outs->getStreamId().c_str(),
@@ -4407,33 +2942,43 @@ int XmppClient::outputStreamWrite(int streamNr,
               b64data.c_str()))
             {
             outs->reset();
-            return -1;
+            return false;
             }
         pause(5000);
+        
+        pos = pos2;
         }
-    return outLen;
+        
+    return true;
 }
 
 /**
  *
  */
-int XmppClient::outputStreamClose(int streamNr)
+bool XmppClient::outputStreamClose(const DOMString &streamId)
 {
-    XmppStream *outs = outputStreams[streamNr];
+    std::map<DOMString, XmppStream *>::iterator iter =
+        outputStreams.find(streamId);
+    if (iter == outputStreams.end())
+        return false;
+    XmppStream *outs = iter->second;
 
     char buf[32];
     snprintf(buf, 31, "inband%d", getMsgId());
-    DOMString iqId = buf;
-    outs->setIqId(iqId);
+    DOMString messageId = buf;
+    outs->setMessageId(messageId);
 
     outs->setState(STREAM_CLOSING);
     char *fmt =
-    "<iq type='set' from='%s' to='%s' id='%s'>"
-    "<close sid='%s' xmlns='http://jabber.org/protocol/ibb'/></iq>\n";
-    if (!write(fmt, jid.c_str(),
-                    outs->getPeerId().c_str(),
-                    iqId.c_str(),
-                    outs->getStreamId().c_str()))
+    "<%s type='set' to='%s' id='%s'>"
+    "<close sid='%s' xmlns='http://jabber.org/protocol/ibb'/></%s>\n";
+    if (!write(fmt,
+            streamPacket.c_str(),
+            outs->getPeerId().c_str(),
+            messageId.c_str(),
+            outs->getStreamId().c_str(),
+            streamPacket.c_str()
+            ))
         return false;
 
     int state = outs->getState();
@@ -4445,7 +2990,7 @@ int XmppClient::outputStreamClose(int streamNr)
             {
             printf("ERROR\n");
             outs->reset();
-            return -1;
+            return false;
             }
         Thread::sleep(1000);
         state = outs->getState();
@@ -4454,33 +2999,26 @@ int XmppClient::outputStreamClose(int streamNr)
         {
         printf("TIMEOUT ERROR\n");
         outs->reset();
-        return -1;
+        return false;
         }
 
-    outs->reset();
-    return 1;
+    delete outs;
+    outputStreams.erase(streamId);
+
+    return true;
 }
 
 
 /**
  *
  */
-int XmppClient::inputStreamOpen(const DOMString &fromJid, const DOMString &streamId,
+bool XmppClient::inputStreamOpen(const DOMString &fromJid,
+                                const DOMString &streamId,
                                 const DOMString &iqId)
 {
-    int i;
-    for (i=0 ; i<inputStreamCount ; i++)
-        {
-        if (inputStreams[i]->getState() == STREAM_AVAILABLE)
-            break;
-        }
-    if (i>=inputStreamCount)
-        {
-        error("No available input streams");
-        return -1;
-        }
-    int streamNr = i;
-    XmppStream *ins = inputStreams[streamNr];
+    XmppStream *ins = new XmppStream();
+
+    inputStreams[streamId] = ins;
     ins->reset();
     ins->setPeerId(fromJid);
     ins->setState(STREAM_CLOSED);
@@ -4495,7 +3033,7 @@ int XmppClient::inputStreamOpen(const DOMString &fromJid, const DOMString &strea
             {
             printf("ERROR\n");
             ins->reset();
-            return -1;
+            return false;
             }
         Thread::sleep(1000);
         state = ins->getState();
@@ -4504,17 +3042,18 @@ int XmppClient::inputStreamOpen(const DOMString &fromJid, const DOMString &strea
         {
         printf("TIMEOUT ERROR\n");
         ins->reset();
-        return -1;
+        return false;
         }
     char *fmt =
-    "<iq type='result' from='%s' to='%s' id='%s'/>\n";
-    if (!write(fmt, jid.c_str(),  fromJid.c_str(), ins->getIqId().c_str()))
+    "<%s type='result' to='%s' id='%s'/>\n";
+    if (!write(fmt, streamPacket.c_str(),
+           fromJid.c_str(), ins->getMessageId().c_str()))
         {
-        return -1;
+        return false;
         }
 
     ins->setState(STREAM_OPEN);
-    return streamNr;
+    return true;
 }
 
 
@@ -4522,60 +3061,112 @@ int XmppClient::inputStreamOpen(const DOMString &fromJid, const DOMString &strea
 /**
  *
  */
-int XmppClient::inputStreamAvailable(int streamNr)
-{
-    XmppStream *ins = inputStreams[streamNr];
-    return ins->available();
-}
-
-/**
- *
- */
-std::vector<unsigned char> XmppClient::inputStreamRead(int streamNr)
+bool XmppClient::inputStreamClose(const DOMString &streamId)
 {
-    XmppStream *ins = inputStreams[streamNr];
-    return ins->read();
-}
+    std::map<DOMString, XmppStream *>::iterator iter =
+        inputStreams.find(streamId);
+    if (iter == inputStreams.end())
+        return false;
+    XmppStream *ins = iter->second;
 
-/**
- *
- */
-bool XmppClient::inputStreamClosing(int streamNr)
-{
-    XmppStream *ins = inputStreams[streamNr];
     if (ins->getState() == STREAM_CLOSING)
-        return true;
-    return false;
+        {
+        char *fmt =
+        "<iq type='result' to='%s' id='%s'/>\n";
+        if (!write(fmt, ins->getPeerId().c_str(),
+                    ins->getMessageId().c_str()))
+            {
+            return false;
+            }
+        }
+    inputStreams.erase(streamId);
+    delete ins;
+
+    return true;
 }
 
 
-/**
- *
- */
-int XmppClient::inputStreamClose(int streamNr)
+
+
+
+
+//########################################################################
+//# FILE   TRANSFERS
+//########################################################################
+
+
+bool XmppClient::processFileMessage(Element *root)
 {
-    int ret=1;
-    XmppStream *ins = inputStreams[streamNr];
-    if (ins->getState() == STREAM_CLOSING)
+    DOMString siNamespace = "http://jabber.org/protocol/si";
+    if (root->getTagAttribute("si", "xmlns") != siNamespace)
+        return false;
+
+    
+    Element *mainElement = root->getFirstChild();
+    if (!mainElement)
+        return false;
+
+    DOMString from  = mainElement->getAttribute("from");
+    DOMString id    = mainElement->getAttribute("id");
+    DOMString type  = mainElement->getAttribute("type");
+
+    status("received file message from %s", from.c_str());
+
+    if (type == "set")
         {
-        char *fmt =
-        "<iq type='result' from='%s' to='%s' id='%s'/>\n";
-        if (!write(fmt, jid.c_str(),  ins->getPeerId().c_str(),
-                    ins->getIqId().c_str()))
+        DOMString streamId = root->getTagAttribute("si", "id");
+        DOMString fname    = root->getTagAttribute("file", "name");
+        DOMString sizeStr  = root->getTagAttribute("file", "size");
+        DOMString hash     = root->getTagAttribute("file", "hash");
+        XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_RECEIVE);
+        event.setFrom(from);
+        event.setIqId(id);
+        event.setStreamId(streamId);
+        event.setFileName(fname);
+        event.setFileHash(hash);
+        event.setFileSize(atol(sizeStr.c_str()));
+        dispatchXmppEvent(event);
+        return true;
+        }
+
+    //##expecting result or error
+    //file sends id'd by message id's
+    std::map<DOMString, XmppStream *>::iterator iter =
+        fileSends.find(id);
+    if (iter != fileSends.end())
+        {
+        XmppStream *outf = iter->second;
+        if (from != outf->getPeerId())
+            return true;
+        if (type == "error")
             {
-            ret = -1;
+            outf->setState(STREAM_ERROR);
+            error("user '%s' rejected file", from.c_str());
+            return true;
+            }
+        else if (type == "result")
+            {
+            if (outf->getState() == STREAM_OPENING)
+                {
+                XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_ACCEPTED);
+                event.setFrom(from);
+                dispatchXmppEvent(event);
+                outf->setState(STREAM_OPEN);
+                }
+            else if (outf->getState() == STREAM_CLOSING)
+                {
+                outf->setState(STREAM_CLOSED);
+                }
+            return true;
             }
         }
-    ins->reset();
-    return ret;
+
+    return true;
 }
 
 
 
 
-//#######################
-//# FILE   TRANSFERS
-//#######################
 
 
 /**
@@ -4591,20 +3182,6 @@ bool XmppClient::fileSend(const DOMString &destJidArg,
     DOMString fileName    = fileNameArg;
     DOMString description = descriptionArg;
 
-    int i;
-    for (i=0; i<fileSendCount ; i++)
-        if (fileSends[i]->getState() == STREAM_AVAILABLE)
-            break;
-    if (i>=fileSendCount)
-        {
-        error("No available file send streams");
-        return false;
-        }
-    int fileSendNr = i;
-    XmppStream *outf = fileSends[fileSendNr];
-
-    outf->setState(STREAM_OPENING);
-
     struct stat finfo;
     if (stat(fileName.c_str(), &finfo)<0)
         {
@@ -4628,18 +3205,21 @@ bool XmppClient::fileSend(const DOMString &destJidArg,
         error("cannot open '%s' for sending", fileName.c_str());
         return false;
         }
-    unsigned char *sendBuf = (unsigned char *)malloc(fileLen+1);
-    if (!sendBuf)
-        {
-        error("cannot cannot allocate send buffer for %s", fileName.c_str());
-        return false;
-        }
+    std::vector<unsigned char> sendBuf;
+    Md5 md5hash;
     for (long i=0 ; i<fileLen && !feof(f); i++)
         {
-        sendBuf[i] = fgetc(f);
+        int ch = fgetc(f);
+        if (ch<0)
+            break;
+        md5hash.append((unsigned char)ch);
+        sendBuf.push_back((unsigned char)ch);
         }
     fclose(f);
+    DOMString hash = md5hash.finishHex();
+    printf("Hash:%s\n", hash.c_str());
 
+   
     //## get the last path segment from the whole path
     if (offeredName.size()<1)
         {
@@ -4660,16 +3240,18 @@ bool XmppClient::fileSend(const DOMString &destJidArg,
 
     char buf[32];
     snprintf(buf, 31, "file%d", getMsgId());
-    DOMString iqId = buf;
-    outf->setIqId(iqId);
+    DOMString messageId = buf;
+
+    XmppStream *outf = new XmppStream();
+
+    outf->setState(STREAM_OPENING);
+    outf->setMessageId(messageId);
+    fileSends[messageId] = outf;
 
     snprintf(buf, 31, "stream%d", getMsgId());
     DOMString streamId = buf;
     //outf->setStreamId(streamId);
 
-    DOMString hash = Md5::hashHex(sendBuf, fileLen);
-    printf("Hash:%s\n", hash.c_str());
-
     outf->setPeerId(destJid);
 
     char dtgBuf[81];
@@ -4677,7 +3259,7 @@ bool XmppClient::fileSend(const DOMString &destJidArg,
     strftime(dtgBuf, 80, "%Y-%m-%dT%H:%M:%Sz", timeVal);
 
     char *fmt =
-    "<iq type='set' id='%s' to='%s'>"
+    "<%s type='set' id='%s' to='%s'>"
     "<si xmlns='http://jabber.org/protocol/si' id='%s'"
       " mime-type='text/plain'"
       " profile='http://jabber.org/protocol/si/profile/file-transfer'>"
@@ -4688,15 +3270,17 @@ bool XmppClient::fileSend(const DOMString &destJidArg,
     "<field var='stream-method' type='list-single'>"
     //"<option><value>http://jabber.org/protocol/bytestreams</value></option>"
     "<option><value>http://jabber.org/protocol/ibb</value></option>"
-    "</field></x></feature></si></iq>\n";
-    if (!write(fmt, iqId.c_str(), destJid.c_str(),
+    "</field></x></feature></si></%s>\n";
+    if (!write(fmt, streamPacket.c_str(),
+         messageId.c_str(), destJid.c_str(),
          streamId.c_str(), offeredName.c_str(), fileLen,
-         hash.c_str(), dtgBuf, description.c_str()))
+         hash.c_str(), dtgBuf, description.c_str(),
+         streamPacket.c_str()))
         {
-        free(sendBuf);
         return false;
         }
 
+    int ret = true;
     int state = outf->getState();
     for (int tim=0 ; tim<20 ; tim++)
         {
@@ -4710,7 +3294,7 @@ bool XmppClient::fileSend(const DOMString &destJidArg,
             {
             printf("ERROR\n");
             outf->reset();
-            return false;
+            ret = false;
             }
         Thread::sleep(1000);
         state = outf->getState();
@@ -4718,30 +3302,27 @@ bool XmppClient::fileSend(const DOMString &destJidArg,
     if (state != STREAM_OPEN)
         {
         printf("TIMEOUT ERROR\n");
-        outf->reset();
-        return false;
+        ret = false;
         }
 
-    //free up this reqource
-    outf->reset();
+    //free up this resource
+    fileSends.erase(messageId);
+    delete outf;
 
-    int  streamNr = outputStreamOpen(destJid, streamId);
-    if (streamNr<0)
+    if (!outputStreamOpen(destJid, streamId))
         {
         error("cannot open output stream %s", streamId.c_str());
-        outf->reset();
         return false;
         }
 
-    int ret = outputStreamWrite(streamNr, sendBuf, fileLen);
-
-    if (ret<0)
+    if (!outputStreamWrite(streamId, sendBuf))
         {
         }
 
-    outputStreamClose(streamNr);
+    if (!outputStreamClose(streamId))
+        {
+        }
 
-    free(sendBuf);
     return true;
 }
 
@@ -4805,25 +3386,27 @@ bool XmppClient::fileReceive(const DOMString &fromJid,
                              const DOMString &fileHash)
 {
     char *fmt =
-    "<iq type='result' to='%s' id='%s'>"
+    "<%s type='result' to='%s' id='%s'>"
     "<si xmlns='http://jabber.org/protocol/si'>"
     "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'/>"
     "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
     "<x xmlns='jabber:x:data' type='submit'>"
     "<field var='stream-method'>"
     "<value>http://jabber.org/protocol/ibb</value>"
-    "</field></x></feature></si></iq>\n";
-    if (!write(fmt, fromJid.c_str(), iqId.c_str()))
+    "</field></x></feature></si></%s>\n";
+    if (!write(fmt, streamPacket.c_str(),
+                    fromJid.c_str(), iqId.c_str(),
+                    streamPacket.c_str()))
         {
         return false;
         }
 
-    int streamNr = inputStreamOpen(fromJid, streamId, iqId);
-    if (streamNr < 0)
+    if (!inputStreamOpen(fromJid, streamId, iqId))
         {
         return false;
         }
 
+    XmppStream *ins = inputStreams[streamId];
 
     Md5 md5;
     FILE *f = fopen(fileName.c_str(), "wb");
@@ -4834,14 +3417,14 @@ bool XmppClient::fileReceive(const DOMString &fromJid,
 
     while (true)
         {
-        if (inputStreamAvailable(streamNr)<1)
+        if (ins->available()<1)
             {
-            if (inputStreamClosing(streamNr))
+            if (ins->getState() == STREAM_CLOSING)
                 break;
             pause(100);
             continue;
             }
-        std::vector<unsigned char> ret = inputStreamRead(streamNr);
+        std::vector<unsigned char> ret = ins->read();
         std::vector<unsigned char>::iterator iter;
         for (iter=ret.begin() ; iter!=ret.end() ; iter++)
             {
@@ -4851,7 +3434,7 @@ bool XmppClient::fileReceive(const DOMString &fromJid,
             }
         }
 
-    inputStreamClose(streamNr);
+    inputStreamClose(streamId);
     fclose(f);
 
     DOMString hash = md5.finishHex();
@@ -5006,6 +3589,9 @@ std::vector<XmppUser> XmppGroupChat::getUserList() const
 
 
 
+
+
+
 } //namespace Pedro
 //########################################################################
 //# E N D    O F     F I L E