index e64a568f059e76786efecbd3e4d14460cd66e524..4082881875bbf237dafebcf3430647dd3477e419 100644 (file)
--- a/src/pedro/pedroxmpp.cpp
+++ b/src/pedro/pedroxmpp.cpp
* 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)
userList = val;
}
+
+
+
+
+
+
+
+
+//########################################################################
//########################################################################
//# X M P P E V E N T T A R G E T
//########################################################################
+//########################################################################
+
//###########################
//# CONSTRUCTORS
//# 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;
}
+
+
+
+//########################################################################
//########################################################################
//# X M P P S T R E A M
//########################################################################
+//########################################################################
+
/**
*
/**
*
*/
- 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;
};
-/**
- *
- */
-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:
};
-//###########################
+
+
+
+//########################################################################
//# CONSTRUCTORS
-//###########################
+//########################################################################
XmppClient::XmppClient()
{
connected = other.connected;
doRegister = other.doRegister;
groupChats = other.groupChats;
+ streamPacket = other.streamPacket;
}
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
-//##############################################
+//########################################################################
/**
*
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;
}
return Parser::encode(str);
}
+
+
static DOMString trim(const DOMString &str)
{
unsigned int i;
return str.substr(start, end);
}
-//##############################################
+
+
+
+
+//########################################################################
//# VARIABLES (ones that need special handling)
-//##############################################
+//########################################################################
+
/**
*
*/
}
}
-//##############################################
-//# CONNECTION
-//##############################################
-//#######################
+
+
+
+
+
+
+//########################################################################
//# RECEIVING
-//#######################
+//########################################################################
+
+
DOMString XmppClient::readStanza()
{
+
int openCount = 0;
bool inTag = false;
bool slashSeen = false;
bool textSeen = false;
DOMString buf;
+
+ time_t timeout = time((time_t *)0) + 180;
+
while (true)
{
int ch = sock->read();
//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
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;
}
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
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")
{
{
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;
}
}
//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)
{
//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")
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)
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;
}
-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();
-
-
+/**
+ * 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;
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))
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());
}
challenge = Base64Decoder::decodeToString(b64challenge);
- status("challenge: '%s'", challenge.c_str());
+ status("md5 challenge: '%s'", challenge.c_str());
if (!saslParse(challenge, attrs))
{
return success;
}
+
+
+/**
+ * Attempt to authentication using the SASL PLAIN mechanism. This
+ * is used most commonly my Google Talk.
+ */
bool XmppClient::saslPlainAuthenticate()
{
Parser parser;
+/**
+ * Handshake with SASL, and use one of its offered mechanisms to
+ * authenticate.
+ */
bool XmppClient::saslAuthenticate()
{
Parser parser;
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 =
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;
}
}
-/**
- * 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()
{
return false;
}
+ if (sock->getEnableSSL())
+ {
+ XmppEvent event(XmppEvent::EVENT_SSL_STARTED);
+ dispatchXmppEvent(event);
+ }
+
char *fmt =
"<stream:stream "
"to='%s' "
//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")
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'>"
return true;
}
+
+
+/**
+ * Public call to connect
+ */
bool XmppClient::connect()
{
if (!createSession())
}
+/**
+ * Public call to connect
+ */
bool XmppClient::connect(DOMString hostArg, int portArg,
DOMString usernameArg,
DOMString passwordArg,
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();
return true;
}
-//#######################
+
+
+
+
+//########################################################################
//# ROSTER
-//#######################
+//########################################################################
+/**
+ * Add an XMPP id to your roster
+ */
bool XmppClient::rosterAdd(const DOMString &rosterGroup,
const DOMString &otherJid,
const DOMString &name)
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;
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;
}
}
+/**
+ * Comparison method for sort() call below
+ */
static bool xmppRosterCompare(const XmppUser& p1, const XmppUser& p2)
{
DOMString s1 = p1.group;
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;
return ros;
}
+
+/**
+ *
+ */
void XmppClient::rosterShow(const DOMString &jid, const DOMString &show)
{
DOMString theShow = show;
}
}
-//#######################
+
+
+
+
+
+//########################################################################
//# CHAT (individual)
-//#######################
+//########################################################################
+/**
+ * Send a message to an xmpp jid
+ */
bool XmppClient::message(const DOMString &user, const DOMString &subj,
const DOMString &msg)
{
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;
+/**
+ *
+ */
bool XmppClient::message(const DOMString &user, const DOMString &msg)
{
return message(user, "", msg);
+/**
+ *
+ */
bool XmppClient::presence(const DOMString &presence)
{
if (!checkConnect())
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;
{
if ((*iter)->getGroupJid() == groupJid)
{
- error("Group chat '%s' already exists", groupJid.c_str());
+ //error("Group chat '%s' already exists", groupJid.c_str());
return false;
}
}
return true;
}
+
+
/**
*
*/
}
}
+
+
/**
*
*/
return false;
}
+
+
/**
*
*/
}
+
+
/**
*
*/
}
}
+
+
/**
*
*/
}
}
+
+
+
/**
*
*/
}
}
+
+
+/**
+ * Comparison method for the sort() below
+ */
static bool xmppUserCompare(const XmppUser& p1, const XmppUser& p2)
{
DOMString s1 = p1.nick;
}
+
+/**
+ * Return the user list for the named group
+ */
std::vector<XmppUser> XmppClient::groupChatGetUserList(
const DOMString &groupJid)
{
return dummy;
}
+
+
+
+/**
+ * Try to join a group
+ */
bool XmppClient::groupChatJoin(const DOMString &groupJid,
const DOMString &nick,
const DOMString &pass)
}
+
+
+/**
+ * Leave a group
+ */
bool XmppClient::groupChatLeave(const DOMString &groupJid,
const DOMString &nick)
{
}
+
+
+/**
+ * Send a message to a group
+ */
bool XmppClient::groupChatMessage(const DOMString &groupJid,
const DOMString &msg)
{
"<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)
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)
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)
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;
{
printf("ERROR\n");
outs->reset();
- return -1;
+ return false;
}
Thread::sleep(1000);
state = outs->getState();
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>"
"<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(),
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();
{
printf("ERROR\n");
outs->reset();
- return -1;
+ return false;
}
Thread::sleep(1000);
state = outs->getState();
{
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
-//#######################
/**
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)
{
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)
{
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];
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'>"
"<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++)
{
{
printf("ERROR\n");
outf->reset();
- return false;
+ ret = false;
}
Thread::sleep(1000);
state = outf->getState();
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;
}
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");
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++)
{
}
}
- inputStreamClose(streamNr);
+ inputStreamClose(streamId);
fclose(f);
DOMString hash = md5.finishHex();
+
+
+
} //namespace Pedro
//########################################################################
//# E N D O F F I L E