Code

Greatly improve SASL challenge parsing
[inkscape.git] / src / pedro / pedroxmpp.cpp
1 /*
2  * Implementation the Pedro mini-XMPP client
3  *
4  * Authors:
5  *   Bob Jamison
6  *
7  * Copyright (C) 2005 Bob Jamison
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Lesser General Public
11  *  License as published by the Free Software Foundation; either
12  *  version 2.1 of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public
20  *  License along with this library; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  */
25 #include <stdio.h>
26 #include <stdarg.h>
28 #include <sys/stat.h>
30 #include "pedroxmpp.h"
31 #include "pedrodom.h"
33 #include <map>
35 #ifdef __WIN32__
37 #include <windows.h>
39 #else /* UNIX */
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <netinet/in.h>
44 #include <netdb.h>
45 #include <unistd.h>
46 #include <sys/ioctl.h>
48 #include <pthread.h>
50 #endif
52 #ifdef HAVE_SSL
53 #include <openssl/ssl.h>
54 #include <openssl/err.h>
55 #endif
58 namespace Pedro
59 {
61 //########################################################################
62 //########################################################################
63 //### U T I L I T Y
64 //########################################################################
65 //########################################################################
68 //########################################################################
69 //# B A S E    6 4
70 //########################################################################
72 //#################
73 //# ENCODER
74 //#################
77 static char *base64encode =
78     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
80 /**
81  * This class is for Base-64 encoding
82  */
83 class Base64Encoder
84 {
86 public:
88     Base64Encoder()
89         {
90         reset();
91         }
93     virtual ~Base64Encoder()
94         {}
96     virtual void reset()
97         {
98         outBuf   = 0L;
99         bitCount = 0;
100         buf = "";
101         }
103     virtual void append(int ch);
105     virtual void append(char *str);
107     virtual void append(unsigned char *str, int len);
109     virtual void append(const DOMString &str);
111     virtual DOMString finish();
113     static DOMString encode(const DOMString &str);
116 private:
119     unsigned long outBuf;
121     int bitCount;
123     DOMString buf;
125 };
129 /**
130  * Writes the specified byte to the output buffer
131  */
132 void Base64Encoder::append(int ch)
134     outBuf   <<=  8;
135     outBuf   |=  (ch & 0xff);
136     bitCount +=  8;
137     if (bitCount >= 24)
138         {
139         int indx  = (int)((outBuf & 0x00fc0000L) >> 18);
140         int obyte = (int)base64encode[indx & 63];
141         buf.push_back(obyte);
143         indx      = (int)((outBuf & 0x0003f000L) >> 12);
144         obyte     = (int)base64encode[indx & 63];
145         buf.push_back(obyte);
147         indx      = (int)((outBuf & 0x00000fc0L) >>  6);
148         obyte     = (int)base64encode[indx & 63];
149         buf.push_back(obyte);
151         indx      = (int)((outBuf & 0x0000003fL)      );
152         obyte     = (int)base64encode[indx & 63];
153         buf.push_back(obyte);
155         bitCount = 0;
156         outBuf   = 0L;
157         }
160 /**
161  * Writes the specified string to the output buffer
162  */
163 void Base64Encoder::append(char *str)
165     while (*str)
166         append((int)*str++);
169 /**
170  * Writes the specified string to the output buffer
171  */
172 void Base64Encoder::append(unsigned char *str, int len)
174     while (len>0)
175         {
176         append((int)*str++);
177         len--;
178         }
181 /**
182  * Writes the specified string to the output buffer
183  */
184 void Base64Encoder::append(const DOMString &str)
186     append((char *)str.c_str());
189 /**
190  * Closes this output stream and releases any system resources
191  * associated with this stream.
192  */
193 DOMString Base64Encoder::finish()
195     //get any last bytes (1 or 2) out of the buffer
196     if (bitCount == 16)
197         {
198         outBuf <<= 2;  //pad to make 18 bits
200         int indx  = (int)((outBuf & 0x0003f000L) >> 12);
201         int obyte = (int)base64encode[indx & 63];
202         buf.push_back(obyte);
204         indx      = (int)((outBuf & 0x00000fc0L) >>  6);
205         obyte     = (int)base64encode[indx & 63];
206         buf.push_back(obyte);
208         indx      = (int)((outBuf & 0x0000003fL)      );
209         obyte     = (int)base64encode[indx & 63];
210         buf.push_back(obyte);
212         buf.push_back('=');
213         }
214     else if (bitCount == 8)
215         {
216         outBuf <<= 4; //pad to make 12 bits
218         int indx  = (int)((outBuf & 0x00000fc0L) >>  6);
219         int obyte = (int)base64encode[indx & 63];
220         buf.push_back(obyte);
222         indx      = (int)((outBuf & 0x0000003fL)      );
223         obyte     = (int)base64encode[indx & 63];
224         buf.push_back(obyte);
226         buf.push_back('=');
227         buf.push_back('=');
228         }
230     DOMString ret = buf;
231     reset();
232     return ret;
236 DOMString Base64Encoder::encode(const DOMString &str)
238     Base64Encoder encoder;
239     encoder.append(str);
240     DOMString ret = encoder.finish();
241     return ret;
246 //#################
247 //# DECODER
248 //#################
250 static int base64decode[] =
252 /*00*/    -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
253 /*08*/    -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
254 /*10*/    -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
255 /*18*/    -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
256 /*20*/    -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
257 /*28*/    -1,   -1,   -1,   62,   -1,   -1,   -1,   63,
258 /*30*/    52,   53,   54,   55,   56,   57,   58,   59,
259 /*38*/    60,   61,   -1,   -1,   -1,   -1,   -1,   -1,
260 /*40*/    -1,    0,    1,    2,    3,    4,    5,    6,
261 /*48*/     7,    8,    9,   10,   11,   12,   13,   14,
262 /*50*/    15,   16,   17,   18,   19,   20,   21,   22,
263 /*58*/    23,   24,   25,   -1,   -1,   -1,   -1,   -1,
264 /*60*/    -1,   26,   27,   28,   29,   30,   31,   32,
265 /*68*/    33,   34,   35,   36,   37,   38,   39,   40,
266 /*70*/    41,   42,   43,   44,   45,   46,   47,   48,
267 /*78*/    49,   50,   51,   -1,   -1,   -1,   -1,   -1
268 };
270 class Base64Decoder
272 public:
273     Base64Decoder()
274         {
275         reset();
276         }
278     virtual ~Base64Decoder()
279         {}
281     virtual void reset()
282         {
283         inCount = 0;
284         buf.clear();
285         }
288     virtual void append(int ch);
290     virtual void append(char *str);
292     virtual void append(const DOMString &str);
294     std::vector<unsigned char> finish();
296     static std::vector<unsigned char> decode(const DOMString &str);
298     static DOMString decodeToString(const DOMString &str);
300 private:
302     int inBytes[4];
303     int inCount;
304     std::vector<unsigned char> buf;
305 };
307 /**
308  * Appends one char to the decoder
309  */
310 void Base64Decoder::append(int ch)
312     if (isspace(ch))
313         return;
314     else if (ch == '=') //padding
315         {
316         inBytes[inCount++] = 0;
317         }
318     else
319         {
320         int byteVal = base64decode[ch & 0x7f];
321         //printf("char:%c %d\n", ch, byteVal);
322         if (byteVal < 0)
323             {
324             //Bad lookup value
325             }
326         inBytes[inCount++] = byteVal;
327         }
329     if (inCount >=4 )
330         {
331         unsigned char b0 = ((inBytes[0]<<2) & 0xfc) | ((inBytes[1]>>4) & 0x03);
332         unsigned char b1 = ((inBytes[1]<<4) & 0xf0) | ((inBytes[2]>>2) & 0x0f);
333         unsigned char b2 = ((inBytes[2]<<6) & 0xc0) | ((inBytes[3]   ) & 0x3f);
334         buf.push_back(b0);
335         buf.push_back(b1);
336         buf.push_back(b2);
337         inCount = 0;
338         }
342 void Base64Decoder::append(char *str)
344     while (*str)
345         append((int)*str++);
348 void Base64Decoder::append(const DOMString &str)
350     append((char *)str.c_str());
353 std::vector<unsigned char> Base64Decoder::finish()
355     std::vector<unsigned char> ret = buf;
356     reset();
357     return ret;
360 std::vector<unsigned char> Base64Decoder::decode(const DOMString &str)
362     Base64Decoder decoder;
363     decoder.append(str);
364     std::vector<unsigned char> ret = decoder.finish();
365     return ret;
368 DOMString Base64Decoder::decodeToString(const DOMString &str)
370     Base64Decoder decoder;
371     decoder.append(str);
372     std::vector<unsigned char> ret = decoder.finish();
373     DOMString buf;
374     for (unsigned int i=0 ; i<ret.size() ; i++)
375         buf.push_back(ret[i]);
376     return buf;
381 //########################################################################
382 //# S H A   1
383 //########################################################################
385 class Sha1
387 public:
389     /**
390      *
391      */
392     Sha1()
393         { init(); }
395     /**
396      *
397      */
398     virtual ~Sha1()
399         {}
402     /**
403      * Static convenience method.  This would be the most commonly used
404      * version;
405      * @parm digest points to a bufer of 20 unsigned chars
406      */
407     static void hash(unsigned char *dataIn, int len, unsigned char *digest);
409     /**
410      * Static convenience method.  This will fill a string with the hex
411      * codex string.
412      */
413     static DOMString hashHex(unsigned char *dataIn, int len);
415     /**
416      *  Initialize the context (also zeroizes contents)
417      */
418     virtual void init();
420     /**
421      *
422      */
423     virtual void append(unsigned char *dataIn, int len);
425     /**
426      *
427      * @parm digest points to a bufer of 20 unsigned chars
428      */
429     virtual void finish(unsigned char *digest);
432 private:
434     void hashblock();
436     unsigned long H[5];
437     unsigned long W[80];
438     unsigned long sizeHi,sizeLo;
439     int lenW;
441 };
445 void Sha1::hash(unsigned char *dataIn, int len, unsigned char *digest)
447     Sha1 sha1;
448     sha1.append(dataIn, len);
449     sha1.finish(digest);
452 static char *sha1hex = "0123456789abcdef";
454 DOMString Sha1::hashHex(unsigned char *dataIn, int len)
456     unsigned char hashout[20];
457     hash(dataIn, len, hashout);
458     DOMString ret;
459     for (int i=0 ; i<20 ; i++)
460         {
461         unsigned char ch = hashout[i];
462         ret.push_back(sha1hex[ (ch>>4) & 15 ]);
463         ret.push_back(sha1hex[ ch      & 15 ]);
464         }
465     return ret;
469 void Sha1::init()
472     lenW   = 0;
473     sizeHi = 0;
474     sizeLo = 0;
476     // Initialize H with the magic constants (see FIPS180 for constants)
477     H[0] = 0x67452301L;
478     H[1] = 0xefcdab89L;
479     H[2] = 0x98badcfeL;
480     H[3] = 0x10325476L;
481     H[4] = 0xc3d2e1f0L;
483     for (int i = 0; i < 80; i++)
484         W[i] = 0;
488 void Sha1::append(unsigned char *dataIn, int len)
490     // Read the data into W and process blocks as they get full
491     for (int i = 0; i < len; i++)
492         {
493         W[lenW / 4] <<= 8;
494         W[lenW / 4] |= (unsigned long)dataIn[i];
495         if ((++lenW) % 64 == 0)
496             {
497             hashblock();
498             lenW = 0;
499             }
500         sizeLo += 8;
501         sizeHi += (sizeLo < 8);
502         }
506 void Sha1::finish(unsigned char hashout[20])
508     unsigned char pad0x80 = 0x80;
509     unsigned char pad0x00 = 0x00;
510     unsigned char padlen[8];
512     // Pad with a binary 1 (e.g. 0x80), then zeroes, then length
513     padlen[0] = (unsigned char)((sizeHi >> 24) & 255);
514     padlen[1] = (unsigned char)((sizeHi >> 16) & 255);
515     padlen[2] = (unsigned char)((sizeHi >>  8) & 255);
516     padlen[3] = (unsigned char)((sizeHi >>  0) & 255);
517     padlen[4] = (unsigned char)((sizeLo >> 24) & 255);
518     padlen[5] = (unsigned char)((sizeLo >> 16) & 255);
519     padlen[6] = (unsigned char)((sizeLo >>  8) & 255);
520     padlen[7] = (unsigned char)((sizeLo >>  0) & 255);
522     append(&pad0x80, 1);
524     while (lenW != 56)
525         append(&pad0x00, 1);
526     append(padlen, 8);
528     // Output hash
529     for (int i = 0; i < 20; i++)
530         {
531         hashout[i] = (unsigned char)(H[i / 4] >> 24);
532         H[i / 4] <<= 8;
533         }
535     // Re-initialize the context (also zeroizes contents)
536     init();
540 #define SHA_ROTL(X,n) ((((X) << (n)) | ((X) >> (32-(n)))) & 0xffffffffL)
542 void Sha1::hashblock()
545     for (int t = 16; t <= 79; t++)
546         W[t] = SHA_ROTL(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1);
548     unsigned long A = H[0];
549     unsigned long B = H[1];
550     unsigned long C = H[2];
551     unsigned long D = H[3];
552     unsigned long E = H[4];
554     unsigned long TEMP;
556     for (int t = 0; t <= 19; t++)
557         {
558         TEMP = (SHA_ROTL(A,5) + (((C^D)&B)^D) +
559                 E + W[t] + 0x5a827999L) & 0xffffffffL;
560         E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
561         }
562     for (int t = 20; t <= 39; t++)
563         {
564         TEMP = (SHA_ROTL(A,5) + (B^C^D) +
565                 E + W[t] + 0x6ed9eba1L) & 0xffffffffL;
566         E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
567         }
568     for (int t = 40; t <= 59; t++)
569         {
570         TEMP = (SHA_ROTL(A,5) + ((B&C)|(D&(B|C))) +
571                 E + W[t] + 0x8f1bbcdcL) & 0xffffffffL;
572         E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
573         }
574     for (int t = 60; t <= 79; t++)
575         {
576         TEMP = (SHA_ROTL(A,5) + (B^C^D) +
577                 E + W[t] + 0xca62c1d6L) & 0xffffffffL;
578         E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
579         }
581     H[0] += A;
582     H[1] += B;
583     H[2] += C;
584     H[3] += D;
585     H[4] += E;
592 //########################################################################
593 //# M D  5
594 //########################################################################
596 class Md5
598 public:
600     /**
601      *
602      */
603     Md5()
604         { init(); }
606     /**
607      *
608      */
609     virtual ~Md5()
610         {}
612     /**
613      * Static convenience method.
614      * @parm digest points to an buffer of 16 unsigned chars
615      */
616     static void hash(unsigned char *dataIn,
617                      unsigned long len, unsigned char *digest);
619     static DOMString Md5::hashHex(unsigned char *dataIn, unsigned long len);
621     /**
622      *  Initialize the context (also zeroizes contents)
623      */
624     virtual void init();
626     /**
627      *
628      */
629     virtual void append(unsigned char *dataIn, unsigned long len);
631     /**
632      *
633      */
634     virtual void append(const DOMString &str);
636     /**
637      * Finalize and output the hash.
638      * @parm digest points to an buffer of 16 unsigned chars
639      */
640     virtual void finish(unsigned char *digest);
643     /**
644      * Same as above , but hex to an output String
645      */
646     virtual DOMString finishHex();
648 private:
650     void transform(unsigned long *buf, unsigned long *in);
652     unsigned long buf[4];
653     unsigned long bits[2];
654     unsigned char in[64];
656 };
661 void Md5::hash(unsigned char *dataIn, unsigned long len, unsigned char *digest)
663     Md5 md5;
664     md5.append(dataIn, len);
665     md5.finish(digest);
668 DOMString Md5::hashHex(unsigned char *dataIn, unsigned long len)
670     Md5 md5;
671     md5.append(dataIn, len);
672     DOMString ret = md5.finishHex();
673     return ret;
678 /*
679  * Note: this code is harmless on little-endian machines.
680  */
681 /*
682 static void byteReverse(unsigned char *buf, unsigned long longs)
684     do
685         {
686         unsigned long t = (unsigned long)
687             ((unsigned) buf[3] << 8 | buf[2]) << 16 |
688             ((unsigned) buf[1] << 8 | buf[0]);
689         *(unsigned long *) buf = t;
690         buf += 4;
691         } while (--longs);
693 */
695 static void md5_memcpy(void *dest, void *src, int n)
697     unsigned char *s1 = (unsigned char *)dest;
698     unsigned char *s2 = (unsigned char *)src;
699     while (n--)
700         *s1++ = *s2++;
703 static void md5_memset(void *dest, char v, int n)
705     unsigned char *s = (unsigned char *)dest;
706     while (n--)
707         *s++ = v;
710 /**
711  * Initialize MD5 polynomials and storage
712  */
713 void Md5::init()
715     buf[0]  = 0x67452301;
716     buf[1]  = 0xefcdab89;
717     buf[2]  = 0x98badcfe;
718     buf[3]  = 0x10325476;
720     bits[0] = 0;
721     bits[1] = 0;
724 /*
725  * Update context to reflect the concatenation of another buffer full
726  * of bytes.
727  */
728 void Md5::append(unsigned char *source, unsigned long len)
731     // Update bitcount
732     unsigned long t = bits[0];
733     if ((bits[0] = t + ((unsigned long) len << 3)) < t)
734             bits[1]++;// Carry from low to high
735     bits[1] += len >> 29;
737         //Bytes already in shsInfo->data
738     t = (t >> 3) & 0x3f;
741     // Handle any leading odd-sized chunks
742     if (t)
743         {
744         unsigned char *p = (unsigned char *) in + t;
745         t = 64 - t;
746         if (len < t)
747             {
748             md5_memcpy(p, source, len);
749             return;
750             }
751         md5_memcpy(p, source, t);
752         //byteReverse(in, 16);
753         transform(buf, (unsigned long *) in);
754         source += t;
755         len    -= t;
756         }
758     // Process data in 64-byte chunks
759     while (len >= 64)
760         {
761         md5_memcpy(in, source, 64);
762         //byteReverse(in, 16);
763         transform(buf, (unsigned long *) in);
764         source += 64;
765         len    -= 64;
766         }
768     // Handle any remaining bytes of data.
769     md5_memcpy(in, source, len);
772 /*
773  * Update context to reflect the concatenation of another string
774  */
775 void Md5::append(const DOMString &str)
777     append((unsigned char *)str.c_str(), str.size());
780 /*
781  * Final wrapup - pad to 64-byte boundary with the bit pattern
782  * 1 0* (64-bit count of bits processed, MSB-first)
783  */
784 void Md5::finish(unsigned char *digest)
786     // Compute number of bytes mod 64
787     unsigned int count = (bits[0] >> 3) & 0x3F;
789     // Set the first char of padding to 0x80.
790     // This is safe since there is always at least one byte free
791     unsigned char *p = in + count;
792     *p++ = 0x80;
794     // Bytes of padding needed to make 64 bytes
795     count = 64 - 1 - count;
797     // Pad out to 56 mod 64
798     if (count < 8)
799         {
800             // Two lots of padding:  Pad the first block to 64 bytes
801             md5_memset(p, 0, count);
802             //byteReverse(in, 16);
803             transform(buf, (unsigned long *) in);
805             // Now fill the next block with 56 bytes
806             md5_memset(in, 0, 56);
807         }
808     else
809         {
810         // Pad block to 56 bytes
811         md5_memset(p, 0, count - 8);
812         }
813     //byteReverse(in, 14);
815     // Append length in bits and transform
816     ((unsigned long *) in)[14] = bits[0];
817     ((unsigned long *) in)[15] = bits[1];
819     transform(buf, (unsigned long *) in);
820     //byteReverse((unsigned char *) buf, 4);
821     md5_memcpy(digest, buf, 16);
822     init();  // Security!  ;-)
825 static char *md5hex = "0123456789abcdef";
827 DOMString Md5::finishHex()
829     unsigned char hashout[16];
830     finish(hashout);
831     DOMString ret;
832     for (int i=0 ; i<16 ; i++)
833         {
834         unsigned char ch = hashout[i];
835         ret.push_back(md5hex[ (ch>>4) & 15 ]);
836         ret.push_back(md5hex[ ch      & 15 ]);
837         }
838     return ret;
843 //#  The four core functions - F1 is optimized somewhat
845 //  #define F1(x, y, z) (x & y | ~x & z)
846 #define F1(x, y, z) (z ^ (x & (y ^ z)))
847 #define F2(x, y, z) F1(z, x, y)
848 #define F3(x, y, z) (x ^ y ^ z)
849 #define F4(x, y, z) (y ^ (x | ~z))
851 // ## This is the central step in the MD5 algorithm.
852 #define MD5STEP(f, w, x, y, z, data, s) \
853         ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
855 /*
856  * The core of the MD5 algorithm, this alters an existing MD5 hash to
857  * reflect the addition of 16 longwords of new data.  MD5Update blocks
858  * the data and converts bytes into longwords for this routine.
859  * @parm buf points to an array of 4 unsigned longs
860  * @parm in points to an array of 16 unsigned longs
861  */
862 void Md5::transform(unsigned long *buf, unsigned long *in)
864     unsigned long a = buf[0];
865     unsigned long b = buf[1];
866     unsigned long c = buf[2];
867     unsigned long d = buf[3];
869     MD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478,  7);
870     MD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12);
871     MD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17);
872     MD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22);
873     MD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf,  7);
874     MD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12);
875     MD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17);
876     MD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22);
877     MD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8,  7);
878     MD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12);
879     MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
880     MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
881     MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122,  7);
882     MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
883     MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
884     MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
886     MD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562,  5);
887     MD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340,  9);
888     MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
889     MD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20);
890     MD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d,  5);
891     MD5STEP(F2, d, a, b, c, in[10] + 0x02441453,  9);
892     MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
893     MD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20);
894     MD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6,  5);
895     MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6,  9);
896     MD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14);
897     MD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20);
898     MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905,  5);
899     MD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8,  9);
900     MD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14);
901     MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
903     MD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942,  4);
904     MD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11);
905     MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
906     MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
907     MD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44,  4);
908     MD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11);
909     MD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16);
910     MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
911     MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6,  4);
912     MD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11);
913     MD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16);
914     MD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23);
915     MD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039,  4);
916     MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
917     MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
918     MD5STEP(F3, b, c, d, a, in[ 2] + 0xc4ac5665, 23);
920     MD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244,  6);
921     MD5STEP(F4, d, a, b, c, in[ 7] + 0x432aff97, 10);
922     MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
923     MD5STEP(F4, b, c, d, a, in[ 5] + 0xfc93a039, 21);
924     MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3,  6);
925     MD5STEP(F4, d, a, b, c, in[ 3] + 0x8f0ccc92, 10);
926     MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
927     MD5STEP(F4, b, c, d, a, in[ 1] + 0x85845dd1, 21);
928     MD5STEP(F4, a, b, c, d, in[ 8] + 0x6fa87e4f,  6);
929     MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
930     MD5STEP(F4, c, d, a, b, in[ 6] + 0xa3014314, 15);
931     MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
932     MD5STEP(F4, a, b, c, d, in[ 4] + 0xf7537e82,  6);
933     MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
934     MD5STEP(F4, c, d, a, b, in[ 2] + 0x2ad7d2bb, 15);
935     MD5STEP(F4, b, c, d, a, in[ 9] + 0xeb86d391, 21);
937     buf[0] += a;
938     buf[1] += b;
939     buf[2] += c;
940     buf[3] += d;
949 //########################################################################
950 //########################################################################
951 //### T H R E A D
952 //########################################################################
953 //########################################################################
956 //########################################################################
957 //### T H R E A D
958 //########################################################################
960 /**
961  * This is the interface for a delegate class which can
962  * be run by a Thread.
963  * Thread thread(runnable);
964  * thread.start();
965  */
966 class Runnable
968 public:
970     Runnable()
971         {}
972     virtual ~Runnable()
973         {}
975     /**
976      * The method of a delegate class which can
977      * be run by a Thread.  Thread is completed when this
978      * method is done.
979      */
980     virtual void run() = 0;
982 };
986 /**
987  *  A simple wrapper of native threads in a portable class.
988  *  It can be used either to execute its own run() method, or
989  *  delegate to a Runnable class's run() method.
990  */
991 class Thread
993 public:
995     /**
996      *  Create a thread which will execute its own run() method.
997      */
998     Thread()
999         { runnable = NULL ; started = false; }
1001     /**
1002      * Create a thread which will run a Runnable class's run() method.
1003      */
1004     Thread(const Runnable &runner)
1005         { runnable = (Runnable *)&runner; started = false; }
1007     /**
1008      *  This does not kill a spawned thread.
1009      */
1010     virtual ~Thread()
1011         {}
1013     /**
1014      *  Static method to pause the current thread for a given
1015      *  number of milliseconds.
1016      */
1017     static void sleep(unsigned long millis);
1019     /**
1020      *  This method will be executed if the Thread was created with
1021      *  no delegated Runnable class.  The thread is completed when
1022      *  the method is done.
1023      */
1024     virtual void run()
1025         {}
1027     /**
1028      *  Starts the thread.
1029      */
1030     virtual void start();
1032     /**
1033      *  Calls either this class's run() method, or that of a Runnable.
1034      *  A user would normally not call this directly.
1035      */
1036     virtual void execute()
1037         {
1038         started = true;
1039         if (runnable)
1040             runnable->run();
1041         else
1042             run();
1043         }
1045 private:
1047     Runnable *runnable;
1049     bool started;
1051 };
1057 #ifdef __WIN32__
1060 static DWORD WINAPI WinThreadFunction(LPVOID context)
1062     Thread *thread = (Thread *)context;
1063     thread->execute();
1064     return 0;
1068 void Thread::start()
1070     DWORD dwThreadId;
1071     HANDLE hThread = CreateThread(NULL, 0, WinThreadFunction,
1072                (LPVOID)this,  0,  &dwThreadId);
1073     //Make sure the thread is started before 'this' is deallocated
1074     while (!started)
1075         sleep(10);
1076     CloseHandle(hThread);
1079 void Thread::sleep(unsigned long millis)
1081     Sleep(millis);
1084 #else /* UNIX */
1087 void *PthreadThreadFunction(void *context)
1089     Thread *thread = (Thread *)context;
1090     thread->execute();
1091     return NULL;
1095 void Thread::start()
1097     pthread_t thread;
1099     int ret = pthread_create(&thread, NULL,
1100             PthreadThreadFunction, (void *)this);
1101     if (ret != 0)
1102         printf("Thread::start: thread creation failed: %s\n", strerror(ret));
1104     //Make sure the thread is started before 'this' is deallocated
1105     while (!started)
1106         sleep(10);
1110 void Thread::sleep(unsigned long millis)
1112     timespec requested;
1113     requested.tv_sec = millis / 1000;
1114     requested.tv_nsec = (millis % 1000 ) * 1000000L;
1115     nanosleep(&requested, NULL);
1118 #endif
1121 //########################################################################
1122 //########################################################################
1123 //### S O C K E T
1124 //########################################################################
1125 //########################################################################
1131 class TcpSocket
1133 public:
1135     TcpSocket();
1137     TcpSocket(const std::string &hostname, int port);
1139     TcpSocket(const char *hostname, int port);
1141     TcpSocket(const TcpSocket &other);
1143     virtual ~TcpSocket();
1145     bool isConnected();
1147     void enableSSL(bool val);
1149     bool connect(const std::string &hostname, int portno);
1151     bool connect(const char *hostname, int portno);
1153     bool startTls();
1155     bool connect();
1157     bool disconnect();
1159     bool setReceiveTimeout(unsigned long millis);
1161     long available();
1163     bool write(int ch);
1165     bool write(char *str);
1167     bool write(const std::string &str);
1169     int read();
1171     std::string readLine();
1173 private:
1174     void init();
1176     std::string hostname;
1177     int  portno;
1178     int  sock;
1179     bool connected;
1181     bool sslEnabled;
1183     unsigned long receiveTimeout;
1185 #ifdef HAVE_SSL
1186     SSL_CTX *sslContext;
1187     SSL *sslStream;
1188 #endif
1190 };
1194 //#########################################################################
1195 //# U T I L I T Y
1196 //#########################################################################
1198 static void mybzero(void *s, size_t n)
1200     unsigned char *p = (unsigned char *)s;
1201     while (n > 0)
1202         {
1203         *p++ = (unsigned char)0;
1204         n--;
1205         }
1208 static void mybcopy(void *src, void *dest, size_t n)
1210     unsigned char *p = (unsigned char *)dest;
1211     unsigned char *q = (unsigned char *)src;
1212     while (n > 0)
1213         {
1214         *p++ = *q++;
1215         n--;
1216         }
1221 //#########################################################################
1222 //# T C P    C O N N E C T I O N
1223 //#########################################################################
1225 TcpSocket::TcpSocket()
1227     init();
1231 TcpSocket::TcpSocket(const char *hostnameArg, int port)
1233     init();
1234     hostname  = hostnameArg;
1235     portno    = port;
1238 TcpSocket::TcpSocket(const std::string &hostnameArg, int port)
1240     init();
1241     hostname  = hostnameArg;
1242     portno    = port;
1246 #ifdef HAVE_SSL
1248 static void cryptoLockCallback(int mode, int type, const char *file, int line)
1250     //printf("########### LOCK\n");
1251     static int modes[CRYPTO_NUM_LOCKS]; /* = {0, 0, ... } */
1252     const char *errstr = NULL;
1254     int rw = mode & (CRYPTO_READ|CRYPTO_WRITE);
1255     if (!((rw == CRYPTO_READ) || (rw == CRYPTO_WRITE)))
1256         {
1257         errstr = "invalid mode";
1258         goto err;
1259         }
1261     if (type < 0 || type >= CRYPTO_NUM_LOCKS)
1262         {
1263         errstr = "type out of bounds";
1264         goto err;
1265         }
1267     if (mode & CRYPTO_LOCK)
1268         {
1269         if (modes[type])
1270             {
1271             errstr = "already locked";
1272             /* must not happen in a single-threaded program
1273              * (would deadlock)
1274              */
1275             goto err;
1276             }
1278         modes[type] = rw;
1279         }
1280     else if (mode & CRYPTO_UNLOCK)
1281         {
1282         if (!modes[type])
1283             {
1284              errstr = "not locked";
1285              goto err;
1286              }
1288         if (modes[type] != rw)
1289             {
1290             errstr = (rw == CRYPTO_READ) ?
1291                   "CRYPTO_r_unlock on write lock" :
1292                   "CRYPTO_w_unlock on read lock";
1293             }
1295         modes[type] = 0;
1296         }
1297     else
1298         {
1299         errstr = "invalid mode";
1300         goto err;
1301         }
1303     err:
1304     if (errstr)
1305         {
1306         /* we cannot use bio_err here */
1307         fprintf(stderr, "openssl (lock_dbg_cb): %s (mode=%d, type=%d) at %s:%d\n",
1308                 errstr, mode, type, file, line);
1309         }
1312 static unsigned long cryptoIdCallback()
1314 #ifdef __WIN32__
1315     unsigned long ret = (unsigned long) GetCurrentThreadId();
1316 #else
1317     unsigned long ret = (unsigned long) pthread_self();
1318 #endif
1319     return ret;
1322 #endif
1325 TcpSocket::TcpSocket(const TcpSocket &other)
1327     init();
1328     sock      = other.sock;
1329     hostname  = other.hostname;
1330     portno    = other.portno;
1333 static bool tcp_socket_inited = false;
1335 void TcpSocket::init()
1337     if (!tcp_socket_inited)
1338         {
1339 #ifdef __WIN32__
1340         WORD wVersionRequested = MAKEWORD( 2, 2 );
1341         WSADATA wsaData;
1342         WSAStartup( wVersionRequested, &wsaData );
1343 #endif
1344 #ifdef HAVE_SSL
1345         sslStream  = NULL;
1346         sslContext = NULL;
1347             CRYPTO_set_locking_callback(cryptoLockCallback);
1348         CRYPTO_set_id_callback(cryptoIdCallback);
1349         SSL_library_init();
1350         SSL_load_error_strings();
1351 #endif
1352         tcp_socket_inited = true;
1353         }
1354     sock           = -1;
1355     connected      = false;
1356     hostname       = "";
1357     portno         = -1;
1358     sslEnabled     = false;
1359     receiveTimeout = 0;
1362 TcpSocket::~TcpSocket()
1364     disconnect();
1367 bool TcpSocket::isConnected()
1369     if (!connected || sock < 0)
1370         return false;
1371     return true;
1374 void TcpSocket::enableSSL(bool val)
1376     sslEnabled = val;
1380 bool TcpSocket::connect(const char *hostnameArg, int portnoArg)
1382     hostname = hostnameArg;
1383     portno   = portnoArg;
1384     return connect();
1387 bool TcpSocket::connect(const std::string &hostnameArg, int portnoArg)
1389     hostname = hostnameArg;
1390     portno   = portnoArg;
1391     return connect();
1396 #ifdef HAVE_SSL
1397 /*
1398 static int password_cb(char *buf, int bufLen, int rwflag, void *userdata)
1400     char *password = "password";
1401     if (bufLen < (int)(strlen(password)+1))
1402         return 0;
1404     strcpy(buf,password);
1405     int ret = strlen(password);
1406     return ret;
1409 static void infoCallback(const SSL *ssl, int where, int ret)
1411     switch (where)
1412         {
1413         case SSL_CB_ALERT:
1414             {
1415             printf("## %d SSL ALERT: %s\n",  where, SSL_alert_desc_string_long(ret));
1416             break;
1417             }
1418         default:
1419             {
1420             printf("## %d SSL: %s\n",  where, SSL_state_string_long(ssl));
1421             break;
1422             }
1423         }
1425 */
1426 #endif
1429 bool TcpSocket::startTls()
1431 #ifdef HAVE_SSL
1432     sslStream  = NULL;
1433     sslContext = NULL;
1435     //SSL_METHOD *meth = SSLv23_method();
1436     //SSL_METHOD *meth = SSLv3_client_method();
1437     SSL_METHOD *meth = TLSv1_client_method();
1438     sslContext = SSL_CTX_new(meth);
1439     //SSL_CTX_set_info_callback(sslContext, infoCallback);
1441 #if 0
1442     char *keyFile  = "client.pem";
1443     char *caList   = "root.pem";
1444     /* Load our keys and certificates*/
1445     if (!(SSL_CTX_use_certificate_chain_file(sslContext, keyFile)))
1446         {
1447         fprintf(stderr, "Can't read certificate file\n");
1448         disconnect();
1449         return false;
1450         }
1452     SSL_CTX_set_default_passwd_cb(sslContext, password_cb);
1454     if (!(SSL_CTX_use_PrivateKey_file(sslContext, keyFile, SSL_FILETYPE_PEM)))
1455         {
1456         fprintf(stderr, "Can't read key file\n");
1457         disconnect();
1458         return false;
1459         }
1461     /* Load the CAs we trust*/
1462     if (!(SSL_CTX_load_verify_locations(sslContext, caList, 0)))
1463         {
1464         fprintf(stderr, "Can't read CA list\n");
1465         disconnect();
1466         return false;
1467         }
1468 #endif
1470     /* Connect the SSL socket */
1471     sslStream  = SSL_new(sslContext);
1472     SSL_set_fd(sslStream, sock);
1474     int ret = SSL_connect(sslStream);
1475     if (ret == 0)
1476         {
1477         fprintf(stderr, "SSL connection not successful\n");
1478         disconnect();
1479         return false;
1480         }
1481     else if (ret < 0)
1482         {
1483         int err = SSL_get_error(sslStream, ret);
1484         fprintf(stderr, "SSL connect error %d\n", err);
1485         disconnect();
1486         return false;
1487         }
1489     sslEnabled = true;
1490 #endif /*HAVE_SSL*/
1491     return true;
1495 bool TcpSocket::connect()
1497     if (hostname.size()<1)
1498         {
1499         fprintf(stderr, "open: null hostname\n");
1500         return false;
1501         }
1503     if (portno<1)
1504         {
1505         fprintf(stderr, "open: bad port number\n");
1506         return false;
1507         }
1509     sock = socket(PF_INET, SOCK_STREAM, 0);
1510     if (sock < 0)
1511         {
1512         fprintf(stderr, "open: error creating socket\n");
1513         return false;
1514         }
1516     char *c_hostname = (char *)hostname.c_str();
1517     struct hostent *server = gethostbyname(c_hostname);
1518     if (!server)
1519         {
1520         fprintf(stderr, "open: could not locate host '%s'\n", c_hostname);
1521         return false;
1522         }
1524     struct sockaddr_in serv_addr;
1525     mybzero((char *) &serv_addr, sizeof(serv_addr));
1526     serv_addr.sin_family = AF_INET;
1527     mybcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr,
1528          server->h_length);
1529     serv_addr.sin_port = htons(portno);
1531     int ret = ::connect(sock, (const sockaddr *)&serv_addr, sizeof(serv_addr));
1532     if (ret < 0)
1533         {
1534         fprintf(stderr, "open: could not connect to host '%s'\n", c_hostname);
1535         return false;
1536         }
1538      if (sslEnabled)
1539         {
1540         if (!startTls())
1541             return false;
1542         }
1543     connected = true;
1544     return true;
1547 bool TcpSocket::disconnect()
1549     bool ret  = true;
1550     connected = false;
1551 #ifdef HAVE_SSL
1552     if (sslEnabled)
1553         {
1554         if (sslStream)
1555             {
1556             int r = SSL_shutdown(sslStream);
1557             switch(r)
1558                 {
1559                 case 1:
1560                     break; /* Success */
1561                 case 0:
1562                 case -1:
1563                 default:
1564                     //printf("Shutdown failed");
1565                     ret = false;
1566                 }
1567             SSL_free(sslStream);
1568             }
1569         if (sslContext)
1570             SSL_CTX_free(sslContext);
1571         }
1572     sslStream  = NULL;
1573     sslContext = NULL;
1574 #endif /*HAVE_SSL*/
1576 #ifdef __WIN32__
1577     closesocket(sock);
1578 #else
1579     ::close(sock);
1580 #endif
1581     sock = -1;
1582     sslEnabled = false;
1584     return ret;
1589 bool TcpSocket::setReceiveTimeout(unsigned long millis)
1591     receiveTimeout = millis;
1592     return true;
1595 /**
1596  * For normal sockets, return the number of bytes waiting to be received.
1597  * For SSL, just return >0 when something is ready to be read.
1598  */
1599 long TcpSocket::available()
1601     if (!isConnected())
1602         return -1;
1604     long count = 0;
1605 #ifdef __WIN32__
1606     if (ioctlsocket(sock, FIONREAD, (unsigned long *)&count) != 0)
1607         return -1;
1608 #else
1609     if (ioctl(sock, FIONREAD, &count) != 0)
1610         return -1;
1611 #endif
1612     if (count<=0 && sslEnabled)
1613         {
1614 #ifdef HAVE_SSL
1615         return SSL_pending(sslStream);
1616 #endif
1617         }
1618     return count;
1623 bool TcpSocket::write(int ch)
1625     if (!isConnected())
1626         {
1627         fprintf(stderr, "write: socket closed\n");
1628         return false;
1629         }
1630     unsigned char c = (unsigned char)ch;
1632     if (sslEnabled)
1633         {
1634 #ifdef HAVE_SSL
1635         int r = SSL_write(sslStream, &c, 1);
1636         if (r<=0)
1637             {
1638             switch(SSL_get_error(sslStream, r))
1639                 {
1640                 default:
1641                     fprintf(stderr, "SSL write problem");
1642                     return -1;
1643                 }
1644             }
1645 #endif
1646         }
1647     else
1648         {
1649         if (send(sock, (const char *)&c, 1, 0) < 0)
1650         //if (send(sock, &c, 1, 0) < 0)
1651             {
1652             fprintf(stderr, "write: could not send data\n");
1653             return false;
1654             }
1655         }
1656     return true;
1659 bool TcpSocket::write(char *str)
1661    if (!isConnected())
1662         {
1663         fprintf(stderr, "write(str): socket closed\n");
1664         return false;
1665         }
1666     int len = strlen(str);
1668     if (sslEnabled)
1669         {
1670 #ifdef HAVE_SSL
1671         int r = SSL_write(sslStream, (unsigned char *)str, len);
1672         if (r<=0)
1673             {
1674             switch(SSL_get_error(sslStream, r))
1675                 {
1676                 default:
1677                     fprintf(stderr, "SSL write problem");
1678                     return -1;
1679                 }
1680             }
1681 #endif
1682         }
1683     else
1684         {
1685         if (send(sock, str, len, 0) < 0)
1686         //if (send(sock, &c, 1, 0) < 0)
1687             {
1688             fprintf(stderr, "write: could not send data\n");
1689             return false;
1690             }
1691         }
1692     return true;
1695 bool TcpSocket::write(const std::string &str)
1697     return write((char *)str.c_str());
1700 int TcpSocket::read()
1702     if (!isConnected())
1703         return -1;
1705     //We'll use this loop for timeouts, so that SSL and plain sockets
1706     //will behave the same way
1707     if (receiveTimeout > 0)
1708         {
1709         unsigned long tim = 0;
1710         while (true)
1711             {
1712             int avail = available();
1713             if (avail > 0)
1714                 break;
1715             if (tim >= receiveTimeout)
1716                 return -2;
1717             Thread::sleep(20);
1718             tim += 20;
1719             }
1720         }
1722     //check again
1723     if (!isConnected())
1724         return -1;
1726     unsigned char ch;
1727     if (sslEnabled)
1728         {
1729 #ifdef HAVE_SSL
1730         if (!sslStream)
1731             return -1;
1732         int r = SSL_read(sslStream, &ch, 1);
1733         unsigned long err = SSL_get_error(sslStream, r);
1734         switch (err)
1735             {
1736             case SSL_ERROR_NONE:
1737                  break;
1738             case SSL_ERROR_ZERO_RETURN:
1739                 return -1;
1740             case SSL_ERROR_SYSCALL:
1741                 fprintf(stderr, "SSL read problem(syscall) %s\n",
1742                      ERR_error_string(ERR_get_error(), NULL));
1743                 return -1;
1744             default:
1745                 fprintf(stderr, "SSL read problem %s\n",
1746                      ERR_error_string(ERR_get_error(), NULL));
1747                 return -1;
1748             }
1749 #endif
1750         }
1751     else
1752         {
1753         if (recv(sock, (char *)&ch, 1, 0) <= 0)
1754             {
1755             fprintf(stderr, "read: could not receive data\n");
1756             disconnect();
1757             return -1;
1758             }
1759         }
1760     return (int)ch;
1763 std::string TcpSocket::readLine()
1765     std::string ret;
1767     while (isConnected())
1768         {
1769         int ch = read();
1770         if (ch<0)
1771             return ret;
1772         if (ch=='\r' || ch=='\n')
1773             return ret;
1774         ret.push_back((char)ch);
1775         }
1777     return ret;
1781 //########################################################################
1782 //########################################################################
1783 //### X M P P
1784 //########################################################################
1785 //########################################################################
1790 //########################################################################
1791 //# X M P P    E V E N T
1792 //########################################################################
1795 XmppEvent::XmppEvent(int type)
1797     eventType = type;
1798     presence  = false;
1799     dom       = NULL;
1802 XmppEvent::XmppEvent(const XmppEvent &other)
1804     assign(other);
1807 XmppEvent &XmppEvent::operator=(const XmppEvent &other)
1809     assign(other);
1810     return (*this);
1813 XmppEvent::~XmppEvent()
1815     if (dom)
1816         delete dom;
1819 void XmppEvent::assign(const XmppEvent &other)
1821     eventType = other.eventType;
1822     presence  = other.presence;
1823     status    = other.status;
1824     show      = other.show;
1825     to        = other.to;
1826     from      = other.from;
1827     group     = other.group;
1828     data      = other.data;
1829     fileName  = other.fileName;
1830     fileDesc  = other.fileDesc;
1831     fileSize  = other.fileSize;
1832     fileHash  = other.fileHash;
1833     setDOM(other.dom);
1836 int XmppEvent::getType() const
1838     return eventType;
1841 DOMString XmppEvent::getIqId() const
1843     return iqId;
1846 void XmppEvent::setIqId(const DOMString &val)
1848     iqId = val;
1851 DOMString XmppEvent::getStreamId() const
1853     return streamId;
1856 void XmppEvent::setStreamId(const DOMString &val)
1858     streamId = val;
1861 bool XmppEvent::getPresence() const
1863     return presence;
1866 void XmppEvent::setPresence(bool val)
1868     presence = val;
1871 DOMString XmppEvent::getShow() const
1873     return show;
1876 void XmppEvent::setShow(const DOMString &val)
1878     show = val;
1881 DOMString XmppEvent::getStatus() const
1883     return status;
1886 void XmppEvent::setStatus(const DOMString &val)
1888     status = val;
1891 DOMString XmppEvent::getTo() const
1893     return to;
1896 void XmppEvent::setTo(const DOMString &val)
1898     to = val;
1901 DOMString XmppEvent::getFrom() const
1903     return from;
1906 void XmppEvent::setFrom(const DOMString &val)
1908     from = val;
1911 DOMString XmppEvent::getGroup() const
1913     return group;
1916 void XmppEvent::setGroup(const DOMString &val)
1918     group = val;
1921 DOMString XmppEvent::getData() const
1923     return data;
1926 void XmppEvent::setData(const DOMString &val)
1928     data = val;
1931 DOMString XmppEvent::getFileName() const
1933     return fileName;
1936 void XmppEvent::setFileName(const DOMString &val)
1938     fileName = val;
1941 DOMString XmppEvent::getFileDesc() const
1943     return fileDesc;
1946 void XmppEvent::setFileDesc(const DOMString &val)
1948     fileDesc = val;
1951 long XmppEvent::getFileSize() const
1953     return fileSize;
1956 void XmppEvent::setFileSize(long val)
1958     fileSize = val;
1961 DOMString XmppEvent::getFileHash() const
1963     return fileHash;
1966 void XmppEvent::setFileHash(const DOMString &val)
1968     fileHash = val;
1971 Element *XmppEvent::getDOM() const
1973     return dom;
1976 void XmppEvent::setDOM(const Element *val)
1978     if (!val)
1979         dom = NULL;
1980     else
1981         dom = ((Element *)val)->clone();
1985 std::vector<XmppUser> XmppEvent::getUserList() const
1987     return userList;
1990 void XmppEvent::setUserList(const std::vector<XmppUser> &val)
1992     userList = val;
1995 //########################################################################
1996 //# X M P P    E V E N T    T A R G E T
1997 //########################################################################
1999 //###########################
2000 //# CONSTRUCTORS
2001 //###########################
2003 XmppEventTarget::XmppEventTarget()
2005     eventQueueEnabled = false;
2009 XmppEventTarget::XmppEventTarget(const XmppEventTarget &other)
2011     listeners         = other.listeners;
2012     eventQueueEnabled = other.eventQueueEnabled;
2015 XmppEventTarget::~XmppEventTarget()
2020 //###########################
2021 //# M E S S A G E S
2022 //###########################
2024 void XmppEventTarget::error(char *fmt, ...)
2026     va_list args;
2027     va_start(args,fmt);
2028     vsnprintf(targetWriteBuf, targetWriteBufLen, fmt, args);
2029     va_end(args) ;
2030     printf("Error:%s\n", targetWriteBuf);
2031     XmppEvent evt(XmppEvent::EVENT_ERROR);
2032     evt.setData(targetWriteBuf);
2033     dispatchXmppEvent(evt);
2036 void XmppEventTarget::status(char *fmt, ...)
2038     va_list args;
2039     va_start(args,fmt);
2040     vsnprintf(targetWriteBuf, targetWriteBufLen, fmt, args);
2041     va_end(args) ;
2042     //printf("Status:%s\n", targetWriteBuf);
2043     XmppEvent evt(XmppEvent::EVENT_STATUS);
2044     evt.setData(targetWriteBuf);
2045     dispatchXmppEvent(evt);
2050 //###########################
2051 //# L I S T E N E R S
2052 //###########################
2054 void XmppEventTarget::dispatchXmppEvent(const XmppEvent &event)
2056     std::vector<XmppEventListener *>::iterator iter;
2057     for (iter = listeners.begin(); iter != listeners.end() ; iter++)
2058         (*iter)->processXmppEvent(event);
2059     if (eventQueueEnabled)
2060         eventQueue.push_back(event);
2063 void XmppEventTarget::addXmppEventListener(const XmppEventListener &listener)
2065     XmppEventListener *lsnr = (XmppEventListener *)&listener;
2066     std::vector<XmppEventListener *>::iterator iter;
2067     for (iter = listeners.begin(); iter != listeners.end() ; iter++)
2068         if (*iter == lsnr)
2069             return;
2070     listeners.push_back(lsnr);
2073 void XmppEventTarget::removeXmppEventListener(const XmppEventListener &listener)
2075     XmppEventListener *lsnr = (XmppEventListener *)&listener;
2076     std::vector<XmppEventListener *>::iterator iter;
2077     for (iter = listeners.begin(); iter != listeners.end() ; iter++)
2078         if (*iter == lsnr)
2079             listeners.erase(iter);
2082 void XmppEventTarget::clearXmppEventListeners()
2084     listeners.clear();
2088 //###########################
2089 //# E V E N T    Q U E U E
2090 //###########################
2092 void XmppEventTarget::eventQueueEnable(bool val)
2094     eventQueueEnabled = val;
2095     if (!eventQueueEnabled)
2096         eventQueue.clear();
2099 int XmppEventTarget::eventQueueAvailable()
2101     return eventQueue.size();
2104 XmppEvent XmppEventTarget::eventQueuePop()
2106     if (!eventQueueEnabled || eventQueue.size()<1)
2107         {
2108         XmppEvent dummy(XmppEvent::EVENT_NONE);
2109         return dummy;
2110         }
2111     XmppEvent event = *(eventQueue.begin());
2112     eventQueue.erase(eventQueue.begin());
2113     return event;
2117 //########################################################################
2118 //# X M P P    S T R E A M
2119 //########################################################################
2121 /**
2122  *
2123  */
2124 class XmppStream
2126 public:
2128     /**
2129      *
2130      */
2131     XmppStream();
2133     /**
2134      *
2135      */
2136     virtual ~XmppStream();
2138     /**
2139      *
2140      */
2141     virtual void reset();
2143     /**
2144      *
2145      */
2146     virtual int getState();
2148     /**
2149      *
2150      */
2151     virtual void setState(int val);
2153     /**
2154      *
2155      */
2156     virtual DOMString getStreamId();
2158     /**
2159      *
2160      */
2161     void setStreamId(const DOMString &val);
2163     /**
2164      *
2165      */
2166     virtual DOMString getIqId();
2168     /**
2169      *
2170      */
2171     void setIqId(const DOMString &val);
2173     /**
2174      *
2175      */
2176     virtual int getSeqNr();
2178     /**
2179      *
2180      */
2181     virtual DOMString getPeerId();
2183     /**
2184      *
2185      */
2186     virtual void setPeerId(const DOMString &val);
2188     /**
2189      *
2190      */
2191     int available();
2193     /**
2194      *
2195      */
2196     void receiveData(std::vector<unsigned char> &newData);
2198     /**
2199      *
2200      */
2201     std::vector<unsigned char> read();
2203 private:
2206     DOMString streamId;
2208     DOMString iqId;
2210     DOMString sourceId;
2212     int state;
2214     long seqNr;
2216     std::vector<unsigned char> data;
2217 };
2220 /**
2221  *
2222  */
2223 XmppStream::XmppStream()
2225     reset();
2228 /**
2229  *
2230  */
2231 XmppStream::~XmppStream()
2233     reset();
2236 /**
2237  *
2238  */
2239 void XmppStream::reset()
2241     state = XmppClient::STREAM_AVAILABLE;
2242     seqNr = 0;
2243     data.clear();
2246 /**
2247  *
2248  */
2249 int XmppStream::getState()
2251     return state;
2254 /**
2255  *
2256  */
2257 void XmppStream::setState(int val)
2259     state = val;
2262 /**
2263  *
2264  */
2265 DOMString XmppStream::getStreamId()
2267     return streamId;
2270 /**
2271  *
2272  */
2273 void XmppStream::setStreamId(const DOMString &val)
2275     streamId = val;
2278 /**
2279  *
2280  */
2281 DOMString XmppStream::getIqId()
2283     return iqId;
2287 /**
2288  *
2289  */
2290 void XmppStream::setIqId(const DOMString &val)
2292     iqId = val;
2295 /**
2296  *  Source or destination JID
2297  */
2298 void XmppStream::setPeerId(const DOMString &val)
2300     sourceId = val;
2303 /**
2304  *  Source or destination JID
2305  */
2306 DOMString XmppStream::getPeerId()
2308     return sourceId;
2311 /**
2312  *  Stream packet sequence number
2313  */
2314 int XmppStream::getSeqNr()
2316     seqNr++;
2317     if (seqNr >= 65535)
2318         seqNr = 0;
2319     return seqNr;
2322 /**
2323  *
2324  */
2325 int XmppStream::available()
2327     return data.size();
2330 /**
2331  *
2332  */
2333 void XmppStream::receiveData(std::vector<unsigned char> &newData)
2335     std::vector<unsigned char>::iterator iter;
2336     for (iter=newData.begin() ; iter!=newData.end() ; iter++)
2337         data.push_back(*iter);
2340 /**
2341  *
2342  */
2343 std::vector<unsigned char> XmppStream::read()
2345     if (state != XmppClient::STREAM_OPEN)
2346         {
2347         std::vector<unsigned char>dummy;
2348         return dummy;
2349         }
2350     std::vector<unsigned char> ret = data;
2351     data.clear();
2352     return ret;
2360 //########################################################################
2361 //# X M P P    C L I E N T
2362 //########################################################################
2363 class ReceiverThread : public Runnable
2365 public:
2367     ReceiverThread(XmppClient &par) : client(par) {}
2369     virtual ~ReceiverThread() {}
2371     void run()
2372       { client.receiveAndProcessLoop(); }
2374 private:
2376     XmppClient &client;
2377 };
2380 //###########################
2381 //# CONSTRUCTORS
2382 //###########################
2384 XmppClient::XmppClient()
2386     init();
2390 XmppClient::XmppClient(const XmppClient &other) : XmppEventTarget(other)
2392     init();
2393     assign(other);
2396 void XmppClient::assign(const XmppClient &other)
2398     msgId         = other.msgId;
2399     host          = other.host;
2400     realm         = other.realm;
2401     port          = other.port;
2402     username      = other.username;
2403     password      = other.password;
2404     resource      = other.resource;
2405     connected     = other.connected;
2406     doRegister    = other.doRegister;
2407     groupChats    = other.groupChats;
2411 void XmppClient::init()
2413     sock          = new TcpSocket();
2414     msgId         = 0;
2415     connected     = false;
2416     doRegister    = false;
2418     for (int i=0 ; i<outputStreamCount ; i++)
2419         {
2420         outputStreams[i] = new XmppStream();
2421         }
2422     for (int i=0 ; i<inputStreamCount ; i++)
2423         {
2424         inputStreams[i] = new XmppStream();
2425         }
2426     for (int i=0 ; i<fileSendCount ; i++)
2427         {
2428         fileSends[i] = new XmppStream();
2429         }
2432 XmppClient::~XmppClient()
2434     disconnect();
2435     delete sock;
2436     for (int i=0 ; i<outputStreamCount ; i++)
2437         {
2438         delete outputStreams[i];
2439         }
2440     for (int i=0 ; i<inputStreamCount ; i++)
2441         {
2442         delete inputStreams[i];
2443         }
2444     for (int i=0 ; i<fileSendCount ; i++)
2445         {
2446         delete fileSends[i];
2447         }
2448     groupChatsClear();
2451 //##############################################
2452 //# UTILILY
2453 //##############################################
2455 /**
2456  *
2457  */
2458 bool XmppClient::pause(unsigned long millis)
2460     Thread::sleep(millis);
2461     return true;
2465 static int strIndex(const DOMString &str, char *key)
2467     unsigned int p = str.find(key);
2468     if (p == DOMString::npos)
2469         return -1;
2470     return p;
2474 DOMString XmppClient::toXml(const DOMString &str)
2476     return Parser::encode(str);
2479 static DOMString trim(const DOMString &str)
2481     unsigned int i;
2482     for (i=0 ; i<str.size() ; i++)
2483         if (!isspace(str[i]))
2484             break;
2485     int start = i;
2486     for (i=str.size() ; i>0 ; i--)
2487         if (!isspace(str[i-1]))
2488             break;
2489     int end = i;
2490     if (start>=end)
2491         return "";
2492     return str.substr(start, end);
2495 //##############################################
2496 //# VARIABLES  (ones that need special handling)
2497 //##############################################
2498 /**
2499  *
2500  */
2501 DOMString XmppClient::getUsername()
2503     return username;
2506 /**
2507  *
2508  */
2509 void XmppClient::setUsername(const DOMString &val)
2511     int p = strIndex(val, "@");
2512     if (p > 0)
2513         {
2514         username = val.substr(0, p);
2515         realm    = val.substr(p+1, jid.size()-p-1);
2516         }
2517     else
2518        {
2519        realm    = host;
2520        username = val;
2521        }
2524 //##############################################
2525 //# CONNECTION
2526 //##############################################
2528 //#######################
2529 //# RECEIVING
2530 //#######################
2531 DOMString XmppClient::readStanza()
2533     int  openCount    = 0;
2534     bool inTag        = false;
2535     bool slashSeen    = false;
2536     bool trivialTag   = false;
2537     bool querySeen    = false;
2538     bool inQuote      = false;
2539     bool textSeen     = false;
2540     DOMString buf;
2542     while (true)
2543         {
2544         int ch = sock->read();
2545         //printf("%c", ch); fflush(stdout);
2546         if (ch<0)
2547             {
2548             if (ch == -2) //a simple timeout, not an error
2549                 {
2550                 //Since we are timed out, let's assume that we
2551                 //are between chunks of text.  Let's reset all states.
2552                 //printf("-----#### Timeout\n");
2553                 continue;
2554                 }
2555             else
2556                 {
2557                 keepGoing = false;
2558                 if (!sock->isConnected())
2559                     {
2560                     disconnect();
2561                     return "";
2562                     }
2563                 else
2564                     {
2565                     error("socket read error");
2566                     disconnect();
2567                     return "";
2568                     }
2569                 }
2570             }
2571         buf.push_back(ch);
2572         if (ch == '<')
2573             {
2574             inTag      = true;
2575             slashSeen  = false;
2576             querySeen  = false;
2577             inQuote    = false;
2578             textSeen   = false;
2579             trivialTag = false;
2580             }
2581         else if (ch == '>')
2582             {
2583             if (!inTag)  //unescaped '>' in pcdata? horror
2584                 continue;
2585             inTag     = false;
2586             if (!trivialTag && !querySeen)
2587                 {
2588                 if (slashSeen)
2589                     openCount--;
2590                 else
2591                     openCount++;
2592                 }
2593             //printf("# openCount:%d t:%d q:%d\n",
2594             //      openCount, trivialTag, querySeen);
2595             //check if we are 'balanced', but not a <?version?> tag
2596             if (openCount <= 0 && !querySeen)
2597                 {
2598                 break;
2599                 }
2600             //we know that this one will be open-ended
2601             if (strIndex(buf, "<stream:stream") >= 0)
2602                 {
2603                 buf.append("</stream:stream>");
2604                 break;
2605                 }
2606             }
2607         else if (ch == '/')
2608             {
2609             if (inTag && !inQuote)
2610                 {
2611                 slashSeen = true;
2612                 if (textSeen) // <tagName/>  <--looks like this
2613                     trivialTag = true;
2614                 }
2615             }
2616         else if (ch == '?')
2617             {
2618             if (inTag && !inQuote)
2619                 querySeen = true;
2620             }
2621         else if (ch == '"' || ch == '\'')
2622             {
2623             if (inTag)
2624                 inQuote = !inQuote;
2625             }
2626         else
2627             {
2628             if (inTag && !inQuote && !isspace(ch))
2629                 textSeen = true;
2630             }
2631         }
2632     return buf;
2637 static bool isGroupChat(Element *root)
2639     if (!root)
2640         return false;
2641     std::vector<Element *>elems = root->findElements("x");
2642     for (unsigned int i=0 ; i<elems.size() ; i++)
2643         {
2644         DOMString xmlns = elems[i]->getAttribute("xmlns");
2645         //printf("### XMLNS ### %s\n", xmlns.c_str());
2646         if (strIndex(xmlns, "http://jabber.org/protocol/muc") >=0 )
2647             return true;
2648         }
2649    return false;
2655 static bool parseJid(const DOMString &fullJid,
2656              DOMString &jid, DOMString &resource)
2658     int p = strIndex(fullJid, "/");
2659     if (p < 0)
2660         {
2661         jid = fullJid;
2662         resource = "";
2663         return true;
2664         }
2665     jid = fullJid.substr(0, p);
2666     resource = fullJid.substr(p+1, fullJid.size()-p-1);
2667     return true;
2673 bool XmppClient::processMessage(Element *root)
2675     DOMString from    = root->getTagAttribute("message", "from");
2676     DOMString to      = root->getTagAttribute("message", "to");
2677     DOMString type    = root->getTagAttribute("message", "type");
2679     //####Check for embedded namespaces here
2680     //# IN BAND BYTESTREAMS
2681     DOMString ibbNamespace = "http://jabber.org/protocol/ibb";
2682     if (root->getTagAttribute("data", "xmlns") == ibbNamespace)
2683         {
2684         DOMString streamId = root->getTagAttribute("data", "sid");
2685         if (streamId.size() > 0)
2686             {
2687             for (int i=0 ; i<inputStreamCount ; i++)
2688                 {
2689                 XmppStream *ins = inputStreams[i];
2690                 //printf("##ins:%s  streamid:%s\n",
2691                 //    ins->getStreamId().c_str(),  streamId.c_str());
2692                 if (ins->getStreamId() == streamId)
2693                     {
2694                     //# We have a winner
2695                     if (ins->getState() != STREAM_OPEN)
2696                         {
2697                         XmppEvent event(XmppEvent::EVENT_ERROR);
2698                         event.setFrom(from);
2699                         event.setData("received unrequested stream data");
2700                         dispatchXmppEvent(event);
2701                         return true;
2702                         }
2703                     DOMString data = root->getTagValue("data");
2704                     std::vector<unsigned char>binData =
2705                                Base64Decoder::decode(data);
2706                     ins->receiveData(binData);
2707                     }
2708                 }
2709             }
2710         }
2713     //#### NORMAL MESSAGES
2714     DOMString subject = root->getTagValue("subject");
2715     DOMString body    = root->getTagValue("body");
2716     DOMString thread  = root->getTagValue("thread");
2717     //##rfc 3921, para 2.4.  ignore if no recognizable info
2718     if (subject.size() < 1 && body.size()<1 && thread.size()<1)
2719         return true;
2721     if (type == "groupchat")
2722         {
2723         DOMString fromGid;
2724         DOMString fromNick;
2725         parseJid(from, fromGid, fromNick);
2726         //printf("fromGid:%s  fromNick:%s\n",
2727         //        fromGid.c_str(), fromNick.c_str());
2728         DOMString toGid;
2729         DOMString toNick;
2730         parseJid(to, toGid, toNick);
2731         //printf("toGid:%s  toNick:%s\n",
2732         //        toGid.c_str(), toNick.c_str());
2734         if (fromNick.size() > 0)//normal group message
2735             {
2736             XmppEvent event(XmppEvent::EVENT_MUC_MESSAGE);
2737             event.setGroup(fromGid);
2738             event.setFrom(fromNick);
2739             event.setData(body);
2740             event.setDOM(root);
2741             dispatchXmppEvent(event);
2742             }
2743         else // from the server itself
2744             {
2745             //note the space before, so it doesnt match 'unlocked'
2746             if (strIndex(body, " locked") >= 0)
2747                 {
2748                 printf("LOCKED!! ;)\n");
2749                 char *fmt =
2750                 "<iq from='%s' id='create%d' to='%s' type='set'>"
2751                 "<query xmlns='http://jabber.org/protocol/muc#owner'>"
2752                 "<x xmlns='jabber:x:data' type='submit'/>"
2753                 "</query></iq>\n";
2754                 if (!write(fmt, jid.c_str(), msgId++, fromGid.c_str()))
2755                     return false;
2756                 }
2757             }
2758         }
2759     else
2760         {
2761         XmppEvent event(XmppEvent::EVENT_MESSAGE);
2762         event.setFrom(from);
2763         event.setData(body);
2764         event.setDOM(root);
2765         dispatchXmppEvent(event);
2766         }
2768     return true;
2774 bool XmppClient::processPresence(Element *root)
2777     DOMString fullJid     = root->getTagAttribute("presence", "from");
2778     DOMString to          = root->getTagAttribute("presence", "to");
2779     DOMString presenceStr = root->getTagAttribute("presence", "type");
2780     bool presence = true;
2781     if (presenceStr == "unavailable")
2782         presence = false;
2783     DOMString status      = root->getTagValue("status");
2784     DOMString show        = root->getTagValue("show");
2786     if (isGroupChat(root))
2787         {
2788         DOMString fromGid;
2789         DOMString fromNick;
2790         parseJid(fullJid, fromGid, fromNick);
2791         //printf("fromGid:%s  fromNick:%s\n",
2792         //        fromGid.c_str(), fromNick.c_str());
2793         DOMString item_jid = root->getTagAttribute("item", "jid");
2794         if (item_jid == jid) //Me
2795             {
2796             if (presence)
2797                 {
2798                 groupChatCreate(fromGid);
2799                 groupChatUserAdd(fromGid, fromNick, "");
2800                 groupChatUserShow(fromGid, fromNick, "available");
2802                 XmppEvent event(XmppEvent::EVENT_MUC_JOIN);
2803                 event.setGroup(fromGid);
2804                 event.setFrom(fromNick);
2805                 event.setPresence(presence);
2806                 event.setShow(show);
2807                 event.setStatus(status);
2808                 dispatchXmppEvent(event);
2809                 }
2810             else
2811                 {
2812                 groupChatDelete(fromGid);
2813                 groupChatUserDelete(fromGid, fromNick);
2815                 XmppEvent event(XmppEvent::EVENT_MUC_LEAVE);
2816                 event.setGroup(fromGid);
2817                 event.setFrom(fromNick);
2818                 event.setPresence(presence);
2819                 event.setShow(show);
2820                 event.setStatus(status);
2821                 dispatchXmppEvent(event);
2822                 }
2823             }
2824         else // someone else
2825             {
2826             if (presence)
2827                 {
2828                 groupChatUserAdd(fromGid, fromNick, "");
2829                 }
2830             else
2831                 groupChatUserDelete(fromGid, fromNick);
2832             groupChatUserShow(fromGid, fromNick, show);
2833             XmppEvent event(XmppEvent::EVENT_MUC_PRESENCE);
2834             event.setGroup(fromGid);
2835             event.setFrom(fromNick);
2836             event.setPresence(presence);
2837             event.setShow(show);
2838             event.setStatus(status);
2839             dispatchXmppEvent(event);
2840             }
2841         }
2842     else
2843         {
2844         DOMString shortJid;
2845         DOMString dummy;
2846         parseJid(fullJid, shortJid, dummy);
2847         rosterShow(shortJid, show); //users in roster do not have resource
2849         XmppEvent event(XmppEvent::EVENT_PRESENCE);
2850         event.setFrom(fullJid);
2851         event.setPresence(presence);
2852         event.setShow(show);
2853         event.setStatus(status);
2854         dispatchXmppEvent(event);
2855         }
2857     return true;
2862 bool XmppClient::processIq(Element *root)
2864     DOMString from  = root->getTagAttribute("iq", "from");
2865     DOMString id    = root->getTagAttribute("iq", "id");
2866     DOMString type  = root->getTagAttribute("iq", "type");
2867     DOMString xmlns = root->getTagAttribute("query", "xmlns");
2869     if (id.size()<1)
2870         return true;
2872     //Group chat
2873     if (strIndex(xmlns, "http://jabber.org/protocol/muc") >=0 )
2874         {
2875         printf("results of MUC query\n");
2876         }
2877     //printf("###IQ xmlns:%s\n", xmlns.c_str());
2879     //### FILE TRANSFERS
2880     DOMString siNamespace = "http://jabber.org/protocol/si";
2881     if (root->getTagAttribute("si", "xmlns") == siNamespace)
2882         {
2883         if (type == "set")
2884             {
2885             DOMString streamId = root->getTagAttribute("si", "id");
2886             DOMString fname    = root->getTagAttribute("file", "name");
2887             DOMString sizeStr  = root->getTagAttribute("file", "size");
2888             DOMString hash     = root->getTagAttribute("file", "hash");
2889             XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_RECEIVE);
2890             event.setFrom(from);
2891             event.setIqId(id);
2892             event.setStreamId(streamId);
2893             event.setFileName(fname);
2894             event.setFileHash(hash);
2895             event.setFileSize(atol(sizeStr.c_str()));
2896             dispatchXmppEvent(event);
2897             }
2898         else  //result
2899             {
2900             printf("Got result\n");
2901             for (int i=0 ; i<fileSendCount ; i++)
2902                 {
2903                 XmppStream *outf = fileSends[i];
2904                 if (outf->getIqId() == id &&
2905                     from == outf->getPeerId())
2906                     {
2907                     if (type == "error")
2908                         {
2909                         outf->setState(STREAM_ERROR);
2910                         error("user '%s' rejected file", from.c_str());
2911                         return true;
2912                         }
2913                     else if (type == "result")
2914                         {
2915                         if (outf->getState() == STREAM_OPENING)
2916                             {
2917                             XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_ACCEPTED);
2918                             event.setFrom(from);
2919                             dispatchXmppEvent(event);
2920                             outf->setState(STREAM_OPEN);
2921                             }
2922                         else if (outf->getState() == STREAM_CLOSING)
2923                             {
2924                             outf->setState(STREAM_CLOSED);
2925                             }
2926                         return true;
2927                         }
2928                     }
2929                 }
2930             }
2931         return true;
2932         }
2935     //### IN-BAND BYTESTREAMS
2936     //### Incoming stream requests
2937     DOMString ibbNamespace = "http://jabber.org/protocol/ibb";
2938     if (root->getTagAttribute("open", "xmlns") == ibbNamespace)
2939         {
2940         DOMString streamId = root->getTagAttribute("open", "sid");
2941         XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_INIT);
2942         dispatchXmppEvent(event);
2943         if (streamId.size()>0)
2944             {
2945             for (int i=0 ; i<inputStreamCount ; i++)
2946                 {
2947                 XmppStream *ins = inputStreams[i];
2948                 if (ins->getStreamId() == streamId)
2949                     {
2950                     ins->setState(STREAM_OPENING);
2951                     ins->setIqId(id);
2952                     return true;
2953                     }
2954                 }
2955             }
2956         return true;
2957         }
2958     else if (root->getTagAttribute("close", "xmlns") == ibbNamespace)
2959         {
2960         XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_CLOSE);
2961         dispatchXmppEvent(event);
2962         DOMString streamId = root->getTagAttribute("close", "sid");
2963         if (streamId.size()>0)
2964             {
2965             for (int i=0 ; i<inputStreamCount ; i++)
2966                 {
2967                 XmppStream *ins = inputStreams[i];
2968                 if (ins->getStreamId() == streamId &&
2969                     from == ins->getPeerId())
2970                     {
2971                     ins->setState(STREAM_CLOSING);
2972                     ins->setIqId(id);
2973                     return true;
2974                     }
2975                 }
2976             }
2977         return true;
2978         }
2979     //### Responses to outgoing requests
2980     for (int i=0 ; i<outputStreamCount ; i++)
2981         {
2982         XmppStream *outs = outputStreams[i];
2983         if (outs->getIqId() == id)
2984             {
2985             if (type == "error")
2986                 {
2987                 outs->setState(STREAM_ERROR);
2988                 return true;
2989                 }
2990             else if (type == "result")
2991                 {
2992                 if (outs->getState() == STREAM_OPENING)
2993                     {
2994                     outs->setState(STREAM_OPEN);
2995                     }
2996                 else if (outs->getState() == STREAM_CLOSING)
2997                     {
2998                     outs->setState(STREAM_CLOSED);
2999                     }
3000                 return true;
3001                 }
3002             }
3003         }
3005     //###Normal Roster stuff
3006     if (root->getTagAttribute("query", "xmlns") == "jabber:iq:roster")
3007         {
3008         roster.clear();
3009         std::vector<Element *>elems = root->findElements("item");
3010         for (unsigned int i=0 ; i<elems.size() ; i++)
3011             {
3012             Element *item = elems[i];
3013             DOMString userJid      = item->getAttribute("jid");
3014             DOMString name         = item->getAttribute("name");
3015             DOMString subscription = item->getAttribute("subscription");
3016             DOMString group        = item->getTagValue("group");
3017             //printf("jid:%s name:%s sub:%s group:%s\n", userJid.c_str(), name.c_str(),
3018             //        subscription.c_str(), group.c_str());
3019             XmppUser user(userJid, name, subscription, group);
3020             roster.push_back(user);
3021             }
3022         XmppEvent event(XmppEvent::XmppEvent::EVENT_ROSTER);
3023         dispatchXmppEvent(event);
3024         }
3026     return true;
3031 bool XmppClient::receiveAndProcess()
3033     if (!keepGoing)
3034         return false;
3036     Parser parser;
3038     DOMString recvBuf = readStanza();
3039     recvBuf = trim(recvBuf);
3040     if (recvBuf.size() < 1)
3041         return true;
3043     //Ugly hack.  Apparently the first char can be dropped on timeouts
3044     //if (recvBuf[0] != '<')
3045     //    recvBuf.insert(0, "<");
3047     status("RECV: %s", recvBuf.c_str());
3048     Element *root = parser.parse(recvBuf);
3049     if (!root)
3050         {
3051         printf("Bad elem\n");
3052         return true;
3053         }
3055     //#### MESSAGE
3056     std::vector<Element *>elems = root->findElements("message");
3057     if (elems.size()>0)
3058         {
3059         if (!processMessage(root))
3060             return false;
3061         }
3063     //#### PRESENCE
3064     elems = root->findElements("presence");
3065     if (elems.size()>0)
3066         {
3067         if (!processPresence(root))
3068             return false;
3069         }
3071     //#### INFO
3072     elems = root->findElements("iq");
3073     if (elems.size()>0)
3074         {
3075         if (!processIq(root))
3076             return false;
3077         }
3079     delete root;
3081     return true;
3085 bool XmppClient::receiveAndProcessLoop()
3087     keepGoing = true;
3088     while (true)
3089         {
3090         if (!keepGoing)
3091             {
3092             printf("Abort requested\n");
3093             break;
3094             }
3095         if (!receiveAndProcess())
3096             return false;
3097         }
3098     return true;
3101 //#######################
3102 //# SENDING
3103 //#######################
3105 bool XmppClient::write(char *fmt, ...)
3107     va_list args;
3108     va_start(args,fmt);
3109     vsnprintf((char *)writeBuf, writeBufLen, fmt,args);
3110     va_end(args) ;
3111     status("SEND: %s", writeBuf);
3112     if (!sock->write((char *)writeBuf))
3113         {
3114         error("Cannot write to socket");
3115         return false;
3116         }
3117     return true;
3120 //#######################
3121 //# CONNECT
3122 //#######################
3124 bool XmppClient::checkConnect()
3126     if (!connected)
3127         {
3128         XmppEvent evt(XmppEvent::EVENT_ERROR);
3129         evt.setData("Attempted operation while disconnected");
3130         dispatchXmppEvent(evt);
3131         return false;
3132         }
3133     return true;
3136 bool XmppClient::iqAuthenticate(const DOMString &streamId)
3138     Parser parser;
3140     char *fmt =
3141     "<iq type='get' to='%s' id='auth%d'>"
3142     "<query xmlns='jabber:iq:auth'><username>%s</username></query>"
3143     "</iq>\n";
3144     if (!write(fmt, realm.c_str(), msgId++, username.c_str()))
3145         return false;
3147     DOMString recbuf = readStanza();
3148     //printf("iq received: '%s'\n", recbuf.c_str());
3149     Element *elem = parser.parse(recbuf);
3150     //elem->print();
3151     DOMString iqType = elem->getTagAttribute("iq", "type");
3152     //printf("##iqType:%s\n", iqType.c_str());
3153     delete elem;
3155     if (iqType != "result")
3156         {
3157         error("error:server does not allow login");
3158         return false;
3159         }
3161     bool digest = true;
3162     if (digest)
3163         {
3164         //## Digest authentication
3165         DOMString digest = streamId;
3166         digest.append(password);
3167         digest = Sha1::hashHex((unsigned char *)digest.c_str(), digest.size());
3168         //printf("digest:%s\n", digest.c_str());
3169         fmt =
3170         "<iq type='set' id='auth%d'>"
3171         "<query xmlns='jabber:iq:auth'>"
3172         "<username>%s</username>"
3173         "<digest>%s</digest>"
3174         "<resource>%s</resource>"
3175         "</query>"
3176         "</iq>\n";
3177         if (!write(fmt, msgId++, username.c_str(),
3178                     digest.c_str(), resource.c_str()))
3179             return false;
3180         }
3181     else
3182         {
3184         //## Plaintext authentication
3185         fmt =
3186         "<iq type='set' id='auth%d'>"
3187         "<query xmlns='jabber:iq:auth'>"
3188         "<username>%s</username>"
3189         "<password>%s</password>"
3190         "<resource>%s</resource>"
3191         "</query>"
3192         "</iq>\n";
3193         if (!write(fmt, msgId++, username.c_str(),
3194                    password.c_str(), resource.c_str()))
3195             return false;
3196         }
3198     recbuf = readStanza();
3199     //printf("iq received: '%s'\n", recbuf.c_str());
3200     elem = parser.parse(recbuf);
3201     //elem->print();
3202     iqType = elem->getTagAttribute("iq", "type");
3203     //printf("##iqType:%s\n", iqType.c_str());
3204     delete elem;
3206     if (iqType != "result")
3207         {
3208         error("server does not allow login");
3209         return false;
3210         }
3212     return true;
3216 static bool
3217 saslParse(const DOMString &s, std::map<DOMString, DOMString> &vals)
3220     vals.clear();
3222     int p  = 0;
3223     int siz = s.size();
3225     while (p < siz)
3226         {
3227         DOMString key;
3228         DOMString value;
3229         char ch = '\0';
3231         //# Parse key
3232         while (p<siz)
3233             {
3234             ch = s[p++];
3235             if (ch == '=')
3236                 break;
3237             key.push_back(ch);
3238             }
3240         //No value?
3241         if (ch != '=')
3242             break;
3244         //# Parse value
3245         bool quoted = false;
3246         while (p<siz)
3247             {
3248             ch = s[p++];
3249             if (ch == '"')
3250                 quoted = !quoted;
3251             else if (ch == ',' && !quoted)
3252                 break;
3253             else
3254                 value.push_back(ch);
3255             }
3257         //printf("# Key: '%s'  Value: '%s'\n", key.c_str(), value.c_str());
3258         vals[key] = value;
3259         if (ch != ',')
3260             break;
3261         }
3263     return true;
3270 bool XmppClient::saslMd5Authenticate()
3272     Parser parser;
3273     char *fmt =
3274     "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
3275     "mechanism='DIGEST-MD5'/>\n";
3276     if (!write(fmt))
3277         return false;
3279     DOMString recbuf = readStanza();
3280     status("challenge received: '%s'", recbuf.c_str());
3281     Element *elem = parser.parse(recbuf);
3282     //elem->print();
3283     DOMString b64challenge = elem->getTagValue("challenge");
3284     delete elem;
3286     if (b64challenge.size() < 1)
3287         {
3288         error("login: no SASL challenge offered by server");
3289         return false;
3290         }
3291     DOMString challenge = Base64Decoder::decodeToString(b64challenge);
3292     status("challenge:'%s'", challenge.c_str());
3294     std::map<DOMString, DOMString> attrs;
3295     if (!saslParse(challenge, attrs))
3296         {
3297         error("login: error parsing SASL challenge");
3298         return false;
3299         }
3301     DOMString nonce = attrs["nonce"];
3302     if (nonce.size()==0)
3303         {
3304         error("login: no SASL nonce sent by server");
3305         return false;
3306         }
3308     DOMString realm = attrs["realm"];
3309     if (nonce.size()==0)
3310         {
3311         error("login: no SASL realm sent by server");
3312         return false;
3313         }
3315     status("SASL recv nonce: '%s' realm:'%s'\n", nonce.c_str(), realm.c_str());
3317     char idBuf[7];
3318     snprintf(idBuf, 6, "%dsasl", msgId++);
3319     DOMString cnonce = Sha1::hashHex((unsigned char *)idBuf, 7);
3320     DOMString authzid = username; authzid.append("@"); authzid.append(host);
3321     DOMString digest_uri = "xmpp/"; digest_uri.append(host);
3323     //## Make A1
3324     Md5 md5;
3325     md5.append(username);
3326     md5.append(":");
3327     md5.append(realm);
3328     md5.append(":");
3329     md5.append(password);
3330     unsigned char a1tmp[16];
3331     md5.finish(a1tmp);
3332     md5.init();
3333     md5.append(a1tmp, 16);
3334     md5.append(":");
3335     md5.append(nonce);
3336     md5.append(":");
3337     md5.append(cnonce);
3338     md5.append(":");
3339     md5.append(authzid);
3340     DOMString a1 = md5.finishHex();
3341     status("##a1:'%s'", a1.c_str());
3343     //# Make A2
3344     md5.init();
3345     md5.append("AUTHENTICATE:");
3346     md5.append(digest_uri);
3347     DOMString a2 = md5.finishHex();
3348     status("##a2:'%s'", a2.c_str());
3350     //# Now make the response
3351     md5.init();
3352     md5.append(a1);
3353     md5.append(":");
3354     md5.append(nonce);
3355     md5.append(":");
3356     md5.append("00000001");//nc
3357     md5.append(":");
3358     md5.append(cnonce);
3359     md5.append(":");
3360     md5.append("auth");//qop
3361     md5.append(":");
3362     md5.append(a2);
3363     DOMString response = md5.finishHex();
3365     DOMString resp;
3366     resp.append("username=\""); resp.append(username); resp.append("\",");
3367     resp.append("realm=\"");    resp.append(realm);    resp.append("\",");
3368     resp.append("nonce=\"");    resp.append(nonce);    resp.append("\",");
3369     resp.append("cnonce=\"");   resp.append(cnonce);   resp.append("\",");
3370     resp.append("nc=00000001,qop=auth,");
3371     resp.append("digest-uri=\""); resp.append(digest_uri); resp.append("\"," );
3372     resp.append("authzid=\"");  resp.append(authzid);  resp.append("\",");
3373     resp.append("response=\""); resp.append(response); resp.append("\",");
3374     resp.append("charset=utf-8");
3375     status("sending response:'%s'", resp.c_str());
3376     resp = Base64Encoder::encode(resp);
3377     status("base64 response:'%s'", resp.c_str());
3378     fmt =
3379     "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>%s</response>\n";
3380     if (!write(fmt, resp.c_str()))
3381         return false;
3383     recbuf = readStanza();
3384     status("server says:: '%s'", recbuf.c_str());
3385     elem = parser.parse(recbuf);
3386     //elem->print();
3387     b64challenge = elem->getTagValue("challenge");
3388     delete elem;
3390     if (b64challenge.size() < 1)
3391         {
3392         error("login: no second SASL challenge offered by server");
3393         return false;
3394         }
3396     challenge = Base64Decoder::decodeToString(b64challenge);
3397     status("challenge: '%s'", challenge.c_str());
3399     if (!saslParse(challenge, attrs))
3400         {
3401         error("login: error parsing SASL challenge");
3402         return false;
3403         }
3405     DOMString rspauth = attrs["rspauth"];
3406     if (rspauth.size()==0)
3407         {
3408         error("login: no SASL respauth sent by server\n");
3409         return false;
3410         }
3412     fmt =
3413     "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>\n";
3414     if (!write(fmt))
3415         return false;
3417     recbuf = readStanza();
3418     status("SASL recv: '%s", recbuf.c_str());
3419     elem = parser.parse(recbuf);
3420     //elem->print();
3421     b64challenge = elem->getTagValue("challenge");
3422     bool success = (elem->findElements("success").size() > 0);
3423     delete elem;
3425     return success;
3428 bool XmppClient::saslPlainAuthenticate()
3430     Parser parser;
3432     DOMString id = username;
3433     //id.append("@");
3434     //id.append(host);
3435     Base64Encoder encoder;
3436     encoder.append('\0');
3437     encoder.append(id);
3438     encoder.append('\0');
3439     encoder.append(password);
3440     DOMString base64Auth = encoder.finish();
3441     //printf("authbuf:%s\n", base64Auth.c_str());
3443     char *fmt =
3444     "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
3445     "mechanism='PLAIN'>%s</auth>\n";
3446     if (!write(fmt, base64Auth.c_str()))
3447         return false;
3448     DOMString recbuf = readStanza();
3449     status("challenge received: '%s'", recbuf.c_str());
3450     Element *elem = parser.parse(recbuf);
3452     bool success = (elem->findElements("success").size() > 0);
3453     delete elem;
3455     return success;
3460 bool XmppClient::saslAuthenticate()
3462     Parser parser;
3464     DOMString recbuf = readStanza();
3465     status("RECV: '%s'\n", recbuf.c_str());
3466     Element *elem = parser.parse(recbuf);
3467     //elem->print();
3469     //Check for starttls
3470     bool wantStartTls = false;
3471     if (elem->findElements("starttls").size() > 0)
3472         {
3473         wantStartTls = true;
3474         if (elem->findElements("required").size() > 0)
3475             status("login: STARTTLS required");
3476         else
3477             status("login: STARTTLS available");
3478         }
3480     if (wantStartTls)
3481         {
3482         delete elem;
3483         char *fmt =
3484         "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n";
3485         if (!write(fmt))
3486             return false;
3487         recbuf = readStanza();
3488         status("RECV: '%s'\n", recbuf.c_str());
3489         elem = parser.parse(recbuf);
3490         if (elem->getTagAttribute("proceed", "xmlns").size()<1)
3491             {
3492             error("Server rejected TLS negotiation");
3493             disconnect();
3494             return false;
3495             }
3496         delete elem;
3497         if (!sock->startTls())
3498             {
3499             error("Could not start TLS");
3500             disconnect();
3501             return false;
3502             }
3504         fmt =
3505          "<stream:stream xmlns='jabber:client' "
3506          "xmlns:stream='http://etherx.jabber.org/streams' "
3507          "to='%s' version='1.0'>\n\n";
3508         if (!write(fmt, realm.c_str()))
3509             return false;
3511         recbuf = readStanza();
3512         status("RECVx: '%s'", recbuf.c_str());
3513         recbuf.append("</stream:stream>");
3514         elem = parser.parse(recbuf);
3515         bool success =
3516         (elem->getTagAttribute("stream:stream", "id").size()>0);
3517         if (!success)
3518             {
3519             error("STARTTLS negotiation failed");
3520             disconnect();
3521             return false;
3522             }
3523         delete elem;
3524         recbuf = readStanza();
3525         status("RECV: '%s'\n", recbuf.c_str());
3526         elem = parser.parse(recbuf);
3527         }
3529     //register, if user requests
3530     if (doRegister)
3531         {
3532         if (!inBandRegistration())
3533             return false;
3534         }
3536     //check for sasl authentication mechanisms
3537     std::vector<Element *> elems =
3538                elem->findElements("mechanism");
3539     if (elems.size() < 1)
3540         {
3541         error("login: no SASL mechanism offered by server");
3542         return false;
3543         }
3544     bool md5Found = false;
3545     bool plainFound = false;
3546     for (unsigned int i=0 ; i<elems.size() ; i++)
3547         {
3548         DOMString mech = elems[i]->getValue();
3549         if (mech == "DIGEST-MD5")
3550             {
3551             status("MD5 authentication offered");
3552             md5Found = true;
3553             }
3554         else if (mech == "PLAIN")
3555             {
3556             status("PLAIN authentication offered");
3557             plainFound = true;
3558             }
3559         }
3560     delete elem;
3562     bool success = false;
3563     if (md5Found)
3564         {
3565         success = saslMd5Authenticate();
3566         }
3567     else if (plainFound)
3568         {
3569         success = saslPlainAuthenticate();
3570         }
3571     else
3572         {
3573         error("not able to handle sasl authentication mechanisms");
3574         return false;
3575         }
3577     if (success)
3578         status("###### SASL authentication success\n");
3579     else
3580         error("###### SASL authentication failure\n");
3582     return success;
3586 /**
3587  * Perform JEP-077 In-Band Registration
3588  */
3589 bool XmppClient::inBandRegistration()
3591     Parser parser;
3593     char *fmt =
3594      "<iq type='get' id='reg1'>"
3595          "<query xmlns='jabber:iq:register'/>"
3596          "</iq>\n\n";
3597     if (!write(fmt))
3598         return false;
3600     DOMString recbuf = readStanza();
3601     status("RECV reg: %s", recbuf.c_str());
3602     Element *elem = parser.parse(recbuf);
3603     elem->print();
3605     //# does the entity send the "instructions" tag?
3606     bool hasInstructions =
3607          (elem->findElements("instructions").size() > 0);
3608     delete elem;
3610     if (!hasInstructions)
3611         {
3612         error("server did not offer registration");
3613         return false;
3614         }
3616     fmt =
3617      "<iq type='set' id='reg2'>"
3618          "<query xmlns='jabber:iq:register'>"
3619          "<username>%s</username>"
3620          "<password>%s</password>"
3621          "</query>"
3622          "</iq>\n\n";
3623     if (!write(fmt, toXml(username).c_str(), toXml(password).c_str() ))
3624         return false;
3627     recbuf = readStanza();
3628     status("RECV reg: %s", recbuf.c_str());
3629     elem = parser.parse(recbuf);
3630     elem->print();
3632     std::vector<Element *> list = elem->findElements("error");
3633     if (list.size()>0)
3634         {
3635         Element *errElem = list[0];
3636         DOMString code = errElem->getAttribute("code");
3637         DOMString errMsg = "Registration error. ";
3638         if (code == "409")
3639             {
3640             errMsg.append("conflict with existing user name");
3641             }
3642         if (code == "406")
3643             {
3644             errMsg.append("some registration information was not provided");
3645             }
3646         error((char *)errMsg.c_str());
3647         delete elem;
3648         return false;
3649         }
3651     delete elem;
3653     XmppEvent evt(XmppEvent::EVENT_REGISTRATION_NEW);
3654     dispatchXmppEvent(evt);
3656     return true;
3660 bool XmppClient::createSession()
3663     Parser parser;
3664     if (port==443 || port==5223)
3665         sock->enableSSL(true);
3666     if (!sock->connect(host, port))
3667         {
3668         return false;
3669         }
3671     char *fmt =
3672      "<stream:stream "
3673           "to='%s' "
3674           "xmlns='jabber:client' "
3675           "xmlns:stream='http://etherx.jabber.org/streams' "
3676           "version='1.0'>\n\n";
3677     if (!write(fmt, realm.c_str()))
3678         return false;
3680     DOMString recbuf = readStanza();
3681     //printf("received: '%s'\n", recbuf.c_str());
3682     recbuf.append("</stream:stream>");
3683     Element *elem = parser.parse(recbuf);
3684     //elem->print();
3685     bool useSasl = false;
3686     DOMString streamId = elem->getTagAttribute("stream:stream", "id");
3687     //printf("### StreamID: %s\n", streamId.c_str());
3688     DOMString streamVersion = elem->getTagAttribute("stream:stream", "version");
3689     if (streamVersion == "1.0")
3690         useSasl = true;
3692     if (useSasl)
3693         {
3694         if (!saslAuthenticate())
3695             return false;
3696         fmt =
3697           "<stream:stream "
3698           "to='%s' "
3699           "xmlns='jabber:client' "
3700           "xmlns:stream='http://etherx.jabber.org/streams' "
3701           "version='1.0'>\n\n";
3703         if (!write(fmt, realm.c_str()))
3704             return false;
3705         recbuf = readStanza();
3706         recbuf.append("</stream:stream>\n");
3707         //printf("now server says:: '%s'\n", recbuf.c_str());
3708         elem = parser.parse(recbuf);
3709         //elem->print();
3710         delete elem;
3712         recbuf = readStanza();
3713         //printf("now server says:: '%s'\n", recbuf.c_str());
3714         elem = parser.parse(recbuf);
3715         bool hasBind = (elem->findElements("bind").size() > 0);
3716         //elem->print();
3717         delete elem;
3719         if (!hasBind)
3720             {
3721             error("no binding provided by server");
3722             return false;
3723             }
3726         }
3727     else // not SASL
3728         {
3729         if (!iqAuthenticate(streamId))
3730             return false;
3731         }
3734     //### Resource binding
3735     fmt =
3736     "<iq type='set' id='bind%d'>"
3737     "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
3738     "<resource>%s</resource>"
3739     "</bind></iq>\n";
3740     if (!write(fmt, msgId++, resource.c_str()))
3741         return false;
3743     recbuf = readStanza();
3744     status("bind result: '%s'", recbuf.c_str());
3745     elem = parser.parse(recbuf);
3746     //elem->print();
3747     DOMString bindType = elem->getTagAttribute("iq", "type");
3748     //printf("##bindType:%s\n", bindType.c_str());
3749     delete elem;
3751     if (bindType != "result")
3752         {
3753         error("no binding with server failed");
3754         return false;
3755         }
3757     fmt =
3758     "<iq type='set' id='sess%d'>"
3759     "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
3760     "</iq>\n";
3761     if (!write(fmt, msgId++))
3762         return false;
3764     recbuf = readStanza();
3765     status("session received: '%s'", recbuf.c_str());
3766     elem = parser.parse(recbuf);
3767     //elem->print();
3768     DOMString sessionType = elem->getTagAttribute("iq", "type");
3769     //printf("##sessionType:%s\n", sessionType.c_str());
3770     delete elem;
3772     if (sessionType != "result")
3773         {
3774         error("no session provided by server");
3775         return false;
3776         }
3778     //printf("########## COOL #########\n");
3779     //Now that we are bound, we have a valid JID
3780     jid = username;
3781     jid.append("@");
3782     jid.append(realm);
3783     jid.append("/");
3784     jid.append(resource);
3786     //We are now done with the synchronous handshaking.  Let's go into
3787     //async mode
3789     fmt =
3790      "<iq type='get' id='roster%d'><query xmlns='jabber:iq:roster'/></iq>\n";
3791     if (!write(fmt, msgId++))
3792         return false;
3794     fmt =
3795      "<iq type='get' id='discoItems%d' to='%s'>"
3796      "<query xmlns='http://jabber.org/protocol/disco#items'/></iq>\n";
3797     if (!write(fmt, msgId++, realm.c_str()))
3798         return false;
3800     fmt =
3801     "<iq type='get' id='discoInfo%d' to='conference.%s'>"
3802     "<query xmlns='http://jabber.org/protocol/disco#info'/></iq>\n";
3803     if (!write(fmt, msgId++, realm.c_str()))
3804         return false;
3806     fmt =
3807      "<presence/>\n";
3808     if (!write(fmt))
3809         return false;
3811     /*
3812     recbuf = readStanza();
3813     status("stream received: '%s'", recbuf.c_str());
3814     elem = parser.parse(recbuf);
3815     //elem->print();
3816     delete elem;
3817     */
3819     //We are now logged in
3820     status("Connected");
3821     connected = true;
3822     XmppEvent evt(XmppEvent::EVENT_CONNECTED);
3823     dispatchXmppEvent(evt);
3824     //Thread::sleep(1000000);
3826     sock->setReceiveTimeout(1000);
3827     ReceiverThread runner(*this);
3828     Thread thread(runner);
3829     thread.start();
3831     return true;
3834 bool XmppClient::connect()
3836     if (!createSession())
3837         {
3838         disconnect();
3839         return false;
3840         }
3841     return true;
3845 bool XmppClient::connect(DOMString hostArg, int portArg,
3846                          DOMString usernameArg,
3847                          DOMString passwordArg,
3848                          DOMString resourceArg)
3850     host     = hostArg;
3851     port     = portArg;
3852     password = passwordArg;
3853     resource = resourceArg;
3855     //parse this one
3856     setUsername(usernameArg);
3858     bool ret = connect();
3859     return ret;
3862 bool XmppClient::disconnect()
3864     if (connected)
3865         {
3866         char *fmt =
3867         "<presence from='%s' type='unavailable'/>\n";
3868         write(fmt, jid.c_str());
3869         }
3870     keepGoing = false;
3871     connected = false;
3872     Thread::sleep(3000); //allow receiving thread to quit
3873     sock->disconnect();
3874     roster.clear();
3875     groupChatsClear();
3876     XmppEvent event(XmppEvent::EVENT_DISCONNECTED);
3877     dispatchXmppEvent(event);
3878     return true;
3881 //#######################
3882 //# ROSTER
3883 //#######################
3885 bool XmppClient::rosterAdd(const DOMString &rosterGroup,
3886                            const DOMString &otherJid,
3887                            const DOMString &name)
3889     if (!checkConnect())
3890         return false;
3891     char *fmt =
3892     "<iq from='%s' type='set' id='roster_%d'>"
3893     "<query xmlns='jabber:iq:roster'>"
3894     "<item jid='%s' name='%s'><group>%s</group></item>"
3895     "</query></iq>\n";
3896     if (!write(fmt, jid.c_str(), msgId++, otherJid.c_str(),
3897          name.c_str(), rosterGroup.c_str()))
3898         {
3899         return false;
3900         }
3901     return true;
3904 bool XmppClient::rosterDelete(const DOMString &otherJid)
3906     if (!checkConnect())
3907         return false;
3908     char *fmt =
3909     "<iq from='%s' type='set' id='roster_%d'>"
3910     "<query xmlns='jabber:iq:roster'>"
3911     "<item jid='%s' subscription='remove'><group>%s</group></item>"
3912     "</query></iq>\n";
3913     if (!write(fmt, jid.c_str(), msgId++, otherJid.c_str()))
3914         {
3915         return false;
3916         }
3917     return true;
3921 static bool xmppRosterCompare(const XmppUser& p1, const XmppUser& p2)
3923     DOMString s1 = p1.group;
3924     DOMString s2 = p2.group;
3925     for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
3926         {
3927         int comp = tolower(s1[len]) - tolower(s2[len]);
3928         if (comp)
3929             return (comp<0);
3930         }
3932     s1 = p1.jid;
3933     s2 = p2.jid;
3934     for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
3935         {
3936         int comp = tolower(s1[len]) - tolower(s2[len]);
3937         if (comp)
3938             return (comp<0);
3939         }
3940     return false;
3943 std::vector<XmppUser> XmppClient::getRoster()
3945     std::vector<XmppUser> ros = roster;
3946     std::sort(ros.begin(), ros.end(), xmppRosterCompare);
3947     return ros;
3950 void XmppClient::rosterShow(const DOMString &jid, const DOMString &show)
3952     DOMString theShow = show;
3953     if (theShow == "")
3954         theShow = "available";
3956     std::vector<XmppUser>::iterator iter;
3957     for (iter=roster.begin() ; iter != roster.end() ; iter++)
3958         {
3959         if (iter->jid == jid)
3960             iter->show = theShow;
3961         }
3964 //#######################
3965 //# CHAT (individual)
3966 //#######################
3968 bool XmppClient::message(const DOMString &user, const DOMString &subj,
3969                          const DOMString &msg)
3971     if (!checkConnect())
3972         return false;
3974     DOMString xmlSubj = toXml(subj);
3975     DOMString xmlMsg  = toXml(msg);
3977     if (xmlSubj.size() > 0)
3978         {
3979         char *fmt =
3980         "<message from='%s' to='%s' type='chat'>"
3981         "<subject>%s</subject><body>%s</body></message>\n";
3982         if (!write(fmt, jid.c_str(), user.c_str(),
3983                 xmlSubj.c_str(), xmlMsg.c_str()))
3984             return false;
3985         }
3986     else
3987         {
3988         char *fmt =
3989         "<message from='%s' to='%s'>"
3990         "<body>%s</body></message>\n";
3991         if (!write(fmt, jid.c_str(), user.c_str(), xmlMsg.c_str()))
3992             return false;
3993         }
3994     return true;
3999 bool XmppClient::message(const DOMString &user, const DOMString &msg)
4001     return message(user, "", msg);
4006 bool XmppClient::presence(const DOMString &presence)
4008     if (!checkConnect())
4009         return false;
4011     DOMString xmlPres = toXml(presence);
4013     char *fmt =
4014     "<presence from='%s'><show>%s</show></presence>\n";
4015     if (!write(fmt, jid.c_str(), xmlPres.c_str()))
4016         return false;
4017     return true;
4020 //#######################
4021 //# GROUP  CHAT
4022 //#######################
4024 bool XmppClient::groupChatCreate(const DOMString &groupJid)
4026     std::vector<XmppGroupChat *>::iterator iter;
4027     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
4028         {
4029         if ((*iter)->getGroupJid() == groupJid)
4030             {
4031             error("Group chat '%s' already exists", groupJid.c_str());
4032             return false;
4033             }
4034         }
4035     XmppGroupChat *chat = new XmppGroupChat(groupJid);
4036     groupChats.push_back(chat);
4037     return true;
4040 /**
4041  *
4042  */
4043 void XmppClient::groupChatDelete(const DOMString &groupJid)
4045     std::vector<XmppGroupChat *>::iterator iter;
4046     for (iter=groupChats.begin() ; iter!=groupChats.end() ; )
4047         {
4048         XmppGroupChat *chat = *iter;
4049         if (chat->getGroupJid() == groupJid)
4050             {
4051             iter = groupChats.erase(iter);
4052             delete chat;
4053             }
4054         else
4055             iter++;
4056         }
4059 /**
4060  *
4061  */
4062 bool XmppClient::groupChatExists(const DOMString &groupJid)
4064     std::vector<XmppGroupChat *>::iterator iter;
4065     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
4066         if ((*iter)->getGroupJid() == groupJid)
4067             return true;
4068     return false;
4071 /**
4072  *
4073  */
4074 void XmppClient::groupChatsClear()
4076     std::vector<XmppGroupChat *>::iterator iter;
4077     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
4078         delete (*iter);
4079     groupChats.clear();
4083 /**
4084  *
4085  */
4086 void XmppClient::groupChatUserAdd(const DOMString &groupJid,
4087                                   const DOMString &nick,
4088                                   const DOMString &jid)
4090     std::vector<XmppGroupChat *>::iterator iter;
4091     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
4092         {
4093         if ((*iter)->getGroupJid() == groupJid)
4094             {
4095             (*iter)->userAdd(nick, jid);
4096             }
4097         }
4100 /**
4101  *
4102  */
4103 void XmppClient::groupChatUserShow(const DOMString &groupJid,
4104                                    const DOMString &nick,
4105                                    const DOMString &show)
4107     std::vector<XmppGroupChat *>::iterator iter;
4108     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
4109         {
4110         if ((*iter)->getGroupJid() == groupJid)
4111             {
4112             (*iter)->userShow(nick, show);
4113             }
4114         }
4117 /**
4118  *
4119  */
4120 void XmppClient::groupChatUserDelete(const DOMString &groupJid,
4121                                      const DOMString &nick)
4123     std::vector<XmppGroupChat *>::iterator iter;
4124     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
4125         {
4126         if ((*iter)->getGroupJid() == groupJid)
4127             {
4128             (*iter)->userDelete(nick);
4129             }
4130         }
4133 static bool xmppUserCompare(const XmppUser& p1, const XmppUser& p2)
4135     DOMString s1 = p1.nick;
4136     DOMString s2 = p2.nick;
4137     int comp = 0;
4138     for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
4139         {
4140         comp = tolower(s1[len]) - tolower(s2[len]);
4141         if (comp)
4142             break;
4143         }
4144     return (comp<0);
4148 std::vector<XmppUser> XmppClient::groupChatGetUserList(
4149                               const DOMString &groupJid)
4151     if (!checkConnect())
4152         {
4153         std::vector<XmppUser> dummy;
4154         return dummy;
4155         }
4157     std::vector<XmppGroupChat *>::iterator iter;
4158     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
4159         {
4160         if ((*iter)->getGroupJid() == groupJid )
4161             {
4162             std::vector<XmppUser> uList = (*iter)->getUserList();
4163             std::sort(uList.begin(), uList.end(), xmppUserCompare);
4164             return uList;
4165             }
4166         }
4167     std::vector<XmppUser> dummy;
4168     return dummy;
4171 bool XmppClient::groupChatJoin(const DOMString &groupJid,
4172                                const DOMString &nick,
4173                                const DOMString &pass)
4175     if (!checkConnect())
4176         return false;
4178     DOMString user = nick;
4179     if (user.size()<1)
4180         user = username;
4182     char *fmt =
4183     "<presence to='%s/%s'>"
4184     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
4185     if (!write(fmt, groupJid.c_str(), user.c_str()))
4186         return false;
4187     return true;
4191 bool XmppClient::groupChatLeave(const DOMString &groupJid,
4192                                 const DOMString &nick)
4194     if (!checkConnect())
4195         return false;
4197     DOMString user = nick;
4198     if (user.size()<1)
4199         user = username;
4201     char *fmt =
4202     "<presence to='%s/%s' type='unavailable'>"
4203     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
4204     if (!write(fmt, groupJid.c_str(), user.c_str()))
4205         return false;
4206     return true;
4210 bool XmppClient::groupChatMessage(const DOMString &groupJid,
4211                                   const DOMString &msg)
4213     if (!checkConnect())
4214         {
4215         return false;
4216         }
4218     DOMString xmlMsg = toXml(msg);
4220     char *fmt =
4221     "<message from='%s' to='%s' type='groupchat'>"
4222     "<body>%s</body></message>\n";
4223     if (!write(fmt, jid.c_str(), groupJid.c_str(), xmlMsg.c_str()))
4224         return false;
4225     return true;
4228 bool XmppClient::groupChatPrivateMessage(const DOMString &groupJid,
4229                                          const DOMString &toNick,
4230                                          const DOMString &msg)
4232     if (!checkConnect())
4233         return false;
4235     DOMString xmlMsg = toXml(msg);
4237     char *fmt =
4238     "<message from='%s' to='%s/%s' type='chat'>"
4239     "<body>%s</body></message>\n";
4240     if (!write(fmt, jid.c_str(), groupJid.c_str(),
4241                toNick.c_str(), xmlMsg.c_str()))
4242         return false;
4243     return true;
4246 bool XmppClient::groupChatPresence(const DOMString &groupJid,
4247                                    const DOMString &myNick,
4248                                    const DOMString &presence)
4250     if (!checkConnect())
4251         return false;
4253     DOMString user = myNick;
4254     if (user.size()<1)
4255         user = username;
4257     DOMString xmlPresence = toXml(presence);
4259     char *fmt =
4260     "<presence from='%s' to='%s/%s' type='unavailable'>"
4261     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
4262     if (!write(fmt, jid.c_str(), groupJid.c_str(), user.c_str(), xmlPresence.c_str()))
4263         return true;
4264     return true;
4269 //#######################
4270 //# S T R E A M S
4271 //#######################
4274 /**
4275  *
4276  */
4277 int XmppClient::outputStreamOpen(const DOMString &destId,
4278                                  const DOMString &streamIdArg)
4280     int i;
4281     for (i=0; i<outputStreamCount ; i++)
4282         if (outputStreams[i]->getState() == STREAM_AVAILABLE)
4283             break;
4284     if (i>=outputStreamCount)
4285         {
4286         error("No available output streams");
4287         return -1;
4288         }
4289     int streamNr = i;
4290     XmppStream *outs = outputStreams[streamNr];
4292     outs->setState(STREAM_OPENING);
4294     char buf[32];
4295     snprintf(buf, 31, "inband%d", getMsgId());
4296     DOMString iqId = buf;
4298     DOMString streamId = streamIdArg;
4299     if (streamId.size()<1)
4300         {
4301         snprintf(buf, 31, "stream%d", getMsgId());
4302         DOMString streamId = buf;
4303         }
4304     outs->setIqId(iqId);
4305     outs->setStreamId(streamId);
4306     outs->setPeerId(destId);
4308     char *fmt =
4309     "<iq type='set' from='%s' to='%s' id='%s'>"
4310     "<open sid='%s' block-size='4096'"
4311     " xmlns='http://jabber.org/protocol/ibb'/></iq>\n";
4312     if (!write(fmt, jid.c_str(),
4313               destId.c_str(), iqId.c_str(),
4314               streamId.c_str()))
4315         {
4316         outs->reset();
4317         return -1;
4318         }
4320     int state = outs->getState();
4321     for (int tim=0 ; tim<20 ; tim++)
4322         {
4323         if (state == STREAM_OPEN)
4324             break;
4325         else if (state == STREAM_ERROR)
4326             {
4327             printf("ERROR\n");
4328             outs->reset();
4329             return -1;
4330             }
4331         Thread::sleep(1000);
4332         state = outs->getState();
4333         }
4334     if (state != STREAM_OPEN)
4335         {
4336         printf("TIMEOUT ERROR\n");
4337         outs->reset();
4338         return -1;
4339         }
4341     return streamNr;
4344 /**
4345  *
4346  */
4347 int XmppClient::outputStreamWrite(int streamNr,
4348                       const unsigned char *buf, unsigned long len)
4350     XmppStream *outs = outputStreams[streamNr];
4352     unsigned long outLen = 0;
4353     unsigned char *p = (unsigned char *)buf;
4355     while (outLen < len)
4356         {
4357         unsigned long chunksize = 1024;
4358         if (chunksize + outLen > len)
4359             chunksize = len - outLen;
4361         Base64Encoder encoder;
4362         encoder.append(p, chunksize);
4363         DOMString b64data = encoder.finish();
4364         p      += chunksize;
4365         outLen += chunksize;
4367         char *fmt =
4368         "<message from='%s' to='%s' id='msg%d'>"
4369         "<data xmlns='http://jabber.org/protocol/ibb' sid='%s' seq='%d'>"
4370         "%s"
4371         "</data>"
4372         "<amp xmlns='http://jabber.org/protocol/amp'>"
4373         "<rule condition='deliver-at' value='stored' action='error'/>"
4374         "<rule condition='match-resource' value='exact' action='error'/>"
4375         "</amp>"
4376         "</message>\n";
4377         if (!write(fmt, jid.c_str(),
4378               outs->getPeerId().c_str(),
4379               getMsgId(),
4380               outs->getStreamId().c_str(),
4381               outs->getSeqNr(),
4382               b64data.c_str()))
4383             {
4384             outs->reset();
4385             return -1;
4386             }
4387         pause(5000);
4388         }
4389     return outLen;
4392 /**
4393  *
4394  */
4395 int XmppClient::outputStreamClose(int streamNr)
4397     XmppStream *outs = outputStreams[streamNr];
4399     char buf[32];
4400     snprintf(buf, 31, "inband%d", getMsgId());
4401     DOMString iqId = buf;
4402     outs->setIqId(iqId);
4404     outs->setState(STREAM_CLOSING);
4405     char *fmt =
4406     "<iq type='set' from='%s' to='%s' id='%s'>"
4407     "<close sid='%s' xmlns='http://jabber.org/protocol/ibb'/></iq>\n";
4408     if (!write(fmt, jid.c_str(),
4409                     outs->getPeerId().c_str(),
4410                     iqId.c_str(),
4411                     outs->getStreamId().c_str()))
4412         return false;
4414     int state = outs->getState();
4415     for (int tim=0 ; tim<20 ; tim++)
4416         {
4417         if (state == STREAM_CLOSED)
4418             break;
4419         else if (state == STREAM_ERROR)
4420             {
4421             printf("ERROR\n");
4422             outs->reset();
4423             return -1;
4424             }
4425         Thread::sleep(1000);
4426         state = outs->getState();
4427         }
4428     if (state != STREAM_CLOSED)
4429         {
4430         printf("TIMEOUT ERROR\n");
4431         outs->reset();
4432         return -1;
4433         }
4435     outs->reset();
4436     return 1;
4440 /**
4441  *
4442  */
4443 int XmppClient::inputStreamOpen(const DOMString &fromJid, const DOMString &streamId,
4444                                 const DOMString &iqId)
4446     int i;
4447     for (i=0 ; i<inputStreamCount ; i++)
4448         {
4449         if (inputStreams[i]->getState() == STREAM_AVAILABLE)
4450             break;
4451         }
4452     if (i>=inputStreamCount)
4453         {
4454         error("No available input streams");
4455         return -1;
4456         }
4457     int streamNr = i;
4458     XmppStream *ins = inputStreams[streamNr];
4459     ins->reset();
4460     ins->setPeerId(fromJid);
4461     ins->setState(STREAM_CLOSED);
4462     ins->setStreamId(streamId);
4464     int state = ins->getState();
4465     for (int tim=0 ; tim<20 ; tim++)
4466         {
4467         if (state == STREAM_OPENING)
4468             break;
4469         else if (state == STREAM_ERROR)
4470             {
4471             printf("ERROR\n");
4472             ins->reset();
4473             return -1;
4474             }
4475         Thread::sleep(1000);
4476         state = ins->getState();
4477         }
4478     if (state != STREAM_OPENING)
4479         {
4480         printf("TIMEOUT ERROR\n");
4481         ins->reset();
4482         return -1;
4483         }
4484     char *fmt =
4485     "<iq type='result' from='%s' to='%s' id='%s'/>\n";
4486     if (!write(fmt, jid.c_str(),  fromJid.c_str(), ins->getIqId().c_str()))
4487         {
4488         return -1;
4489         }
4491     ins->setState(STREAM_OPEN);
4492     return streamNr;
4497 /**
4498  *
4499  */
4500 int XmppClient::inputStreamAvailable(int streamNr)
4502     XmppStream *ins = inputStreams[streamNr];
4503     return ins->available();
4506 /**
4507  *
4508  */
4509 std::vector<unsigned char> XmppClient::inputStreamRead(int streamNr)
4511     XmppStream *ins = inputStreams[streamNr];
4512     return ins->read();
4515 /**
4516  *
4517  */
4518 bool XmppClient::inputStreamClosing(int streamNr)
4520     XmppStream *ins = inputStreams[streamNr];
4521     if (ins->getState() == STREAM_CLOSING)
4522         return true;
4523     return false;
4527 /**
4528  *
4529  */
4530 int XmppClient::inputStreamClose(int streamNr)
4532     int ret=1;
4533     XmppStream *ins = inputStreams[streamNr];
4534     if (ins->getState() == STREAM_CLOSING)
4535         {
4536         char *fmt =
4537         "<iq type='result' from='%s' to='%s' id='%s'/>\n";
4538         if (!write(fmt, jid.c_str(),  ins->getPeerId().c_str(),
4539                     ins->getIqId().c_str()))
4540             {
4541             ret = -1;
4542             }
4543         }
4544     ins->reset();
4545     return ret;
4551 //#######################
4552 //# FILE   TRANSFERS
4553 //#######################
4556 /**
4557  *
4558  */
4559 bool XmppClient::fileSend(const DOMString &destJidArg,
4560                           const DOMString &offeredNameArg,
4561                           const DOMString &fileNameArg,
4562                           const DOMString &descriptionArg)
4564     DOMString destJid     = destJidArg;
4565     DOMString offeredName = offeredNameArg;
4566     DOMString fileName    = fileNameArg;
4567     DOMString description = descriptionArg;
4569     int i;
4570     for (i=0; i<fileSendCount ; i++)
4571         if (fileSends[i]->getState() == STREAM_AVAILABLE)
4572             break;
4573     if (i>=fileSendCount)
4574         {
4575         error("No available file send streams");
4576         return false;
4577         }
4578     int fileSendNr = i;
4579     XmppStream *outf = fileSends[fileSendNr];
4581     outf->setState(STREAM_OPENING);
4583     struct stat finfo;
4584     if (stat(fileName.c_str(), &finfo)<0)
4585         {
4586         error("Cannot stat file '%s' for sending", fileName.c_str());
4587         return false;
4588         }
4589     long fileLen = finfo.st_size;
4590     if (!fileLen > 1000000)
4591         {
4592         error("'%s' too large", fileName.c_str());
4593         return false;
4594         }
4595     if (!S_ISREG(finfo.st_mode))
4596         {
4597         error("'%s' is not a regular file", fileName.c_str());
4598         return false;
4599         }
4600     FILE *f = fopen(fileName.c_str(), "rb");
4601     if (!f)
4602         {
4603         error("cannot open '%s' for sending", fileName.c_str());
4604         return false;
4605         }
4606     unsigned char *sendBuf = (unsigned char *)malloc(fileLen+1);
4607     if (!sendBuf)
4608         {
4609         error("cannot cannot allocate send buffer for %s", fileName.c_str());
4610         return false;
4611         }
4612     for (long i=0 ; i<fileLen && !feof(f); i++)
4613         {
4614         sendBuf[i] = fgetc(f);
4615         }
4616     fclose(f);
4618     //## get the last path segment from the whole path
4619     if (offeredName.size()<1)
4620         {
4621         int slashPos = -1;
4622         for (unsigned int i=0 ; i<fileName.size() ; i++)
4623             {
4624             int ch = fileName[i];
4625             if (ch == '/' || ch == '\\')
4626                 slashPos = i;
4627             }
4628         if (slashPos>=0 && slashPos<=(int)(fileName.size()-1))
4629             {
4630             offeredName = fileName.substr(slashPos+1,
4631                                           fileName.size()-slashPos-1);
4632             printf("offeredName:%s\n", offeredName.c_str());
4633             }
4634         }
4636     char buf[32];
4637     snprintf(buf, 31, "file%d", getMsgId());
4638     DOMString iqId = buf;
4639     outf->setIqId(iqId);
4641     snprintf(buf, 31, "stream%d", getMsgId());
4642     DOMString streamId = buf;
4643     //outf->setStreamId(streamId);
4645     DOMString hash = Md5::hashHex(sendBuf, fileLen);
4646     printf("Hash:%s\n", hash.c_str());
4648     outf->setPeerId(destJid);
4650     char dtgBuf[81];
4651     struct tm *timeVal = gmtime(&(finfo.st_mtime));
4652     strftime(dtgBuf, 80, "%Y-%m-%dT%H:%M:%Sz", timeVal);
4654     char *fmt =
4655     "<iq type='set' id='%s' to='%s'>"
4656     "<si xmlns='http://jabber.org/protocol/si' id='%s'"
4657       " mime-type='text/plain'"
4658       " profile='http://jabber.org/protocol/si/profile/file-transfer'>"
4659     "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'"
4660           " name='%s' size='%d' hash='%s' date='%s'><desc>%s</desc></file>"
4661     "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
4662     "<x xmlns='jabber:x:data' type='form'>"
4663     "<field var='stream-method' type='list-single'>"
4664     //"<option><value>http://jabber.org/protocol/bytestreams</value></option>"
4665     "<option><value>http://jabber.org/protocol/ibb</value></option>"
4666     "</field></x></feature></si></iq>\n";
4667     if (!write(fmt, iqId.c_str(), destJid.c_str(),
4668          streamId.c_str(), offeredName.c_str(), fileLen,
4669          hash.c_str(), dtgBuf, description.c_str()))
4670         {
4671         free(sendBuf);
4672         return false;
4673         }
4675     int state = outf->getState();
4676     for (int tim=0 ; tim<20 ; tim++)
4677         {
4678         printf("##### waiting for open\n");
4679         if (state == STREAM_OPEN)
4680             {
4681             outf->reset();
4682             break;
4683             }
4684         else if (state == STREAM_ERROR)
4685             {
4686             printf("ERROR\n");
4687             outf->reset();
4688             return false;
4689             }
4690         Thread::sleep(1000);
4691         state = outf->getState();
4692         }
4693     if (state != STREAM_OPEN)
4694         {
4695         printf("TIMEOUT ERROR\n");
4696         outf->reset();
4697         return false;
4698         }
4700     //free up this reqource
4701     outf->reset();
4703     int  streamNr = outputStreamOpen(destJid, streamId);
4704     if (streamNr<0)
4705         {
4706         error("cannot open output stream %s", streamId.c_str());
4707         outf->reset();
4708         return false;
4709         }
4711     int ret = outputStreamWrite(streamNr, sendBuf, fileLen);
4713     if (ret<0)
4714         {
4715         }
4717     outputStreamClose(streamNr);
4719     free(sendBuf);
4720     return true;
4724 class FileSendThread : public Thread
4726 public:
4728     FileSendThread(XmppClient &par,
4729                    const DOMString &destJidArg,
4730                    const DOMString &offeredNameArg,
4731                    const DOMString &fileNameArg,
4732                    const DOMString &descriptionArg) : client(par)
4733         {
4734         destJid     = destJidArg;
4735         offeredName = offeredNameArg;
4736         fileName    = fileNameArg;
4737         description = descriptionArg;
4738         }
4740     virtual ~FileSendThread() {}
4742     void run()
4743       {
4744       client.fileSend(destJid, offeredName,
4745                       fileName, description);
4746       }
4748 private:
4750     XmppClient &client;
4751     DOMString destJid;
4752     DOMString offeredName;
4753     DOMString fileName;
4754     DOMString description;
4755 };
4757 /**
4758  *
4759  */
4760 bool XmppClient::fileSendBackground(const DOMString &destJid,
4761                                     const DOMString &offeredName,
4762                                     const DOMString &fileName,
4763                                     const DOMString &description)
4765     FileSendThread thread(*this, destJid, offeredName,
4766                            fileName, description);
4767     thread.start();
4768     return true;
4772 /**
4773  *
4774  */
4775 bool XmppClient::fileReceive(const DOMString &fromJid,
4776                              const DOMString &iqId,
4777                              const DOMString &streamId,
4778                              const DOMString &fileName,
4779                              long  fileSize,
4780                              const DOMString &fileHash)
4782     char *fmt =
4783     "<iq type='result' to='%s' id='%s'>"
4784     "<si xmlns='http://jabber.org/protocol/si'>"
4785     "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'/>"
4786     "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
4787     "<x xmlns='jabber:x:data' type='submit'>"
4788     "<field var='stream-method'>"
4789     "<value>http://jabber.org/protocol/ibb</value>"
4790     "</field></x></feature></si></iq>\n";
4791     if (!write(fmt, fromJid.c_str(), iqId.c_str()))
4792         {
4793         return false;
4794         }
4796     int streamNr = inputStreamOpen(fromJid, streamId, iqId);
4797     if (streamNr < 0)
4798         {
4799         return false;
4800         }
4803     Md5 md5;
4804     FILE *f = fopen(fileName.c_str(), "wb");
4805     if (!f)
4806         {
4807         return false;
4808         }
4810     while (true)
4811         {
4812         if (inputStreamAvailable(streamNr)<1)
4813             {
4814             if (inputStreamClosing(streamNr))
4815                 break;
4816             pause(100);
4817             continue;
4818             }
4819         std::vector<unsigned char> ret = inputStreamRead(streamNr);
4820         std::vector<unsigned char>::iterator iter;
4821         for (iter=ret.begin() ; iter!=ret.end() ; iter++)
4822             {
4823             unsigned char ch = *iter;
4824             md5.append(&ch, 1);
4825             fwrite(&ch, 1, 1, f);
4826             }
4827         }
4829     inputStreamClose(streamNr);
4830     fclose(f);
4832     DOMString hash = md5.finishHex();
4833     printf("received file hash:%s\n", hash.c_str());
4835     return true;
4840 class FileReceiveThread : public Thread
4842 public:
4844     FileReceiveThread(XmppClient &par,
4845                       const DOMString &fromJidArg,
4846                       const DOMString &iqIdArg,
4847                       const DOMString &streamIdArg,
4848                       const DOMString &fileNameArg,
4849                       long  fileSizeArg,
4850                       const DOMString &fileHashArg) : client(par)
4851         {
4852         fromJid     = fromJidArg;
4853         iqId        = iqIdArg;
4854         streamId    = streamIdArg;
4855         fileName    = fileNameArg;
4856         fileSize    = fileSizeArg;
4857         fileHash    = fileHashArg;
4858         }
4860     virtual ~FileReceiveThread() {}
4862     void run()
4863       {
4864       client.fileReceive(fromJid, iqId, streamId,
4865                         fileName, fileSize, fileHash);
4866       }
4868 private:
4870     XmppClient &client;
4871     DOMString fromJid;
4872     DOMString iqId;
4873     DOMString streamId;
4874     DOMString fileName;
4875     long      fileSize;
4876     DOMString fileHash;
4877 };
4879 /**
4880  *
4881  */
4882 bool XmppClient::fileReceiveBackground(const DOMString &fromJid,
4883                                        const DOMString &iqId,
4884                                        const DOMString &streamId,
4885                                        const DOMString &fileName,
4886                                        long  fileSize,
4887                                        const DOMString &fileHash)
4889     FileReceiveThread thread(*this, fromJid, iqId, streamId,
4890                   fileName, fileSize, fileHash);
4891     thread.start();
4892     return true;
4897 //########################################################################
4898 //# X M P P    G R O U P    C H A T
4899 //########################################################################
4901 /**
4902  *
4903  */
4904 XmppGroupChat::XmppGroupChat(const DOMString &groupJidArg)
4906     groupJid = groupJidArg;
4909 /**
4910  *
4911  */
4912 XmppGroupChat::XmppGroupChat(const XmppGroupChat &other)
4914     groupJid = other.groupJid;
4915     userList = other.userList;
4918 /**
4919  *
4920  */
4921 XmppGroupChat::~XmppGroupChat()
4926 /**
4927  *
4928  */
4929 DOMString XmppGroupChat::getGroupJid()
4931     return groupJid;
4935 void XmppGroupChat::userAdd(const DOMString &nick,
4936                             const DOMString &jid)
4938     std::vector<XmppUser>::iterator iter;
4939     for (iter= userList.begin() ; iter!=userList.end() ; iter++)
4940         {
4941         if (iter->nick == nick)
4942             return;
4943         }
4944     XmppUser user(jid, nick);
4945     userList.push_back(user);
4948 void XmppGroupChat::userShow(const DOMString &nick,
4949                              const DOMString &show)
4951     DOMString theShow = show;
4952     if (theShow == "")
4953         theShow = "available"; // a join message will now have a show
4954     std::vector<XmppUser>::iterator iter;
4955     for (iter= userList.begin() ; iter!=userList.end() ; iter++)
4956         {
4957         if (iter->nick == nick)
4958             iter->show = theShow;
4959         }
4962 void XmppGroupChat::userDelete(const DOMString &nick)
4964     std::vector<XmppUser>::iterator iter;
4965     for (iter= userList.begin() ; iter!=userList.end() ; )
4966         {
4967         if (iter->nick == nick)
4968             iter = userList.erase(iter);
4969         else
4970             iter++;
4971         }
4974 std::vector<XmppUser> XmppGroupChat::getUserList() const
4976     return userList;
4984 } //namespace Pedro
4985 //########################################################################
4986 //# E N D    O F     F I L E
4987 //########################################################################