Code

moving trunk for module inkscape
[inkscape.git] / src / jabber_whiteboard / 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 #ifdef __WIN32__
35 #include <windows.h>
37 #else /* UNIX */
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <netdb.h>
43 #include <unistd.h>
44 #include <sys/ioctl.h>
46 #include <pthread.h>
48 #endif
50 #ifdef WITH_SSL
51 #include <openssl/ssl.h>
52 #include <openssl/err.h>
53 #endif
56 namespace Pedro
57 {
59 //########################################################################
60 //########################################################################
61 //### U T I L I T Y
62 //########################################################################
63 //########################################################################
66 //########################################################################
67 //# B A S E    6 4
68 //########################################################################
70 //#################
71 //# ENCODER
72 //#################
75 static char *base64encode =
76     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
78 /**
79  * This class is for Base-64 encoding
80  */
81 class Base64Encoder
82 {
84 public:
86     Base64Encoder()
87         {
88         reset();
89         }
91     virtual ~Base64Encoder()
92         {}
94     virtual void reset()
95         {
96         outBuf   = 0L;
97         bitCount = 0;
98         buf = "";
99         }
101     virtual void append(int ch);
103     virtual void append(char *str);
105     virtual void append(unsigned char *str, int len);
107     virtual void append(const DOMString &str);
109     virtual DOMString finish();
111     static DOMString encode(const DOMString &str);
114 private:
117     unsigned long outBuf;
119     int bitCount;
121     DOMString buf;
123 };
127 /**
128  * Writes the specified byte to the output buffer
129  */
130 void Base64Encoder::append(int ch)
132     outBuf   <<=  8;
133     outBuf   |=  (ch & 0xff);
134     bitCount +=  8;
135     if (bitCount >= 24)
136         {
137         int indx  = (int)((outBuf & 0x00fc0000L) >> 18);
138         int obyte = (int)base64encode[indx & 63];
139         buf.push_back(obyte);
141         indx      = (int)((outBuf & 0x0003f000L) >> 12);
142         obyte     = (int)base64encode[indx & 63];
143         buf.push_back(obyte);
145         indx      = (int)((outBuf & 0x00000fc0L) >>  6);
146         obyte     = (int)base64encode[indx & 63];
147         buf.push_back(obyte);
149         indx      = (int)((outBuf & 0x0000003fL)      );
150         obyte     = (int)base64encode[indx & 63];
151         buf.push_back(obyte);
153         bitCount = 0;
154         outBuf   = 0L;
155         }
158 /**
159  * Writes the specified string to the output buffer
160  */
161 void Base64Encoder::append(char *str)
163     while (*str)
164         append((int)*str++);
167 /**
168  * Writes the specified string to the output buffer
169  */
170 void Base64Encoder::append(unsigned char *str, int len)
172     while (len>0)
173         {
174         append((int)*str++);
175         len--;
176         }
179 /**
180  * Writes the specified string to the output buffer
181  */
182 void Base64Encoder::append(const DOMString &str)
184     append((char *)str.c_str());
187 /**
188  * Closes this output stream and releases any system resources
189  * associated with this stream.
190  */
191 DOMString Base64Encoder::finish()
193     //get any last bytes (1 or 2) out of the buffer
194     if (bitCount == 16)
195         {
196         outBuf <<= 2;  //pad to make 18 bits
198         int indx  = (int)((outBuf & 0x0003f000L) >> 12);
199         int obyte = (int)base64encode[indx & 63];
200         buf.push_back(obyte);
202         indx      = (int)((outBuf & 0x00000fc0L) >>  6);
203         obyte     = (int)base64encode[indx & 63];
204         buf.push_back(obyte);
206         indx      = (int)((outBuf & 0x0000003fL)      );
207         obyte     = (int)base64encode[indx & 63];
208         buf.push_back(obyte);
210         buf.push_back('=');
211         }
212     else if (bitCount == 8)
213         {
214         outBuf <<= 4; //pad to make 12 bits
216         int indx  = (int)((outBuf & 0x00000fc0L) >>  6);
217         int obyte = (int)base64encode[indx & 63];
218         buf.push_back(obyte);
220         indx      = (int)((outBuf & 0x0000003fL)      );
221         obyte     = (int)base64encode[indx & 63];
222         buf.push_back(obyte);
224         buf.push_back('=');
225         buf.push_back('=');
226         }
228     DOMString ret = buf;
229     reset();
230     return ret;
234 DOMString Base64Encoder::encode(const DOMString &str)
236     Base64Encoder encoder;
237     encoder.append(str);
238     DOMString ret = encoder.finish();
239     return ret;
244 //#################
245 //# DECODER
246 //#################
248 static int base64decode[] =
250 /*00*/    -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
251 /*08*/    -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
252 /*10*/    -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
253 /*18*/    -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
254 /*20*/    -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
255 /*28*/    -1,   -1,   -1,   62,   -1,   -1,   -1,   63,
256 /*30*/    52,   53,   54,   55,   56,   57,   58,   59,
257 /*38*/    60,   61,   -1,   -1,   -1,   -1,   -1,   -1,
258 /*40*/    -1,    0,    1,    2,    3,    4,    5,    6,
259 /*48*/     7,    8,    9,   10,   11,   12,   13,   14,
260 /*50*/    15,   16,   17,   18,   19,   20,   21,   22,
261 /*58*/    23,   24,   25,   -1,   -1,   -1,   -1,   -1,
262 /*60*/    -1,   26,   27,   28,   29,   30,   31,   32,
263 /*68*/    33,   34,   35,   36,   37,   38,   39,   40,
264 /*70*/    41,   42,   43,   44,   45,   46,   47,   48,
265 /*78*/    49,   50,   51,   -1,   -1,   -1,   -1,   -1
266 };
268 class Base64Decoder
270 public:
271     Base64Decoder()
272         {
273         reset();
274         }
276     virtual ~Base64Decoder()
277         {}
279     virtual void reset()
280         {
281         inCount = 0;
282         buf.clear();
283         }
286     virtual void append(int ch);
288     virtual void append(char *str);
290     virtual void append(const DOMString &str);
292     std::vector<unsigned char> finish();
294     static std::vector<unsigned char> decode(const DOMString &str);
296     static DOMString decodeToString(const DOMString &str);
298 private:
300     int inBytes[4];
301     int inCount;
302     std::vector<unsigned char> buf;
303 };
305 /**
306  * Appends one char to the decoder
307  */
308 void Base64Decoder::append(int ch)
310     if (isspace(ch))
311         return;
312     else if (ch == '=') //padding
313         {
314         inBytes[inCount++] = 0;
315         }
316     else
317         {
318         int byteVal = base64decode[ch & 0x7f];
319         //printf("char:%c %d\n", ch, byteVal);
320         if (byteVal < 0)
321             {
322             //Bad lookup value
323             }
324         inBytes[inCount++] = byteVal;
325         }
327     if (inCount >=4 )
328         {
329         unsigned char b0 = ((inBytes[0]<<2) & 0xfc) | ((inBytes[1]>>4) & 0x03);
330         unsigned char b1 = ((inBytes[1]<<4) & 0xf0) | ((inBytes[2]>>2) & 0x0f);
331         unsigned char b2 = ((inBytes[2]<<6) & 0xc0) | ((inBytes[3]   ) & 0x3f);
332         buf.push_back(b0);
333         buf.push_back(b1);
334         buf.push_back(b2);
335         inCount = 0;
336         }
340 void Base64Decoder::append(char *str)
342     while (*str)
343         append((int)*str++);
346 void Base64Decoder::append(const DOMString &str)
348     append((char *)str.c_str());
351 std::vector<unsigned char> Base64Decoder::finish()
353     std::vector<unsigned char> ret = buf;
354     reset();
355     return ret;
358 std::vector<unsigned char> Base64Decoder::decode(const DOMString &str)
360     Base64Decoder decoder;
361     decoder.append(str);
362     std::vector<unsigned char> ret = decoder.finish();
363     return ret;
366 DOMString Base64Decoder::decodeToString(const DOMString &str)
368     Base64Decoder decoder;
369     decoder.append(str);
370     std::vector<unsigned char> ret = decoder.finish();
371     DOMString buf;
372     for (unsigned int i=0 ; i<ret.size() ; i++)
373         buf.push_back(ret[i]);
374     return buf;
379 //########################################################################
380 //# S H A   1
381 //########################################################################
383 class Sha1
385 public:
387     /**
388      *
389      */
390     Sha1()
391         { init(); }
393     /**
394      *
395      */
396     virtual ~Sha1()
397         {}
400     /**
401      * Static convenience method.  This would be the most commonly used
402      * version;
403      * @parm digest points to a bufer of 20 unsigned chars
404      */
405     static void hash(unsigned char *dataIn, int len, unsigned char *digest);
407     /**
408      * Static convenience method.  This will fill a string with the hex
409      * codex string.
410      */
411     static DOMString hashHex(unsigned char *dataIn, int len);
413     /**
414      *  Initialize the context (also zeroizes contents)
415      */
416     virtual void init();
418     /**
419      *
420      */
421     virtual void append(unsigned char *dataIn, int len);
423     /**
424      *
425      * @parm digest points to a bufer of 20 unsigned chars
426      */
427     virtual void finish(unsigned char *digest);
430 private:
432     void hashblock();
434     unsigned long H[5];
435     unsigned long W[80];
436     unsigned long sizeHi,sizeLo;
437     int lenW;
439 };
443 void Sha1::hash(unsigned char *dataIn, int len, unsigned char *digest)
445     Sha1 sha1;
446     sha1.append(dataIn, len);
447     sha1.finish(digest);
450 static char *sha1hex = "0123456789abcdef";
452 DOMString Sha1::hashHex(unsigned char *dataIn, int len)
454     unsigned char hashout[20];
455     hash(dataIn, len, hashout);
456     DOMString ret;
457     for (int i=0 ; i<20 ; i++)
458         {
459         unsigned char ch = hashout[i];
460         ret.push_back(sha1hex[ (ch>>4) & 15 ]);
461         ret.push_back(sha1hex[ ch      & 15 ]);
462         }
463     return ret;
467 void Sha1::init()
470     lenW   = 0;
471     sizeHi = 0;
472     sizeLo = 0;
474     // Initialize H with the magic constants (see FIPS180 for constants)
475     H[0] = 0x67452301L;
476     H[1] = 0xefcdab89L;
477     H[2] = 0x98badcfeL;
478     H[3] = 0x10325476L;
479     H[4] = 0xc3d2e1f0L;
481     for (int i = 0; i < 80; i++)
482         W[i] = 0;
486 void Sha1::append(unsigned char *dataIn, int len)
488     // Read the data into W and process blocks as they get full
489     for (int i = 0; i < len; i++)
490         {
491         W[lenW / 4] <<= 8;
492         W[lenW / 4] |= (unsigned long)dataIn[i];
493         if ((++lenW) % 64 == 0)
494             {
495             hashblock();
496             lenW = 0;
497             }
498         sizeLo += 8;
499         sizeHi += (sizeLo < 8);
500         }
504 void Sha1::finish(unsigned char hashout[20])
506     unsigned char pad0x80 = 0x80;
507     unsigned char pad0x00 = 0x00;
508     unsigned char padlen[8];
510     // Pad with a binary 1 (e.g. 0x80), then zeroes, then length
511     padlen[0] = (unsigned char)((sizeHi >> 24) & 255);
512     padlen[1] = (unsigned char)((sizeHi >> 16) & 255);
513     padlen[2] = (unsigned char)((sizeHi >>  8) & 255);
514     padlen[3] = (unsigned char)((sizeHi >>  0) & 255);
515     padlen[4] = (unsigned char)((sizeLo >> 24) & 255);
516     padlen[5] = (unsigned char)((sizeLo >> 16) & 255);
517     padlen[6] = (unsigned char)((sizeLo >>  8) & 255);
518     padlen[7] = (unsigned char)((sizeLo >>  0) & 255);
520     append(&pad0x80, 1);
522     while (lenW != 56)
523         append(&pad0x00, 1);
524     append(padlen, 8);
526     // Output hash
527     for (int i = 0; i < 20; i++)
528         {
529         hashout[i] = (unsigned char)(H[i / 4] >> 24);
530         H[i / 4] <<= 8;
531         }
533     // Re-initialize the context (also zeroizes contents)
534     init();
538 #define SHA_ROTL(X,n) ((((X) << (n)) | ((X) >> (32-(n)))) & 0xffffffffL)
540 void Sha1::hashblock()
543     for (int t = 16; t <= 79; t++)
544         W[t] = SHA_ROTL(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1);
546     unsigned long A = H[0];
547     unsigned long B = H[1];
548     unsigned long C = H[2];
549     unsigned long D = H[3];
550     unsigned long E = H[4];
552     unsigned long TEMP;
554     for (int t = 0; t <= 19; t++)
555         {
556         TEMP = (SHA_ROTL(A,5) + (((C^D)&B)^D) +
557                 E + W[t] + 0x5a827999L) & 0xffffffffL;
558         E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
559         }
560     for (int t = 20; t <= 39; t++)
561         {
562         TEMP = (SHA_ROTL(A,5) + (B^C^D) +
563                 E + W[t] + 0x6ed9eba1L) & 0xffffffffL;
564         E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
565         }
566     for (int t = 40; t <= 59; t++)
567         {
568         TEMP = (SHA_ROTL(A,5) + ((B&C)|(D&(B|C))) +
569                 E + W[t] + 0x8f1bbcdcL) & 0xffffffffL;
570         E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
571         }
572     for (int t = 60; t <= 79; t++)
573         {
574         TEMP = (SHA_ROTL(A,5) + (B^C^D) +
575                 E + W[t] + 0xca62c1d6L) & 0xffffffffL;
576         E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
577         }
579     H[0] += A;
580     H[1] += B;
581     H[2] += C;
582     H[3] += D;
583     H[4] += E;
590 //########################################################################
591 //# M D  5
592 //########################################################################
594 class Md5
596 public:
598     /**
599      *
600      */
601     Md5()
602         { init(); }
604     /**
605      *
606      */
607     virtual ~Md5()
608         {}
610     /**
611      * Static convenience method.
612      * @parm digest points to an buffer of 16 unsigned chars
613      */
614     static void hash(unsigned char *dataIn,
615                      unsigned long len, unsigned char *digest);
617     static DOMString hashHex(unsigned char *dataIn, unsigned long len);
619     /**
620      *  Initialize the context (also zeroizes contents)
621      */
622     virtual void init();
624     /**
625      *
626      */
627     virtual void append(unsigned char *dataIn, unsigned long len);
629     /**
630      *
631      */
632     virtual void append(const DOMString &str);
634     /**
635      * Finalize and output the hash.
636      * @parm digest points to an buffer of 16 unsigned chars
637      */
638     virtual void finish(unsigned char *digest);
641     /**
642      * Same as above , but hex to an output String
643      */
644     virtual DOMString finishHex();
646 private:
648     void transform(unsigned long *buf, unsigned long *in);
650     unsigned long buf[4];
651     unsigned long bits[2];
652     unsigned char in[64];
654 };
659 void Md5::hash(unsigned char *dataIn, unsigned long len, unsigned char *digest)
661     Md5 md5;
662     md5.append(dataIn, len);
663     md5.finish(digest);
666 DOMString Md5::hashHex(unsigned char *dataIn, unsigned long len)
668     Md5 md5;
669     md5.append(dataIn, len);
670     DOMString ret = md5.finishHex();
671     return ret;
676 /*
677  * Note: this code is harmless on little-endian machines.
678  */
679 /*
680 static void byteReverse(unsigned char *buf, unsigned long longs)
682     do
683         {
684         unsigned long t = (unsigned long) 
685             ((unsigned) buf[3] << 8 | buf[2]) << 16 |
686             ((unsigned) buf[1] << 8 | buf[0]);
687         *(unsigned long *) buf = t;
688         buf += 4;
689         } while (--longs);
691 */
693 static void md5_memcpy(void *dest, void *src, int n)
695     unsigned char *s1 = (unsigned char *)dest;
696     unsigned char *s2 = (unsigned char *)src;
697     while (n--)
698         *s1++ = *s2++;
701 static void md5_memset(void *dest, char v, int n)
703     unsigned char *s = (unsigned char *)dest;
704     while (n--)
705         *s++ = v;
708 /**
709  * Initialize MD5 polynomials and storage
710  */
711 void Md5::init()
713     buf[0]  = 0x67452301;
714     buf[1]  = 0xefcdab89;
715     buf[2]  = 0x98badcfe;
716     buf[3]  = 0x10325476;
718     bits[0] = 0;
719     bits[1] = 0;
722 /*
723  * Update context to reflect the concatenation of another buffer full
724  * of bytes.
725  */
726 void Md5::append(unsigned char *source, unsigned long len)
729     // Update bitcount
730     unsigned long t = bits[0];
731     if ((bits[0] = t + ((unsigned long) len << 3)) < t)
732             bits[1]++;// Carry from low to high
733     bits[1] += len >> 29;
735         //Bytes already in shsInfo->data
736     t = (t >> 3) & 0x3f;
739     // Handle any leading odd-sized chunks
740     if (t)
741         {
742         unsigned char *p = (unsigned char *) in + t;
743         t = 64 - t;
744         if (len < t)
745             {
746             md5_memcpy(p, source, len);
747             return;
748             }
749         md5_memcpy(p, source, t);
750         //byteReverse(in, 16);
751         transform(buf, (unsigned long *) in);
752         source += t;
753         len    -= t;
754         }
756     // Process data in 64-byte chunks
757     while (len >= 64)
758         {
759         md5_memcpy(in, source, 64);
760         //byteReverse(in, 16);
761         transform(buf, (unsigned long *) in);
762         source += 64;
763         len    -= 64;
764         }
766     // Handle any remaining bytes of data.
767     md5_memcpy(in, source, len);
770 /*
771  * Update context to reflect the concatenation of another string
772  */
773 void Md5::append(const DOMString &str)
775     append((unsigned char *)str.c_str(), str.size());
778 /*
779  * Final wrapup - pad to 64-byte boundary with the bit pattern
780  * 1 0* (64-bit count of bits processed, MSB-first)
781  */
782 void Md5::finish(unsigned char *digest)
784     // Compute number of bytes mod 64
785     unsigned int count = (bits[0] >> 3) & 0x3F;
787     // Set the first char of padding to 0x80.
788     // This is safe since there is always at least one byte free
789     unsigned char *p = in + count;
790     *p++ = 0x80;
792     // Bytes of padding needed to make 64 bytes
793     count = 64 - 1 - count;
795     // Pad out to 56 mod 64
796     if (count < 8)
797         {
798             // Two lots of padding:  Pad the first block to 64 bytes
799             md5_memset(p, 0, count);
800             //byteReverse(in, 16);
801             transform(buf, (unsigned long *) in);
803             // Now fill the next block with 56 bytes
804             md5_memset(in, 0, 56);
805         }
806     else
807         {
808         // Pad block to 56 bytes
809         md5_memset(p, 0, count - 8);
810         }
811     //byteReverse(in, 14);
813     // Append length in bits and transform
814     ((unsigned long *) in)[14] = bits[0];
815     ((unsigned long *) in)[15] = bits[1];
817     transform(buf, (unsigned long *) in);
818     //byteReverse((unsigned char *) buf, 4);
819     md5_memcpy(digest, buf, 16);
820     init();  // Security!  ;-)
823 static char *md5hex = "0123456789abcdef";
825 DOMString Md5::finishHex()
827     unsigned char hashout[16];
828     finish(hashout);
829     DOMString ret;
830     for (int i=0 ; i<16 ; i++)
831         {
832         unsigned char ch = hashout[i];
833         ret.push_back(md5hex[ (ch>>4) & 15 ]);
834         ret.push_back(md5hex[ ch      & 15 ]);
835         }
836     return ret;
841 //#  The four core functions - F1 is optimized somewhat
843 //  #define F1(x, y, z) (x & y | ~x & z)
844 #define F1(x, y, z) (z ^ (x & (y ^ z)))
845 #define F2(x, y, z) F1(z, x, y)
846 #define F3(x, y, z) (x ^ y ^ z)
847 #define F4(x, y, z) (y ^ (x | ~z))
849 // ## This is the central step in the MD5 algorithm.
850 #define MD5STEP(f, w, x, y, z, data, s) \
851         ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
853 /*
854  * The core of the MD5 algorithm, this alters an existing MD5 hash to
855  * reflect the addition of 16 longwords of new data.  MD5Update blocks
856  * the data and converts bytes into longwords for this routine.
857  * @parm buf points to an array of 4 unsigned longs
858  * @parm in points to an array of 16 unsigned longs
859  */
860 void Md5::transform(unsigned long *buf, unsigned long *in)
862     unsigned long a = buf[0];
863     unsigned long b = buf[1];
864     unsigned long c = buf[2];
865     unsigned long d = buf[3];
867     MD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478,  7);
868     MD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12);
869     MD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17);
870     MD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22);
871     MD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf,  7);
872     MD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12);
873     MD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17);
874     MD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22);
875     MD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8,  7);
876     MD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12);
877     MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
878     MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
879     MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122,  7);
880     MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
881     MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
882     MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
884     MD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562,  5);
885     MD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340,  9);
886     MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
887     MD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20);
888     MD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d,  5);
889     MD5STEP(F2, d, a, b, c, in[10] + 0x02441453,  9);
890     MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
891     MD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20);
892     MD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6,  5);
893     MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6,  9);
894     MD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14);
895     MD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20);
896     MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905,  5);
897     MD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8,  9);
898     MD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14);
899     MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
901     MD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942,  4);
902     MD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11);
903     MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
904     MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
905     MD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44,  4);
906     MD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11);
907     MD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16);
908     MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
909     MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6,  4);
910     MD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11);
911     MD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16);
912     MD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23);
913     MD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039,  4);
914     MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
915     MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
916     MD5STEP(F3, b, c, d, a, in[ 2] + 0xc4ac5665, 23);
918     MD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244,  6);
919     MD5STEP(F4, d, a, b, c, in[ 7] + 0x432aff97, 10);
920     MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
921     MD5STEP(F4, b, c, d, a, in[ 5] + 0xfc93a039, 21);
922     MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3,  6);
923     MD5STEP(F4, d, a, b, c, in[ 3] + 0x8f0ccc92, 10);
924     MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
925     MD5STEP(F4, b, c, d, a, in[ 1] + 0x85845dd1, 21);
926     MD5STEP(F4, a, b, c, d, in[ 8] + 0x6fa87e4f,  6);
927     MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
928     MD5STEP(F4, c, d, a, b, in[ 6] + 0xa3014314, 15);
929     MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
930     MD5STEP(F4, a, b, c, d, in[ 4] + 0xf7537e82,  6);
931     MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
932     MD5STEP(F4, c, d, a, b, in[ 2] + 0x2ad7d2bb, 15);
933     MD5STEP(F4, b, c, d, a, in[ 9] + 0xeb86d391, 21);
935     buf[0] += a;
936     buf[1] += b;
937     buf[2] += c;
938     buf[3] += d;
947 //########################################################################
948 //########################################################################
949 //### T H R E A D
950 //########################################################################
951 //########################################################################
954 //########################################################################
955 //### T H R E A D
956 //########################################################################
958 /**
959  * This is the interface for a delegate class which can
960  * be run by a Thread.
961  * Thread thread(runnable);
962  * thread.start();
963  */
964 class Runnable
966 public:
968     Runnable()
969         {}
970     virtual ~Runnable()
971         {}
972         
973     /**
974      * The method of a delegate class which can
975      * be run by a Thread.  Thread is completed when this
976      * method is done.
977      */
978     virtual void run() = 0;
980 };
984 /**
985  *  A simple wrapper of native threads in a portable class.
986  *  It can be used either to execute its own run() method, or
987  *  delegate to a Runnable class's run() method.
988  */
989 class Thread
991 public:
993     /**
994      *  Create a thread which will execute its own run() method.
995      */
996     Thread()
997         { runnable = NULL ; started = false; }
999     /**
1000      * Create a thread which will run a Runnable class's run() method.
1001      */
1002     Thread(const Runnable &runner)
1003         { runnable = (Runnable *)&runner; started = false; }
1005     /**
1006      *  This does not kill a spawned thread.
1007      */
1008     virtual ~Thread()
1009         {}
1011     /**
1012      *  Static method to pause the current thread for a given
1013      *  number of milliseconds.
1014      */
1015     static void sleep(unsigned long millis);
1017     /**
1018      *  This method will be executed if the Thread was created with
1019      *  no delegated Runnable class.  The thread is completed when
1020      *  the method is done.
1021      */
1022     virtual void run()
1023         {}
1025     /**
1026      *  Starts the thread.
1027      */
1028     virtual void start();
1030     /**
1031      *  Calls either this class's run() method, or that of a Runnable.
1032      *  A user would normally not call this directly.
1033      */
1034     virtual void execute()
1035         {
1036         started = true;
1037         if (runnable)
1038             runnable->run();
1039         else
1040             run();
1041         }
1043 private:
1045     Runnable *runnable;
1047     bool started;
1049 };
1055 #ifdef __WIN32__
1058 static DWORD WINAPI WinThreadFunction(LPVOID context)
1060     Thread *thread = (Thread *)context;
1061     thread->execute();
1065 void Thread::start()
1067     DWORD dwThreadId;
1068     HANDLE hThread = CreateThread(NULL, 0, WinThreadFunction,
1069                (LPVOID)this,  0,  &dwThreadId);
1070     //Make sure the thread is started before 'this' is deallocated
1071     while (!started)
1072         sleep(10);
1073     CloseHandle(hThread);
1076 void Thread::sleep(unsigned long millis)
1078     Sleep(millis);
1081 #else /* UNIX */
1084 void *PthreadThreadFunction(void *context)
1086     Thread *thread = (Thread *)context;
1087     thread->execute();
1088     return NULL;
1092 void Thread::start()
1094     pthread_t thread;
1096     int ret = pthread_create(&thread, NULL,
1097             PthreadThreadFunction, (void *)this);
1098     if (ret != 0)
1099         printf("Thread::start: thread creation failed: %s\n", strerror(ret));
1101     //Make sure the thread is started before 'this' is deallocated
1102     while (!started)
1103         sleep(10);
1107 void Thread::sleep(unsigned long millis)
1109     timespec requested;
1110     requested.tv_sec = millis / 1000;
1111     requested.tv_nsec = (millis % 1000 ) * 1000000L;
1112     nanosleep(&requested, NULL);
1115 #endif
1118 //########################################################################
1119 //########################################################################
1120 //### S O C K E T
1121 //########################################################################
1122 //########################################################################
1128 class TcpSocket
1130 public:
1132     TcpSocket();
1134     TcpSocket(const std::string &hostname, int port);
1136     TcpSocket(const char *hostname, int port);
1138     TcpSocket(const TcpSocket &other);
1140     virtual ~TcpSocket();
1142     bool isConnected();
1144     void enableSSL(bool val);
1146     bool connect(const std::string &hostname, int portno);
1148     bool connect(const char *hostname, int portno);
1150     bool startTls();
1152     bool connect();
1154     bool disconnect();
1156     bool setReceiveTimeout(unsigned long millis);
1158     long available();
1160     bool write(int ch);
1162     bool write(char *str);
1164     bool write(const std::string &str);
1166     int read();
1168     std::string readLine();
1170 private:
1171     void init();
1173     std::string hostname;
1174     int  portno;
1175     int  sock;
1176     bool connected;
1178     bool sslEnabled;
1180     unsigned long receiveTimeout;
1182 #ifdef WITH_SSL
1183     SSL_CTX *sslContext;
1184     SSL *sslStream;
1185 #endif
1187 };
1191 //#########################################################################
1192 //# U T I L I T Y
1193 //#########################################################################
1195 static void mybzero(void *s, size_t n)
1197     unsigned char *p = (unsigned char *)s;
1198     while (n > 0)
1199         {
1200         *p++ = (unsigned char)0;
1201         n--;
1202         }
1205 static void mybcopy(void *src, void *dest, size_t n)
1207     unsigned char *p = (unsigned char *)dest;
1208     unsigned char *q = (unsigned char *)src;
1209     while (n > 0)
1210         {
1211         *p++ = *q++;
1212         n--;
1213         }
1218 //#########################################################################
1219 //# T C P    C O N N E C T I O N
1220 //#########################################################################
1222 TcpSocket::TcpSocket()
1224     init();
1228 TcpSocket::TcpSocket(const char *hostnameArg, int port)
1230     init();
1231     hostname  = hostnameArg;
1232     portno    = port;
1235 TcpSocket::TcpSocket(const std::string &hostnameArg, int port)
1237     init();
1238     hostname  = hostnameArg;
1239     portno    = port;
1243 #ifdef WITH_SSL
1245 static void cryptoLockCallback(int mode, int type, const char *file, int line)
1247     //printf("########### LOCK\n");
1248     static int modes[CRYPTO_NUM_LOCKS]; /* = {0, 0, ... } */
1249     const char *errstr = NULL;
1251     int rw = mode & (CRYPTO_READ|CRYPTO_WRITE);
1252     if (!((rw == CRYPTO_READ) || (rw == CRYPTO_WRITE)))
1253         {
1254         errstr = "invalid mode";
1255         goto err;
1256         }
1258     if (type < 0 || type >= CRYPTO_NUM_LOCKS)
1259         {
1260         errstr = "type out of bounds";
1261         goto err;
1262         }
1264     if (mode & CRYPTO_LOCK)
1265         {
1266         if (modes[type])
1267             {
1268             errstr = "already locked";
1269             /* must not happen in a single-threaded program
1270              * (would deadlock)
1271              */
1272             goto err;
1273             }
1275         modes[type] = rw;
1276         }
1277     else if (mode & CRYPTO_UNLOCK)
1278         {
1279         if (!modes[type])
1280             {
1281              errstr = "not locked";
1282              goto err;
1283              }
1285         if (modes[type] != rw)
1286             {
1287             errstr = (rw == CRYPTO_READ) ?
1288                   "CRYPTO_r_unlock on write lock" :
1289                   "CRYPTO_w_unlock on read lock";
1290             }
1292         modes[type] = 0;
1293         }
1294     else
1295         {
1296         errstr = "invalid mode";
1297         goto err;
1298         }
1300     err:
1301     if (errstr)
1302         {
1303         /* we cannot use bio_err here */
1304         fprintf(stderr, "openssl (lock_dbg_cb): %s (mode=%d, type=%d) at %s:%d\n",
1305                 errstr, mode, type, file, line);
1306         }
1309 static unsigned long cryptoIdCallback()
1311 #ifdef __WIN32__
1312     unsigned long ret = (unsigned long) GetCurrentThreadId();
1313 #else
1314     unsigned long ret = (unsigned long) pthread_self();
1315 #endif
1316     return ret;
1319 #endif
1322 TcpSocket::TcpSocket(const TcpSocket &other)
1324     init();
1325     sock      = other.sock;
1326     hostname  = other.hostname;
1327     portno    = other.portno;
1330 static bool tcp_socket_inited = false;
1332 void TcpSocket::init()
1334     if (!tcp_socket_inited)
1335         {
1336 #ifdef __WIN32__
1337         WORD wVersionRequested = MAKEWORD( 2, 2 );
1338         WSADATA wsaData;
1339         int err = WSAStartup( wVersionRequested, &wsaData );
1340 #endif
1341 #ifdef WITH_SSL
1342         sslStream  = NULL;
1343         sslContext = NULL;
1344             CRYPTO_set_locking_callback(cryptoLockCallback);
1345         CRYPTO_set_id_callback(cryptoIdCallback);
1346         SSL_library_init();
1347         SSL_load_error_strings();
1348 #endif
1349         tcp_socket_inited = true;
1350         }
1351     sock           = -1;
1352     connected      = false;
1353     hostname       = "";
1354     portno         = -1;
1355     sslEnabled     = false;
1356     receiveTimeout = 0;
1359 TcpSocket::~TcpSocket()
1361     disconnect();
1364 bool TcpSocket::isConnected()
1366     if (!connected || sock < 0)
1367         return false;
1368     return true;
1371 void TcpSocket::enableSSL(bool val)
1373     sslEnabled = val;
1377 bool TcpSocket::connect(const char *hostnameArg, int portnoArg)
1379     hostname = hostnameArg;
1380     portno   = portnoArg;
1381     return connect();
1384 bool TcpSocket::connect(const std::string &hostnameArg, int portnoArg)
1386     hostname = hostnameArg;
1387     portno   = portnoArg;
1388     return connect();
1393 #ifdef WITH_SSL
1394 /*
1395 static int password_cb(char *buf, int bufLen, int rwflag, void *userdata)
1397     char *password = "password";
1398     if (bufLen < (int)(strlen(password)+1))
1399         return 0;
1401     strcpy(buf,password);
1402     int ret = strlen(password);
1403     return ret;
1406 static void infoCallback(const SSL *ssl, int where, int ret)
1408     switch (where)
1409         {
1410         case SSL_CB_ALERT:
1411             {
1412             printf("## %d SSL ALERT: %s\n",  where, SSL_alert_desc_string_long(ret));
1413             break;
1414             }
1415         default:
1416             {
1417             printf("## %d SSL: %s\n",  where, SSL_state_string_long(ssl));
1418             break;
1419             }
1420         }
1422 */
1423 #endif
1426 bool TcpSocket::startTls()
1428 #ifdef WITH_SSL
1429     sslStream  = NULL;
1430     sslContext = NULL;
1432     //SSL_METHOD *meth = SSLv23_method();
1433     //SSL_METHOD *meth = SSLv3_client_method();
1434     SSL_METHOD *meth = TLSv1_client_method();
1435     sslContext = SSL_CTX_new(meth);
1436     //SSL_CTX_set_info_callback(sslContext, infoCallback);
1438 #if 0
1439     char *keyFile  = "client.pem";
1440     char *caList   = "root.pem";
1441     /* Load our keys and certificates*/
1442     if (!(SSL_CTX_use_certificate_chain_file(sslContext, keyFile)))
1443         {
1444         fprintf(stderr, "Can't read certificate file\n");
1445         disconnect();
1446         return false;
1447         }
1449     SSL_CTX_set_default_passwd_cb(sslContext, password_cb);
1451     if (!(SSL_CTX_use_PrivateKey_file(sslContext, keyFile, SSL_FILETYPE_PEM)))
1452         {
1453         fprintf(stderr, "Can't read key file\n");
1454         disconnect();
1455         return false;
1456         }
1458     /* Load the CAs we trust*/
1459     if (!(SSL_CTX_load_verify_locations(sslContext, caList, 0)))
1460         {
1461         fprintf(stderr, "Can't read CA list\n");
1462         disconnect();
1463         return false;
1464         }
1465 #endif
1467     /* Connect the SSL socket */
1468     sslStream  = SSL_new(sslContext);
1469     SSL_set_fd(sslStream, sock);
1471     if (SSL_connect(sslStream)<=0)
1472         {
1473         fprintf(stderr, "SSL connect error\n");
1474         disconnect();
1475         return false;
1476         }
1478     sslEnabled = true;
1479 #endif /*WITH_SSL*/
1480     return true;
1484 bool TcpSocket::connect()
1486     if (hostname.size()<1)
1487         {
1488         printf("open: null hostname\n");
1489         return false;
1490         }
1492     if (portno<1)
1493         {
1494         printf("open: bad port number\n");
1495         return false;
1496         }
1498     sock = socket(PF_INET, SOCK_STREAM, 0);
1499     if (sock < 0)
1500         {
1501         printf("open: error creating socket\n");
1502         return false;
1503         }
1505     char *c_hostname = (char *)hostname.c_str();
1506     struct hostent *server = gethostbyname(c_hostname);
1507     if (!server)
1508         {
1509         printf("open: could not locate host '%s'\n", c_hostname);
1510         return false;
1511         }
1513     struct sockaddr_in serv_addr;
1514     mybzero((char *) &serv_addr, sizeof(serv_addr));
1515     serv_addr.sin_family = AF_INET;
1516     mybcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr,
1517          server->h_length);
1518     serv_addr.sin_port = htons(portno);
1520     int ret = ::connect(sock, (const sockaddr *)&serv_addr, sizeof(serv_addr));
1521     if (ret < 0)
1522         {
1523         printf("open: could not connect to host '%s'\n", c_hostname);
1524         return false;
1525         }
1527      if (sslEnabled)
1528         {
1529         if (!startTls())
1530             return false;
1531         }
1532     connected = true;
1533     return true;
1536 bool TcpSocket::disconnect()
1538     bool ret  = true;
1539     connected = false;
1540 #ifdef WITH_SSL
1541     if (sslEnabled)
1542         {
1543         if (sslStream)
1544             {
1545             int r = SSL_shutdown(sslStream);
1546             switch(r)
1547                 {
1548                 case 1:
1549                     break; /* Success */
1550                 case 0:
1551                 case -1:
1552                 default:
1553                     //printf("Shutdown failed");
1554                     ret = false;
1555                 }
1556             SSL_free(sslStream);
1557             }
1558         if (sslContext)
1559             SSL_CTX_free(sslContext);
1560         }
1561     sslStream  = NULL;
1562     sslContext = NULL;
1563 #endif /*WITH_SSL*/
1565 #ifdef __WIN32__
1566     closesocket(sock);
1567 #else
1568     ::close(sock);
1569 #endif
1570     sock = -1;
1571     sslEnabled = false;
1573     return ret;
1578 bool TcpSocket::setReceiveTimeout(unsigned long millis)
1580     receiveTimeout = millis;
1581     return true;
1584 /**
1585  * For normal sockets, return the number of bytes waiting to be received.
1586  * For SSL, just return >0 when something is ready to be read.
1587  */
1588 long TcpSocket::available()
1590     if (!isConnected())
1591         return -1;
1593     long count = 0;
1594 #ifdef __WIN32__
1595     if (ioctlsocket(sock, FIONREAD, (unsigned long *)&count) != 0)
1596         return -1;
1597 #else
1598     if (ioctl(sock, FIONREAD, &count) != 0)
1599         return -1;
1600 #endif
1601     if (count<=0 && sslEnabled)
1602         {
1603 #ifdef WITH_SSL
1604         return SSL_pending(sslStream);
1605 #endif
1606         }
1607     return count;
1612 bool TcpSocket::write(int ch)
1614     if (!isConnected())
1615         {
1616         printf("write: socket closed\n");
1617         return false;
1618         }
1619     unsigned char c = (unsigned char)ch;
1621     if (sslEnabled)
1622         {
1623 #ifdef WITH_SSL
1624         int r = SSL_write(sslStream, &c, 1);
1625         if (r<=0)
1626             {
1627             switch(SSL_get_error(sslStream, r))
1628                 {
1629                 default:
1630                     printf("SSL write problem");
1631                     return -1;
1632                 }
1633             }
1634 #endif
1635         }
1636     else
1637         {
1638         if (send(sock, (const char *)&c, 1, 0) < 0)
1639         //if (send(sock, &c, 1, 0) < 0)
1640             {
1641             printf("write: could not send data\n");
1642             return false;
1643             }
1644         }
1645     return true;
1648 bool TcpSocket::write(char *str)
1650    if (!isConnected())
1651         {
1652         printf("write(str): socket closed\n");
1653         return false;
1654         }
1655     unsigned char *s = (unsigned char *)str;
1656     int len = strlen(str);
1658     if (sslEnabled)
1659         {
1660 #ifdef WITH_SSL
1661         int r = SSL_write(sslStream, s, len);
1662         if (r<=0)
1663             {
1664             switch(SSL_get_error(sslStream, r))
1665                 {
1666                 default:
1667                     printf("SSL write problem");
1668                     return -1;
1669                 }
1670             }
1671 #endif
1672         }
1673     else
1674         {
1675         if (send(sock, s, len, 0) < 0)
1676         //if (send(sock, &c, 1, 0) < 0)
1677             {
1678             printf("write: could not send data\n");
1679             return false;
1680             }
1681         }
1682     return true;
1685 bool TcpSocket::write(const std::string &str)
1687     return write((char *)str.c_str());
1690 int TcpSocket::read()
1692     if (!isConnected())
1693         return -1;
1695     //We'll use this loop for timeouts, so that SSL and plain sockets
1696     //will behave the same way
1697     if (receiveTimeout > 0)
1698         {
1699         unsigned long tim = 0;
1700         while (true)
1701             {
1702             int avail = available();
1703             if (avail > 0)
1704                 break;
1705             if (tim >= receiveTimeout)
1706                 return -2;
1707             Thread::sleep(20);
1708             tim += 20;
1709             }
1710         }
1712     //check again
1713     if (!isConnected())
1714         return -1;
1716     unsigned char ch;
1717     if (sslEnabled)
1718         {
1719 #ifdef WITH_SSL
1720         if (!sslStream)
1721             return -1;
1722         int r = SSL_read(sslStream, &ch, 1);
1723         unsigned long err = SSL_get_error(sslStream, r);
1724         switch (err)
1725             {
1726             case SSL_ERROR_NONE:
1727                  break;
1728             case SSL_ERROR_ZERO_RETURN:
1729                 return -1;
1730             case SSL_ERROR_SYSCALL:
1731                 printf("SSL read problem(syscall) %s\n",
1732                      ERR_error_string(ERR_get_error(), NULL));
1733                 return -1;
1734             default:
1735                 printf("SSL read problem %s\n",
1736                      ERR_error_string(ERR_get_error(), NULL));
1737                 return -1;
1738             }
1739 #endif
1740         }
1741     else
1742         {
1743         if (recv(sock, (char *)&ch, 1, 0) <= 0)
1744             {
1745             printf("read: could not receive data\n");
1746             disconnect();
1747             return -1;
1748             }
1749         }
1750     return (int)ch;
1753 std::string TcpSocket::readLine()
1755     std::string ret;
1757     while (isConnected())
1758         {
1759         int ch = read();
1760         if (ch<0)
1761             return ret;
1762         if (ch=='\r' || ch=='\n')
1763             return ret;
1764         ret.push_back((char)ch);
1765         }
1767     return ret;
1771 //########################################################################
1772 //########################################################################
1773 //### X M P P
1774 //########################################################################
1775 //########################################################################
1780 //########################################################################
1781 //# X M P P    E V E N T
1782 //########################################################################
1785 XmppEvent::XmppEvent(int type)
1787     eventType = type;
1788     presence  = false;
1789     dom       = NULL;
1792 XmppEvent::XmppEvent(const XmppEvent &other)
1794     assign(other);
1797 XmppEvent &XmppEvent::operator=(const XmppEvent &other)
1799     assign(other);
1800     return (*this);
1803 XmppEvent::~XmppEvent()
1805     if (dom)
1806         delete dom;
1809 void XmppEvent::assign(const XmppEvent &other)
1811     eventType = other.eventType;
1812     presence  = other.presence;
1813     status    = other.status;
1814     show      = other.show;
1815     to        = other.to;
1816     from      = other.from;
1817     group     = other.group;
1818     data      = other.data;
1819     fileName  = other.fileName;
1820     fileDesc  = other.fileDesc;
1821     fileSize  = other.fileSize;
1822     fileHash  = other.fileHash;
1823     setDOM(other.dom);
1826 int XmppEvent::getType() const
1828     return eventType;
1831 DOMString XmppEvent::getIqId() const
1833     return iqId;
1836 void XmppEvent::setIqId(const DOMString &val)
1838     iqId = val;
1841 DOMString XmppEvent::getStreamId() const
1843     return streamId;
1846 void XmppEvent::setStreamId(const DOMString &val)
1848     streamId = val;
1851 bool XmppEvent::getPresence() const
1853     return presence;
1856 void XmppEvent::setPresence(bool val)
1858     presence = val;
1861 DOMString XmppEvent::getShow() const
1863     return show;
1866 void XmppEvent::setShow(const DOMString &val)
1868     show = val;
1871 DOMString XmppEvent::getStatus() const
1873     return status;
1876 void XmppEvent::setStatus(const DOMString &val)
1878     status = val;
1881 DOMString XmppEvent::getTo() const
1883     return to;
1886 void XmppEvent::setTo(const DOMString &val)
1888     to = val;
1891 DOMString XmppEvent::getFrom() const
1893     return from;
1896 void XmppEvent::setFrom(const DOMString &val)
1898     from = val;
1901 DOMString XmppEvent::getGroup() const
1903     return group;
1906 void XmppEvent::setGroup(const DOMString &val)
1908     group = val;
1911 DOMString XmppEvent::getData() const
1913     return data;
1916 void XmppEvent::setData(const DOMString &val)
1918     data = val;
1921 DOMString XmppEvent::getFileName() const
1923     return fileName;
1926 void XmppEvent::setFileName(const DOMString &val)
1928     fileName = val;
1931 DOMString XmppEvent::getFileDesc() const
1933     return fileDesc;
1936 void XmppEvent::setFileDesc(const DOMString &val)
1938     fileDesc = val;
1941 long XmppEvent::getFileSize() const
1943     return fileSize;
1946 void XmppEvent::setFileSize(long val)
1948     fileSize = val;
1951 DOMString XmppEvent::getFileHash() const
1953     return fileHash;
1956 void XmppEvent::setFileHash(const DOMString &val)
1958     fileHash = val;
1961 Element *XmppEvent::getDOM() const
1963     return dom;
1966 void XmppEvent::setDOM(const Element *val)
1968     if (!val)
1969         dom = NULL;
1970     else
1971         dom = ((Element *)val)->clone();
1975 std::vector<XmppUser> XmppEvent::getUserList() const
1977     return userList;
1980 void XmppEvent::setUserList(const std::vector<XmppUser> &val)
1982     userList = val;
1985 //########################################################################
1986 //# X M P P    E V E N T    T A R G E T
1987 //########################################################################
1989 //###########################
1990 //# CONSTRUCTORS
1991 //###########################
1993 XmppEventTarget::XmppEventTarget()
1995     eventQueueEnabled = false;
1999 XmppEventTarget::XmppEventTarget(const XmppEventTarget &other)
2001     listeners         = other.listeners;
2002     eventQueueEnabled = other.eventQueueEnabled;
2005 XmppEventTarget::~XmppEventTarget()
2010 //###########################
2011 //# M E S S A G E S
2012 //###########################
2014 void XmppEventTarget::error(char *fmt, ...)
2016     va_list args;
2017     va_start(args,fmt);
2018     vsnprintf(targetWriteBuf, targetWriteBufLen, fmt, args);
2019     va_end(args) ;
2020     printf("Error:%s\n", targetWriteBuf);
2021     XmppEvent evt(XmppEvent::EVENT_ERROR);
2022     evt.setData(targetWriteBuf);
2023     dispatchXmppEvent(evt);
2026 void XmppEventTarget::status(char *fmt, ...)
2028     va_list args;
2029     va_start(args,fmt);
2030     vsnprintf(targetWriteBuf, targetWriteBufLen, fmt, args);
2031     va_end(args) ;
2032     //printf("Status:%s\n", targetWriteBuf);
2033     XmppEvent evt(XmppEvent::EVENT_STATUS);
2034     evt.setData(targetWriteBuf);
2035     dispatchXmppEvent(evt);
2040 //###########################
2041 //# L I S T E N E R S
2042 //###########################
2044 void XmppEventTarget::dispatchXmppEvent(const XmppEvent &event)
2046     std::vector<XmppEventListener *>::iterator iter;
2047     for (iter = listeners.begin(); iter != listeners.end() ; iter++)
2048         (*iter)->processXmppEvent(event);
2049     if (eventQueueEnabled)
2050         eventQueue.push_back(event);
2053 void XmppEventTarget::addXmppEventListener(const XmppEventListener &listener)
2055     XmppEventListener *lsnr = (XmppEventListener *)&listener;
2056     std::vector<XmppEventListener *>::iterator iter;
2057     for (iter = listeners.begin(); iter != listeners.end() ; iter++)
2058         if (*iter == lsnr)
2059             return;
2060     listeners.push_back(lsnr);
2063 void XmppEventTarget::removeXmppEventListener(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             listeners.erase(iter);
2072 void XmppEventTarget::clearXmppEventListeners()
2074     listeners.clear();
2078 //###########################
2079 //# E V E N T    Q U E U E
2080 //###########################
2082 void XmppEventTarget::eventQueueEnable(bool val)
2084     eventQueueEnabled = val;
2085     if (!eventQueueEnabled)
2086         eventQueue.clear();
2089 int XmppEventTarget::eventQueueAvailable()
2091     return eventQueue.size();
2094 XmppEvent XmppEventTarget::eventQueuePop()
2096     if (!eventQueueEnabled || eventQueue.size()<1)
2097         {
2098         XmppEvent dummy(XmppEvent::EVENT_NONE);
2099         return dummy;
2100         }
2101     XmppEvent event = *(eventQueue.begin());
2102     eventQueue.erase(eventQueue.begin());
2103     return event;
2107 //########################################################################
2108 //# X M P P    S T R E A M
2109 //########################################################################
2111 /**
2112  *
2113  */
2114 class XmppStream
2116 public:
2118     /**
2119      *
2120      */
2121     XmppStream();
2123     /**
2124      *
2125      */
2126     virtual ~XmppStream();
2128     /**
2129      *
2130      */
2131     virtual void reset();
2133     /**
2134      *
2135      */
2136     virtual int getState();
2138     /**
2139      *
2140      */
2141     virtual void setState(int val);
2143     /**
2144      *
2145      */
2146     virtual DOMString getStreamId();
2148     /**
2149      *
2150      */
2151     void setStreamId(const DOMString &val);
2153     /**
2154      *
2155      */
2156     virtual DOMString getIqId();
2158     /**
2159      *
2160      */
2161     void setIqId(const DOMString &val);
2163     /**
2164      *
2165      */
2166     virtual int getSeqNr();
2168     /**
2169      *
2170      */
2171     virtual DOMString getPeerId();
2173     /**
2174      *
2175      */
2176     virtual void setPeerId(const DOMString &val);
2178     /**
2179      *
2180      */
2181     int available();
2183     /**
2184      *
2185      */
2186     void receiveData(std::vector<unsigned char> &newData);
2188     /**
2189      *
2190      */
2191     std::vector<unsigned char> read();
2193 private:
2196     DOMString streamId;
2198     DOMString iqId;
2200     DOMString sourceId;
2202     int state;
2204     long seqNr;
2206     std::vector<unsigned char> data;
2207 };
2210 /**
2211  *
2212  */
2213 XmppStream::XmppStream()
2215     reset();
2218 /**
2219  *
2220  */
2221 XmppStream::~XmppStream()
2223     reset();
2226 /**
2227  *
2228  */
2229 void XmppStream::reset()
2231     state = XmppClient::STREAM_AVAILABLE;
2232     seqNr = 0;
2233     data.clear();
2236 /**
2237  *
2238  */
2239 int XmppStream::getState()
2241     return state;
2244 /**
2245  *
2246  */
2247 void XmppStream::setState(int val)
2249     state = val;
2252 /**
2253  *
2254  */
2255 DOMString XmppStream::getStreamId()
2257     return streamId;
2260 /**
2261  *
2262  */
2263 void XmppStream::setStreamId(const DOMString &val)
2265     streamId = val;
2268 /**
2269  *
2270  */
2271 DOMString XmppStream::getIqId()
2273     return iqId;
2277 /**
2278  *
2279  */
2280 void XmppStream::setIqId(const DOMString &val)
2282     iqId = val;
2285 /**
2286  *  Source or destination JID
2287  */
2288 void XmppStream::setPeerId(const DOMString &val)
2290     sourceId = val;
2293 /**
2294  *  Source or destination JID
2295  */
2296 DOMString XmppStream::getPeerId()
2298     return sourceId;
2301 /**
2302  *  Stream packet sequence number
2303  */
2304 int XmppStream::getSeqNr()
2306     seqNr++;
2307     if (seqNr >= 65535)
2308         seqNr = 0;
2309     return seqNr;
2312 /**
2313  *
2314  */
2315 int XmppStream::available()
2317     return data.size();
2320 /**
2321  *
2322  */
2323 void XmppStream::receiveData(std::vector<unsigned char> &newData)
2325     std::vector<unsigned char>::iterator iter;
2326     for (iter=newData.begin() ; iter!=newData.end() ; iter++)
2327         data.push_back(*iter);
2330 /**
2331  *
2332  */
2333 std::vector<unsigned char> XmppStream::read()
2335     if (state != XmppClient::STREAM_OPEN)
2336         {
2337         std::vector<unsigned char>dummy;
2338         return dummy;
2339         }
2340     std::vector<unsigned char> ret = data;
2341     data.clear();
2342     return ret;
2350 //########################################################################
2351 //# X M P P    C L I E N T
2352 //########################################################################
2353 class ReceiverThread : public Runnable
2355 public:
2357     ReceiverThread(XmppClient &par) : client(par) {}
2359     virtual ~ReceiverThread() {}
2361     void run()
2362       { client.receiveAndProcessLoop(); }
2364 private:
2366     XmppClient &client;
2367 };
2370 //###########################
2371 //# CONSTRUCTORS
2372 //###########################
2374 XmppClient::XmppClient()
2376     init();
2380 XmppClient::XmppClient(const XmppClient &other) : XmppEventTarget(other)
2382     init();
2383     assign(other);
2386 void XmppClient::assign(const XmppClient &other)
2388     msgId         = other.msgId;
2389     host          = other.host;
2390     realm         = other.realm;
2391     port          = other.port;
2392     username      = other.username;
2393     password      = other.password;
2394     resource      = other.resource;
2395     connected     = other.connected;
2396     groupChats    = other.groupChats;
2400 void XmppClient::init()
2402     sock          = new TcpSocket();
2403     msgId         = 0;
2404     connected     = false;
2406     for (int i=0 ; i<outputStreamCount ; i++)
2407         {
2408         outputStreams[i] = new XmppStream();
2409         }
2410     for (int i=0 ; i<inputStreamCount ; i++)
2411         {
2412         inputStreams[i] = new XmppStream();
2413         }
2414     for (int i=0 ; i<fileSendCount ; i++)
2415         {
2416         fileSends[i] = new XmppStream();
2417         }
2420 XmppClient::~XmppClient()
2422     disconnect();
2423     delete sock;
2424     for (int i=0 ; i<outputStreamCount ; i++)
2425         {
2426         delete outputStreams[i];
2427         }
2428     for (int i=0 ; i<inputStreamCount ; i++)
2429         {
2430         delete inputStreams[i];
2431         }
2432     for (int i=0 ; i<fileSendCount ; i++)
2433         {
2434         delete fileSends[i];
2435         }
2436     groupChatsClear();
2439 //##############################################
2440 //# UTILILY
2441 //##############################################
2443 /**
2444  *
2445  */
2446 bool XmppClient::pause(unsigned long millis)
2448     Thread::sleep(millis);
2449     return true;
2453 static int strIndex(const DOMString &str, char *key)
2455     unsigned int p = str.find(key);
2456     if (p == DOMString::npos)
2457         return -1;
2458     return p;
2462 static DOMString toXml(const DOMString &str)
2464     return Parser::encode(str);
2467 static DOMString trim(const DOMString &str)
2469     unsigned int i;
2470     for (i=0 ; i<str.size() ; i++)
2471         if (!isspace(str[i]))
2472             break;
2473     int start = i;
2474     for (i=str.size() ; i>0 ; i--)
2475         if (!isspace(str[i-1]))
2476             break;
2477     int end = i;
2478     if (start>=end)
2479         return "";
2480     return str.substr(start, end);
2483 //##############################################
2484 //# VARIABLES  (ones that need special handling)
2485 //##############################################
2486 /**
2487  *
2488  */
2489 DOMString XmppClient::getUsername()
2491     return username;
2494 /**
2495  *
2496  */
2497 void XmppClient::setUsername(const DOMString &val)
2499     int p = strIndex(val, "@");
2500     if (p > 0)
2501         {
2502         username = val.substr(0, p);
2503         realm    = val.substr(p+1, jid.size()-p-1);
2504         }
2505     else
2506        {
2507        realm    = host;
2508        username = val;
2509        }
2512 //##############################################
2513 //# CONNECTION
2514 //##############################################
2516 //#######################
2517 //# RECEIVING
2518 //#######################
2519 DOMString XmppClient::readStanza()
2521     int  openCount    = 0;
2522     bool inTag        = false;
2523     bool slashSeen    = false;
2524     bool trivialTag   = false;
2525     bool querySeen    = false;
2526     bool inQuote      = false;
2527     bool textSeen     = false;
2528     DOMString buf;
2530     while (true)
2531         {
2532         int ch = sock->read();
2533         //printf("%c", ch); fflush(stdout);
2534         if (ch<0)
2535             {
2536             if (ch == -2) //a simple timeout, not an error
2537                 {
2538                 //Since we are timed out, let's assume that we
2539                 //are between chunks of text.  Let's reset all states.
2540                 //printf("-----#### Timeout\n");
2541                 continue;
2542                 }
2543             else
2544                 {
2545                 keepGoing = false;
2546                 if (!sock->isConnected())
2547                     {
2548                     disconnect();
2549                     return "";
2550                     }
2551                 else
2552                     {
2553                     error("socket read error");
2554                     disconnect();
2555                     return "";
2556                     }
2557                 }
2558             }
2559         buf.push_back(ch);
2560         if (ch == '<')
2561             {
2562             inTag      = true;
2563             slashSeen  = false;
2564             querySeen  = false;
2565             inQuote    = false;
2566             textSeen   = false;
2567             trivialTag = false;
2568             }
2569         else if (ch == '>')
2570             {
2571             if (!inTag)  //unescaped '>' in pcdata? horror
2572                 continue;
2573             inTag     = false;
2574             if (!trivialTag && !querySeen)
2575                 {
2576                 if (slashSeen)
2577                     openCount--;
2578                 else
2579                     openCount++;
2580                 }
2581             //printf("# openCount:%d t:%d q:%d\n",
2582             //      openCount, trivialTag, querySeen);
2583             //check if we are 'balanced', but not a <?version?> tag
2584             if (openCount <= 0 && !querySeen)
2585                 {
2586                 break;
2587                 }
2588             //we know that this one will be open-ended
2589             if (strIndex(buf, "<stream:stream") >= 0)
2590                 {
2591                 buf.append("</stream:stream>");
2592                 break;
2593                 }
2594             }
2595         else if (ch == '/')
2596             {
2597             if (inTag && !inQuote)
2598                 {
2599                 slashSeen = true;
2600                 if (textSeen) // <tagName/>  <--looks like this
2601                     trivialTag = true;
2602                 }
2603             }
2604         else if (ch == '?')
2605             {
2606             if (inTag && !inQuote)
2607                 querySeen = true;
2608             }
2609         else if (ch == '"' || ch == '\'')
2610             {
2611             if (inTag)
2612                 inQuote = !inQuote;
2613             }
2614         else
2615             {
2616             if (inTag && !inQuote && !isspace(ch))
2617                 textSeen = true;
2618             }
2619         }
2620     return buf;
2625 static bool isGroupChat(Element *root)
2627     if (!root)
2628         return false;
2629     std::vector<Element *>elems = root->findElements("x");
2630     for (unsigned int i=0 ; i<elems.size() ; i++)
2631         {
2632         DOMString xmlns = elems[i]->getAttribute("xmlns");
2633         //printf("### XMLNS ### %s\n", xmlns.c_str());
2634         if (strIndex(xmlns, "http://jabber.org/protocol/muc") >=0 )
2635             return true;
2636         }
2637    return false;
2643 static bool getGroupIds(const DOMString &jid,
2644              DOMString &groupJid, DOMString &userNick)
2646     int p = strIndex(jid, "/");
2647     if (p < 0)
2648         {
2649         groupJid = jid;
2650         userNick = "";
2651         return true;
2652         }
2653     groupJid = jid.substr(0, p);
2654     userNick = jid.substr(p+1, jid.size()-p-1);
2655     return true;
2661 bool XmppClient::processMessage(Element *root)
2663     DOMString from    = root->getTagAttribute("message", "from");
2664     DOMString to      = root->getTagAttribute("message", "to");
2665     DOMString type    = root->getTagAttribute("message", "type");
2667     //####Check for embedded namespaces here
2668     //# IN BAND BYTESTREAMS
2669     DOMString ibbNamespace = "http://jabber.org/protocol/ibb";
2670     if (root->getTagAttribute("data", "xmlns") == ibbNamespace)
2671         {
2672         DOMString streamId = root->getTagAttribute("data", "sid");
2673         if (streamId.size() > 0)
2674             {
2675             for (int i=0 ; i<inputStreamCount ; i++)
2676                 {
2677                 XmppStream *ins = inputStreams[i];
2678                 //printf("##ins:%s  streamid:%s\n",
2679                 //    ins->getStreamId().c_str(),  streamId.c_str());
2680                 if (ins->getStreamId() == streamId)
2681                     {
2682                     //# We have a winner
2683                     if (ins->getState() != STREAM_OPEN)
2684                         {
2685                         XmppEvent event(XmppEvent::EVENT_ERROR);
2686                         event.setFrom(from);
2687                         event.setData("received unrequested stream data");
2688                         dispatchXmppEvent(event);
2689                         return true;
2690                         }
2691                     DOMString data = root->getTagValue("data");
2692                     std::vector<unsigned char>binData =
2693                                Base64Decoder::decode(data);
2694                     ins->receiveData(binData);
2695                     }
2696                 }
2697             }
2698         }
2701     //#### NORMAL MESSAGES
2702     DOMString subject = root->getTagValue("subject");
2703     DOMString body    = root->getTagValue("body");
2704     DOMString thread  = root->getTagValue("thread");
2705     //##rfc 3921, para 2.4.  ignore if no recognizable info
2706     if (subject.size() < 1 && body.size()<1 && thread.size()<1)
2707         return true;
2709     if (type == "groupchat")
2710         {
2711         DOMString fromGid;
2712         DOMString fromNick;
2713         getGroupIds(from, fromGid, fromNick);
2714         //printf("fromGid:%s  fromNick:%s\n",
2715         //        fromGid.c_str(), fromNick.c_str());
2716         DOMString toGid;
2717         DOMString toNick;
2718         getGroupIds(to, toGid, toNick);
2719         //printf("toGid:%s  toNick:%s\n",
2720         //        toGid.c_str(), toNick.c_str());
2722         if (fromNick.size() > 0)//normal group message
2723             {
2724             XmppEvent event(XmppEvent::EVENT_MUC_MESSAGE);
2725             event.setGroup(fromGid);
2726             event.setFrom(fromNick);
2727             event.setData(body);
2728             event.setDOM(root);
2729             dispatchXmppEvent(event);
2730             }
2731         else // from the server itself
2732             {
2733             //note the space before, so it doesnt match 'unlocked'
2734             if (strIndex(body, " locked") >= 0)
2735                 {
2736                 printf("LOCKED!! ;)\n");
2737                 char *fmt =
2738                 "<iq from='%s' id='create%d' to='%s' type='set'>"
2739                 "<query xmlns='http://jabber.org/protocol/muc#owner'>"
2740                 "<x xmlns='jabber:x:data' type='submit'/>"
2741                 "</query></iq>\n";
2742                 if (!write(fmt, jid.c_str(), msgId++, fromGid.c_str()))
2743                     return false;
2744                 }
2745             }
2746         }
2747     else
2748         {
2749         XmppEvent event(XmppEvent::EVENT_MESSAGE);
2750         event.setFrom(from);
2751         event.setData(body);
2752         event.setDOM(root);
2753         dispatchXmppEvent(event);
2754         }
2756     return true;
2762 bool XmppClient::processPresence(Element *root)
2765     DOMString from        = root->getTagAttribute("presence", "from");
2766     DOMString to          = root->getTagAttribute("presence", "to");
2767     DOMString presenceStr = root->getTagAttribute("presence", "type");
2768     bool presence = true;
2769     if (presenceStr == "unavailable")
2770         presence = false;
2771     DOMString status      = root->getTagValue("status");
2772     DOMString show        = root->getTagValue("show");
2774     if (isGroupChat(root))
2775         {
2776         DOMString fromGid;
2777         DOMString fromNick;
2778         getGroupIds(from, fromGid, fromNick);
2779         //printf("fromGid:%s  fromNick:%s\n",
2780         //        fromGid.c_str(), fromNick.c_str());
2781         DOMString item_jid = root->getTagAttribute("item", "jid");
2782         if (item_jid == jid) //Me
2783             {
2784             if (presence)
2785                 {
2786                 groupChatCreate(fromGid);
2787                 groupChatUserAdd(fromGid, fromNick);
2789                 XmppEvent event(XmppEvent::EVENT_MUC_JOIN);
2790                 event.setGroup(fromGid);
2791                 event.setFrom(fromNick);
2792                 event.setPresence(presence);
2793                 event.setShow(show);
2794                 event.setStatus(status);
2795                 dispatchXmppEvent(event);
2796                 }
2797             else
2798                 {
2799                 groupChatDelete(fromGid);
2800                 groupChatUserDelete(fromGid, fromNick);
2802                 XmppEvent event(XmppEvent::EVENT_MUC_LEAVE);
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             }
2811         else
2812             {
2813             if (presence)
2814                 groupChatUserAdd(fromGid, fromNick);
2815             else
2816                 groupChatUserDelete(fromGid, fromNick);
2817             XmppEvent event(XmppEvent::EVENT_MUC_PRESENCE);
2818             event.setGroup(fromGid);
2819             event.setFrom(fromNick);
2820             event.setPresence(presence);
2821             event.setShow(show);
2822             event.setStatus(status);
2823             dispatchXmppEvent(event);
2824             }
2825         }
2826     else
2827         {
2828         XmppEvent event(XmppEvent::EVENT_PRESENCE);
2829         event.setFrom(from);
2830         event.setPresence(presence);
2831         event.setShow(show);
2832         event.setStatus(status);
2833         dispatchXmppEvent(event);
2834         }
2836     return true;
2841 bool XmppClient::processIq(Element *root)
2843     DOMString from  = root->getTagAttribute("iq", "from");
2844     DOMString id    = root->getTagAttribute("iq", "id");
2845     DOMString type  = root->getTagAttribute("iq", "type");
2846     DOMString xmlns = root->getTagAttribute("query", "xmlns");
2848     if (id.size()<1)
2849         return true;
2851     //Group chat
2852     if (strIndex(xmlns, "http://jabber.org/protocol/muc") >=0 )
2853         {
2854         printf("results of MUC query\n");
2855         }
2856     //printf("###IQ xmlns:%s\n", xmlns.c_str());
2858     //### FILE TRANSFERS
2859     DOMString siNamespace = "http://jabber.org/protocol/si";
2860     if (root->getTagAttribute("si", "xmlns") == siNamespace)
2861         {
2862         if (type == "set")
2863             {
2864             DOMString streamId = root->getTagAttribute("si", "id");
2865             DOMString fname    = root->getTagAttribute("file", "name");
2866             DOMString sizeStr  = root->getTagAttribute("file", "size");
2867             DOMString hash     = root->getTagAttribute("file", "hash");
2868             XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_RECEIVE);
2869             event.setFrom(from);
2870             event.setIqId(id);
2871             event.setStreamId(streamId);
2872             event.setFileName(fname);
2873             event.setFileHash(hash);
2874             event.setFileSize(atol(sizeStr.c_str()));
2875             dispatchXmppEvent(event);
2876             }
2877         else  //result
2878             {
2879             printf("Got result\n");
2880             for (int i=0 ; i<fileSendCount ; i++)
2881                 {
2882                 XmppStream *outf = fileSends[i];
2883                 if (outf->getIqId() == id &&
2884                     from == outf->getPeerId())
2885                     {
2886                     if (type == "error")
2887                         {
2888                         outf->setState(STREAM_ERROR);
2889                         error("user '%s' rejected file", from.c_str());
2890                         return true;
2891                         }
2892                     else if (type == "result")
2893                         {
2894                         if (outf->getState() == STREAM_OPENING)
2895                             {
2896                             XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_ACCEPTED);
2897                             event.setFrom(from);
2898                             dispatchXmppEvent(event);
2899                             outf->setState(STREAM_OPEN);
2900                             }
2901                         else if (outf->getState() == STREAM_CLOSING)
2902                             {
2903                             outf->setState(STREAM_CLOSED);
2904                             }
2905                         return true;
2906                         }
2907                     }
2908                 }
2909             }
2910         return true;
2911         }
2914     //### IN-BAND BYTESTREAMS
2915     //### Incoming stream requests
2916     DOMString ibbNamespace = "http://jabber.org/protocol/ibb";
2917     if (root->getTagAttribute("open", "xmlns") == ibbNamespace)
2918         {
2919         DOMString streamId = root->getTagAttribute("open", "sid");
2920         XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_INIT);
2921         dispatchXmppEvent(event);
2922         if (streamId.size()>0)
2923             {
2924             for (int i=0 ; i<inputStreamCount ; i++)
2925                 {
2926                 XmppStream *ins = inputStreams[i];
2927                 if (ins->getStreamId() == streamId)
2928                     {
2929                     ins->setState(STREAM_OPENING);
2930                     ins->setIqId(id);
2931                     return true;
2932                     }
2933                 }
2934             }
2935         return true;
2936         }
2937     else if (root->getTagAttribute("close", "xmlns") == ibbNamespace)
2938         {
2939         XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_CLOSE);
2940         dispatchXmppEvent(event);
2941         DOMString streamId = root->getTagAttribute("close", "sid");
2942         if (streamId.size()>0)
2943             {
2944             for (int i=0 ; i<inputStreamCount ; i++)
2945                 {
2946                 XmppStream *ins = inputStreams[i];
2947                 if (ins->getStreamId() == streamId &&
2948                     from == ins->getPeerId())
2949                     {
2950                     ins->setState(STREAM_CLOSING);
2951                     ins->setIqId(id);
2952                     return true;
2953                     }
2954                 }
2955             }
2956         return true;
2957         }
2958     //### Responses to outgoing requests
2959     for (int i=0 ; i<outputStreamCount ; i++)
2960         {
2961         XmppStream *outs = outputStreams[i];
2962         if (outs->getIqId() == id)
2963             {
2964             if (type == "error")
2965                 {
2966                 outs->setState(STREAM_ERROR);
2967                 return true;
2968                 }
2969             else if (type == "result")
2970                 {
2971                 if (outs->getState() == STREAM_OPENING)
2972                     {
2973                     outs->setState(STREAM_OPEN);
2974                     }
2975                 else if (outs->getState() == STREAM_CLOSING)
2976                     {
2977                     outs->setState(STREAM_CLOSED);
2978                     }
2979                 return true;
2980                 }
2981             }
2982         }
2984     //###Normal Roster stuff
2985     if (root->getTagAttribute("query", "xmlns") == "jabber:iq:roster")
2986         {
2987         roster.clear();
2988         std::vector<Element *>elems = root->findElements("item");
2989         for (unsigned int i=0 ; i<elems.size() ; i++)
2990             {
2991             Element *item = elems[i];
2992             DOMString userJid      = item->getAttribute("jid");
2993             DOMString name         = item->getAttribute("name");
2994             DOMString subscription = item->getAttribute("subscription");
2995             DOMString group        = item->getTagValue("group");
2996             //printf("jid:%s name:%s sub:%s group:%s\n", userJid.c_str(), name.c_str(),
2997             //        subscription.c_str(), group.c_str());
2998             XmppUser user(userJid, name, subscription, group);
2999             roster.push_back(user);
3000             }
3001         XmppEvent event(XmppEvent::XmppEvent::EVENT_ROSTER);
3002         dispatchXmppEvent(event);
3003         }
3005     return true;
3010 bool XmppClient::receiveAndProcess()
3012     if (!keepGoing)
3013         return false;
3015     Parser parser;
3017     DOMString recvBuf = readStanza();
3018     recvBuf = trim(recvBuf);
3019     if (recvBuf.size() < 1)
3020         return true;
3022     //Ugly hack.  Apparently the first char can be dropped on timeouts
3023     //if (recvBuf[0] != '<')
3024     //    recvBuf.insert(0, "<");
3026     status("RECV: %s", recvBuf.c_str());
3027     Element *root = parser.parse(recvBuf);
3028     if (!root)
3029         {
3030         printf("Bad elem\n");
3031         return true;
3032         }
3034     //#### MESSAGE
3035     std::vector<Element *>elems = root->findElements("message");
3036     if (elems.size()>0)
3037         {
3038         if (!processMessage(root))
3039             return false;
3040         }
3042     //#### PRESENCE
3043     elems = root->findElements("presence");
3044     if (elems.size()>0)
3045         {
3046         if (!processPresence(root))
3047             return false;
3048         }
3050     //#### INFO
3051     elems = root->findElements("iq");
3052     if (elems.size()>0)
3053         {
3054         if (!processIq(root))
3055             return false;
3056         }
3058     delete root;
3060     return true;
3064 bool XmppClient::receiveAndProcessLoop()
3066     keepGoing = true;
3067     while (true)
3068         {
3069         if (!keepGoing)
3070             {
3071             printf("Abort requested\n");
3072             break;
3073             }
3074         if (!receiveAndProcess())
3075             return false;
3076         }
3077     return true;
3080 //#######################
3081 //# SENDING
3082 //#######################
3084 bool XmppClient::write(char *fmt, ...)
3086     va_list args;
3087     va_start(args,fmt);
3088     vsnprintf((char *)writeBuf, writeBufLen, fmt,args);
3089     va_end(args) ;
3090     status("SEND: %s", writeBuf);
3091     if (!sock->write((char *)writeBuf))
3092         {
3093         error("Cannot write to socket");
3094         return false;
3095         }
3096     return true;
3099 //#######################
3100 //# CONNECT
3101 //#######################
3103 bool XmppClient::checkConnect()
3105     if (!connected)
3106         {
3107         XmppEvent evt(XmppEvent::EVENT_ERROR);
3108         evt.setData("Attempted operation while disconnected");
3109         dispatchXmppEvent(evt);
3110         return false;
3111         }
3112     return true;
3115 bool XmppClient::iqAuthenticate(const DOMString &streamId)
3117     Parser parser;
3119     char *fmt =
3120     "<iq type='get' to='%s' id='auth%d'>"
3121     "<query xmlns='jabber:iq:auth'><username>%s</username></query>"
3122     "</iq>\n";
3123     if (!write(fmt, realm.c_str(), msgId++, username.c_str()))
3124         return false;
3126     DOMString recbuf = readStanza();
3127     //printf("iq received: '%s'\n", recbuf.c_str());
3128     Element *elem = parser.parse(recbuf);
3129     //elem->print();
3130     DOMString iqType = elem->getTagAttribute("iq", "type");
3131     //printf("##iqType:%s\n", iqType.c_str());
3132     delete elem;
3134     if (iqType != "result")
3135         {
3136         error("error:server does not allow login");
3137         return false;
3138         }
3140     bool digest = true;
3141     if (digest)
3142         {
3143         //## Digest authentication
3144         DOMString digest = streamId;
3145         digest.append(password);
3146         digest = Sha1::hashHex((unsigned char *)digest.c_str(), digest.size());
3147         //printf("digest:%s\n", digest.c_str());
3148         fmt =
3149         "<iq type='set' id='auth%d'>"
3150         "<query xmlns='jabber:iq:auth'>"
3151         "<username>%s</username>"
3152         "<digest>%s</digest>"
3153         "<resource>%s</resource>"
3154         "</query>"
3155         "</iq>\n";
3156         if (!write(fmt, msgId++, username.c_str(),
3157                     digest.c_str(), resource.c_str()))
3158             return false;
3159         }
3160     else
3161         {
3163         //## Plaintext authentication
3164         fmt =
3165         "<iq type='set' id='auth%d'>"
3166         "<query xmlns='jabber:iq:auth'>"
3167         "<username>%s</username>"
3168         "<password>%s</password>"
3169         "<resource>%s</resource>"
3170         "</query>"
3171         "</iq>\n";
3172         if (!write(fmt, msgId++, username.c_str(),
3173                    password.c_str(), resource.c_str()))
3174             return false;
3175         }
3177     recbuf = readStanza();
3178     //printf("iq received: '%s'\n", recbuf.c_str());
3179     elem = parser.parse(recbuf);
3180     //elem->print();
3181     iqType = elem->getTagAttribute("iq", "type");
3182     //printf("##iqType:%s\n", iqType.c_str());
3183     delete elem;
3185     if (iqType != "result")
3186         {
3187         error("server does not allow login");
3188         return false;
3189         }
3191     return true;
3196 bool XmppClient::saslMd5Authenticate()
3198     Parser parser;
3199     char *fmt =
3200     "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
3201     "mechanism='DIGEST-MD5'/>\n";
3202     if (!write(fmt))
3203         return false;
3205     DOMString recbuf = readStanza();
3206     status("challenge received: '%s'", recbuf.c_str());
3207     Element *elem = parser.parse(recbuf);
3208     //elem->print();
3209     DOMString b64challenge = elem->getTagValue("challenge");
3210     delete elem;
3212     if (b64challenge.size() < 1)
3213         {
3214         error("login: no SASL challenge offered by server");
3215         return false;
3216         }
3217     DOMString challenge = Base64Decoder::decodeToString(b64challenge);
3218     status("challenge:'%s'", challenge.c_str());
3220     unsigned int p1 = challenge.find("nonce=\"");
3221     if (p1 == DOMString::npos)
3222         {
3223         error("login: no SASL nonce sent by server");
3224         return false;
3225         }
3226     p1 += 7;
3227     unsigned int p2 = challenge.find("\"", p1);
3228     if (p2 == DOMString::npos)
3229         {
3230         error("login: unterminated SASL nonce sent by server");
3231         return false;
3232         }
3233     DOMString nonce = challenge.substr(p1, p2-p1);
3234     //printf("nonce: '%s'\n", nonce.c_str());
3235     char idBuf[7];
3236     snprintf(idBuf, 6, "%dsasl", msgId++);
3237     DOMString cnonce = Sha1::hashHex((unsigned char *)idBuf, 7);
3238     DOMString authzid = username; authzid.append("@"); authzid.append(host);
3239     DOMString digest_uri = "xmpp/"; digest_uri.append(host);
3241     //## Make A1
3242     Md5 md5;
3243     md5.append(username);
3244     md5.append(":");
3245     md5.append(realm);
3246     md5.append(":");
3247     md5.append(password);
3248     unsigned char a1tmp[16];
3249     md5.finish(a1tmp);
3250     md5.init();
3251     md5.append(a1tmp, 16);
3252     md5.append(":");
3253     md5.append(nonce);
3254     md5.append(":");
3255     md5.append(cnonce);
3256     md5.append(":");
3257     md5.append(authzid);
3258     DOMString a1 = md5.finishHex();
3259     status("##a1:'%s'", a1.c_str());
3261     //# Make A2
3262     md5.init();
3263     md5.append("AUTHENTICATE:");
3264     md5.append(digest_uri);
3265     DOMString a2 = md5.finishHex();
3266     status("##a2:'%s'", a2.c_str());
3268     //# Now make the response
3269     md5.init();
3270     md5.append(a1);
3271     md5.append(":");
3272     md5.append(nonce);
3273     md5.append(":");
3274     md5.append("00000001");//nc
3275     md5.append(":");
3276     md5.append(cnonce);
3277     md5.append(":");
3278     md5.append("auth");//qop
3279     md5.append(":");
3280     md5.append(a2);
3281     DOMString response = md5.finishHex();
3283     DOMString resp;
3284     resp.append("username=\""); resp.append(username); resp.append("\",");
3285     resp.append("realm=\"");    resp.append(realm);    resp.append("\",");
3286     resp.append("nonce=\"");    resp.append(nonce);    resp.append("\",");
3287     resp.append("cnonce=\"");   resp.append(cnonce);   resp.append("\",");
3288     resp.append("nc=00000001,qop=auth,");
3289     resp.append("digest-uri=\""); resp.append(digest_uri); resp.append("\"," );
3290     resp.append("authzid=\"");  resp.append(authzid);  resp.append("\",");
3291     resp.append("response=\""); resp.append(response); resp.append("\",");
3292     resp.append("charset=utf-8");
3293     status("sending response:'%s'", resp.c_str());
3294     resp = Base64Encoder::encode(resp);
3295     status("base64 response:'%s'", resp.c_str());
3296     fmt =
3297     "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>%s</response>\n";
3298     if (!write(fmt, resp.c_str()))
3299         return false;
3301     recbuf = readStanza();
3302     status("server says:: '%s'", recbuf.c_str());
3303     elem = parser.parse(recbuf);
3304     //elem->print();
3305     b64challenge = elem->getTagValue("challenge");
3306     delete elem;
3308     if (b64challenge.size() < 1)
3309         {
3310         error("login: no second SASL challenge offered by server");
3311         return false;
3312         }
3314     challenge = Base64Decoder::decodeToString(b64challenge);
3315     status("challenge: '%s'", challenge.c_str());
3316     p1 = challenge.find("rspauth=");
3317     if (p1 == DOMString::npos)
3318         {
3319         error("login: no SASL respauth sent by server\n");
3320         return false;
3321         }
3323     fmt =
3324     "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>\n";
3325     if (!write(fmt))
3326         return false;
3328     recbuf = readStanza();
3329     status("server says: '%s", recbuf.c_str());
3330     elem = parser.parse(recbuf);
3331     //elem->print();
3332     b64challenge = elem->getTagValue("challenge");
3333     bool success = (elem->findElements("success").size() > 0);
3334     delete elem;
3336     return success;
3339 bool XmppClient::saslPlainAuthenticate()
3341     Parser parser;
3343     DOMString id = username;
3344     //id.append("@");
3345     //id.append(host);
3346     Base64Encoder encoder;
3347     encoder.append('\0');
3348     encoder.append(id);
3349     encoder.append('\0');
3350     encoder.append(password);
3351     DOMString base64Auth = encoder.finish();
3352     //printf("authbuf:%s\n", base64Auth.c_str());
3354     char *fmt =
3355     "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
3356     "mechanism='PLAIN'>%s</auth>\n";
3357     if (!write(fmt, base64Auth.c_str()))
3358         return false;
3359     DOMString recbuf = readStanza();
3360     status("challenge received: '%s'", recbuf.c_str());
3361     Element *elem = parser.parse(recbuf);
3363     bool success = (elem->findElements("success").size() > 0);
3364     delete elem;
3366     return success;
3371 bool XmppClient::saslAuthenticate()
3373     Parser parser;
3375     DOMString recbuf = readStanza();
3376     status("RECV: '%s'\n", recbuf.c_str());
3377     Element *elem = parser.parse(recbuf);
3378     //elem->print();
3380     //Check for starttls
3381     bool wantStartTls = false;
3382     if (elem->findElements("starttls").size() > 0)
3383         {
3384         wantStartTls = true;
3385         if (elem->findElements("required").size() > 0)
3386             status("login: STARTTLS required");
3387         else
3388             status("login: STARTTLS available");
3389         }
3391     if (wantStartTls)
3392         {
3393         delete elem;
3394         char *fmt =
3395         "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n";
3396         if (!write(fmt))
3397             return false;
3398         recbuf = readStanza();
3399         status("RECV: '%s'\n", recbuf.c_str());
3400         elem = parser.parse(recbuf);
3401         if (elem->getTagAttribute("proceed", "xmlns").size()<1)
3402             {
3403             error("Server rejected TLS negotiation");
3404             disconnect();
3405             return false;
3406             }
3407         delete elem;
3408         if (!sock->startTls())
3409             {
3410             error("Could not start TLS");
3411             disconnect();
3412             return false;
3413             }
3415         fmt =
3416          "<stream:stream xmlns='jabber:client' "
3417          "xmlns:stream='http://etherx.jabber.org/streams' "
3418          "to='%s' version='1.0'>\n\n";
3419         if (!write(fmt, realm.c_str()))
3420             return false;
3422         recbuf = readStanza();
3423         status("RECVx: '%s'", recbuf.c_str());
3424         recbuf.append("</stream:stream>");
3425         elem = parser.parse(recbuf);
3426         bool success =
3427         (elem->getTagAttribute("stream:stream", "id").size()>0);
3428         if (!success)
3429             {
3430             error("STARTTLS negotiation failed");
3431             disconnect();
3432             return false;
3433             }
3434         delete elem;
3435         recbuf = readStanza();
3436         status("RECV: '%s'\n", recbuf.c_str());
3437         elem = parser.parse(recbuf);
3438         }
3440     //check for sasl authentication mechanisms
3441     std::vector<Element *> elems =
3442                elem->findElements("mechanism");
3443     if (elems.size() < 1)
3444         {
3445         error("login: no SASL mechanism offered by server");
3446         return false;
3447         }
3448     bool md5Found = false;
3449     bool plainFound = false;
3450     for (unsigned int i=0 ; i<elems.size() ; i++)
3451         {
3452         DOMString mech = elems[i]->getValue();
3453         if (mech == "DIGEST-MD5")
3454             {
3455             status("MD5 authentication offered");
3456             md5Found = true;
3457             }
3458         else if (mech == "PLAIN")
3459             {
3460             status("PLAIN authentication offered");
3461             plainFound = true;
3462             }
3463         }
3464     delete elem;
3466     bool success = false;
3467     if (md5Found)
3468         {
3469         success = saslMd5Authenticate();
3470         }
3471     else if (plainFound)
3472         {
3473         success = saslPlainAuthenticate();
3474         }
3475     else
3476         {
3477         error("not able to handle sasl authentication mechanisms");
3478         return false;
3479         }
3481     if (success)
3482         status("###### SASL authentication success\n");
3483     else
3484         error("###### SASL authentication failure\n");
3486     return success;
3493 bool XmppClient::createSession()
3496     Parser parser;
3497     if (port==443 || port==5223)
3498         sock->enableSSL(true);
3499     if (!sock->connect(host, port))
3500         {
3501         return false;
3502         }
3504     char *fmt =
3505      "<stream:stream "
3506           "to='%s' "
3507           "xmlns='jabber:client' "
3508           "xmlns:stream='http://etherx.jabber.org/streams' "
3509           "version='1.0'>\n\n";
3510     if (!write(fmt, realm.c_str()))
3511         return false;
3513     DOMString recbuf = readStanza();
3514     //printf("received: '%s'\n", recbuf.c_str());
3515     recbuf.append("</stream:stream>");
3516     Element *elem = parser.parse(recbuf);
3517     //elem->print();
3518     bool useSasl = false;
3519     DOMString streamId = elem->getTagAttribute("stream:stream", "id");
3520     //printf("### StreamID: %s\n", streamId.c_str());
3521     DOMString streamVersion = elem->getTagAttribute("stream:stream", "version");
3522     if (streamVersion == "1.0")
3523         useSasl = true;
3525     if (useSasl)
3526         {
3527         if (!saslAuthenticate())
3528             return false;
3529         fmt =
3530           "<stream:stream "
3531           "to='%s' "
3532           "xmlns='jabber:client' "
3533           "xmlns:stream='http://etherx.jabber.org/streams' "
3534           "version='1.0'>\n\n";
3536         if (!write(fmt, realm.c_str()))
3537             return false;
3538         recbuf = readStanza();
3539         recbuf.append("</stream:stream>\n");
3540         //printf("now server says:: '%s'\n", recbuf.c_str());
3541         elem = parser.parse(recbuf);
3542         //elem->print();
3543         delete elem;
3545         recbuf = readStanza();
3546         //printf("now server says:: '%s'\n", recbuf.c_str());
3547         elem = parser.parse(recbuf);
3548         bool hasBind = (elem->findElements("bind").size() > 0);
3549         //elem->print();
3550         delete elem;
3552         if (!hasBind)
3553             {
3554             error("no binding provided by server");
3555             return false;
3556             }
3559         }
3560     else // not SASL
3561         {
3562         if (!iqAuthenticate(streamId))
3563             return false;
3564         }
3567     //### Resource binding
3568     fmt =
3569     "<iq type='set' id='bind%d'>"
3570     "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
3571     "<resource>%s</resource>"
3572     "</bind></iq>\n";
3573     if (!write(fmt, msgId++, resource.c_str()))
3574         return false;
3576     recbuf = readStanza();
3577     status("bind result: '%s'", recbuf.c_str());
3578     elem = parser.parse(recbuf);
3579     //elem->print();
3580     DOMString bindType = elem->getTagAttribute("iq", "type");
3581     //printf("##bindType:%s\n", bindType.c_str());
3582     delete elem;
3584     if (bindType != "result")
3585         {
3586         error("no binding with server failed");
3587         return false;
3588         }
3590     fmt =
3591     "<iq type='set' id='sess%d'>"
3592     "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
3593     "</iq>\n";
3594     if (!write(fmt, msgId++))
3595         return false;
3597     recbuf = readStanza();
3598     status("session received: '%s'", recbuf.c_str());
3599     elem = parser.parse(recbuf);
3600     //elem->print();
3601     DOMString sessionType = elem->getTagAttribute("iq", "type");
3602     //printf("##sessionType:%s\n", sessionType.c_str());
3603     delete elem;
3605     if (sessionType != "result")
3606         {
3607         error("no session provided by server");
3608         return false;
3609         }
3611     //printf("########## COOL #########\n");
3612     //Now that we are bound, we have a valid JID
3613     jid = username;
3614     jid.append("@");
3615     jid.append(realm);
3616     jid.append("/");
3617     jid.append(resource);
3619     //We are now done with the synchronous handshaking.  Let's go into
3620     //async mode
3622     fmt =
3623      "<presence/>\n";
3624     if (!write(fmt))
3625         return false;
3627     fmt =
3628      "<iq type='get' id='roster%d'><query xmlns='jabber:iq:roster'/></iq>\n";
3629     if (!write(fmt, msgId++))
3630         return false;
3632     fmt =
3633      "<iq type='get' id='discoItems%d' to='%s'>"
3634      "<query xmlns='http://jabber.org/protocol/disco#items'/></iq>\n";
3635     if (!write(fmt, msgId++, realm.c_str()))
3636         return false;
3638     fmt =
3639     "<iq type='get' id='discoInfo%d' to='conference.%s'>"
3640     "<query xmlns='http://jabber.org/protocol/disco#info'/></iq>\n";
3641     if (!write(fmt, msgId++, realm.c_str()))
3642         return false;
3644     /*
3645     recbuf = readStanza();
3646     status("stream received: '%s'", recbuf.c_str());
3647     elem = parser.parse(recbuf);
3648     //elem->print();
3649     delete elem;
3650     */
3652     //We are now logged in
3653     status("Connected");
3654     connected = true;
3655     XmppEvent evt(XmppEvent::EVENT_CONNECTED);
3656     dispatchXmppEvent(evt);
3657     //Thread::sleep(1000000);
3659     sock->setReceiveTimeout(1000);
3660     ReceiverThread runner(*this);
3661     Thread thread(runner);
3662     thread.start();
3664     return true;
3667 bool XmppClient::connect()
3669     if (!createSession())
3670         {
3671         disconnect();
3672         return false;
3673         }
3674     return true;
3678 bool XmppClient::connect(DOMString hostArg, int portArg,
3679                          DOMString usernameArg,
3680                          DOMString passwordArg,
3681                          DOMString resourceArg)
3683     host     = hostArg;
3684     port     = portArg;
3685     password = passwordArg;
3686     resource = resourceArg;
3688     //parse this one
3689     setUsername(usernameArg);
3691     bool ret = connect();
3692     return ret;
3695 bool XmppClient::disconnect()
3697     if (connected)
3698         {
3699         char *fmt =
3700         "<presence from='%s' type='unavailable'/>\n";
3701         write(fmt, jid.c_str());
3702         }
3703     keepGoing = false;
3704     connected = false;
3705     Thread::sleep(3000); //allow receiving thread to quit
3706     sock->disconnect();
3707     roster.clear();
3708     groupChatsClear();
3709     XmppEvent event(XmppEvent::EVENT_DISCONNECTED);
3710     dispatchXmppEvent(event);
3711     return true;
3714 //#######################
3715 //# ROSTER
3716 //#######################
3718 bool XmppClient::rosterAdd(const DOMString &rosterGroup,
3719                            const DOMString &otherJid,
3720                            const DOMString &name)
3722     if (!checkConnect())
3723         return false;
3724     char *fmt =
3725     "<iq from='%s' type='set' id='roster_%d'>"
3726     "<query xmlns='jabber:iq:roster'>"
3727     "<item jid='%s' name='%s'><group>%s</group></item>"
3728     "</query></iq>\n";
3729     if (!write(fmt, jid.c_str(), msgId++, otherJid.c_str(),
3730          name.c_str(), rosterGroup.c_str()))
3731         {
3732         return false;
3733         }
3734     return true;
3737 bool XmppClient::rosterDelete(const DOMString &otherJid)
3739     if (!checkConnect())
3740         return false;
3741     char *fmt =
3742     "<iq from='%s' type='set' id='roster_%d'>"
3743     "<query xmlns='jabber:iq:roster'>"
3744     "<item jid='%s' subscription='remove'><group>%s</group></item>"
3745     "</query></iq>\n";
3746     if (!write(fmt, jid.c_str(), msgId++, otherJid.c_str()))
3747         {
3748         return false;
3749         }
3750     return true;
3754 static bool xmppRosterCompare(const XmppUser& p1, const XmppUser& p2)
3756     DOMString s1 = p1.group;
3757     DOMString s2 = p2.group;
3758     for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
3759         {
3760         int comp = tolower(s1[len]) - tolower(s2[len]);
3761         if (comp)
3762             return (comp<0);
3763         }
3765     s1 = p1.jid;
3766     s2 = p2.jid;
3767     for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
3768         {
3769         int comp = tolower(s1[len]) - tolower(s2[len]);
3770         if (comp)
3771             return (comp<0);
3772         }
3773     return false;
3776 std::vector<XmppUser> XmppClient::getRoster()
3778     std::vector<XmppUser> ros = roster;
3779     std::sort(ros.begin(), ros.end(), xmppRosterCompare);
3780     return ros;
3783 //#######################
3784 //# CHAT (individual)
3785 //#######################
3787 bool XmppClient::message(const DOMString &user, const DOMString &subj,
3788                          const DOMString &msg)
3790     if (!checkConnect())
3791         return false;
3793     DOMString xmlSubj = toXml(subj);
3794     DOMString xmlMsg  = toXml(msg);
3796     if (xmlSubj.size() > 0)
3797         {
3798         char *fmt =
3799         "<message from='%s' to='%s' type='chat'>"
3800         "<subject>%s</subject><body>%s</body></message>\n";
3801         if (!write(fmt, jid.c_str(), user.c_str(),
3802                 xmlSubj.c_str(), xmlMsg.c_str()))
3803             return false;
3804         }
3805     else
3806         {
3807         char *fmt =
3808         "<message from='%s' to='%s'>"
3809         "<body>%s</body></message>\n";
3810         if (!write(fmt, jid.c_str(), user.c_str(), xmlMsg.c_str()))
3811             return false;
3812         }
3813     return true;
3818 bool XmppClient::message(const DOMString &user, const DOMString &msg)
3820     return message(user, "", msg);
3825 bool XmppClient::presence(const DOMString &presence)
3827     if (!checkConnect())
3828         return false;
3830     DOMString xmlPres = toXml(presence);
3832     char *fmt =
3833     "<presence from='%s'><show>%s</show></presence>\n";
3834     if (!write(fmt, jid.c_str(), xmlPres.c_str()))
3835         return false;
3836     return true;
3839 //#######################
3840 //# GROUP  CHAT
3841 //#######################
3843 bool XmppClient::groupChatCreate(const DOMString &groupJid)
3845     std::vector<XmppGroupChat *>::iterator iter;
3846     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
3847         {
3848         if ((*iter)->getGroupJid() == groupJid)
3849             {
3850             error("Group chat '%s' already exists", groupJid.c_str());
3851             return false;
3852             }
3853         }
3854     XmppGroupChat *chat = new XmppGroupChat(groupJid);
3855     groupChats.push_back(chat);
3856     return true;
3859 /**
3860  *
3861  */
3862 void XmppClient::groupChatDelete(const DOMString &groupJid)
3864     std::vector<XmppGroupChat *>::iterator iter;
3865     for (iter=groupChats.begin() ; iter!=groupChats.end() ; )
3866         {
3867         XmppGroupChat *chat = *iter;
3868         if (chat->getGroupJid() == groupJid)
3869             {
3870             iter = groupChats.erase(iter);
3871             delete chat;
3872             }
3873         else
3874             iter++;
3875         }
3878 /**
3879  *
3880  */
3881 bool XmppClient::groupChatExists(const DOMString &groupJid)
3883     std::vector<XmppGroupChat *>::iterator iter;
3884     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
3885         if ((*iter)->getGroupJid() == groupJid)
3886             return true;
3887     return false;
3890 /**
3891  *
3892  */
3893 void XmppClient::groupChatsClear()
3895     std::vector<XmppGroupChat *>::iterator iter;
3896     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
3897         delete (*iter);
3898     groupChats.clear();
3902 /**
3903  *
3904  */
3905 void XmppClient::groupChatUserAdd(const DOMString &groupJid,
3906                                   const DOMString &nick)
3908     std::vector<XmppGroupChat *>::iterator iter;
3909     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
3910         {
3911         if ((*iter)->getGroupJid() == groupJid)
3912             {
3913             (*iter)->userAdd("", nick);
3914             }
3915         }
3918 /**
3919  *
3920  */
3921 void XmppClient::groupChatUserDelete(const DOMString &groupJid,
3922                                      const DOMString &nick)
3924     std::vector<XmppGroupChat *>::iterator iter;
3925     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
3926         {
3927         if ((*iter)->getGroupJid() == groupJid)
3928             {
3929             (*iter)->userDelete("", nick);
3930             }
3931         }
3934 static bool xmppUserCompare(const XmppUser& p1, const XmppUser& p2)
3936     DOMString s1 = p1.nick;
3937     DOMString s2 = p2.nick;
3938     int comp = 0;
3939     for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
3940         {
3941         comp = tolower(s1[len]) - tolower(s2[len]);
3942         if (comp)
3943             break;
3944         }
3945     return (comp<0);
3949 std::vector<XmppUser> XmppClient::groupChatGetUserList(
3950                               const DOMString &groupJid)
3952     if (!checkConnect())
3953         {
3954         std::vector<XmppUser> dummy;
3955         return dummy;
3956         }
3958     std::vector<XmppGroupChat *>::iterator iter;
3959     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
3960         {
3961         if ((*iter)->getGroupJid() == groupJid )
3962             {
3963             std::vector<XmppUser> uList = (*iter)->getUserList();
3964             std::sort(uList.begin(), uList.end(), xmppUserCompare);
3965             return uList;
3966             }
3967         }
3968     std::vector<XmppUser> dummy;
3969     return dummy;
3972 bool XmppClient::groupChatJoin(const DOMString &groupJid,
3973                                const DOMString &nick,
3974                                const DOMString &pass)
3976     if (!checkConnect())
3977         return false;
3979     DOMString user = nick;
3980     if (user.size()<1)
3981         user = username;
3983     char *fmt =
3984     "<presence to='%s/%s'>"
3985     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
3986     if (!write(fmt, groupJid.c_str(), user.c_str()))
3987         return false;
3988     return true;
3992 bool XmppClient::groupChatLeave(const DOMString &groupJid,
3993                                 const DOMString &nick)
3995     if (!checkConnect())
3996         return false;
3998     DOMString user = nick;
3999     if (user.size()<1)
4000         user = username;
4002     char *fmt =
4003     "<presence to='%s/%s' type='unavailable'>"
4004     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
4005     if (!write(fmt, groupJid.c_str(), user.c_str()))
4006         return false;
4007     return true;
4011 bool XmppClient::groupChatMessage(const DOMString &groupJid,
4012                                   const DOMString &msg)
4014     if (!checkConnect())
4015         {
4016         return false;
4017         }
4019     DOMString xmlMsg = toXml(msg);
4021     char *fmt =
4022     "<message from='%s' to='%s' type='groupchat'>"
4023     "<body>%s</body></message>\n";
4024     if (!write(fmt, jid.c_str(), groupJid.c_str(), xmlMsg.c_str()))
4025         return false;
4026     return true;
4029 bool XmppClient::groupChatPrivateMessage(const DOMString &groupJid,
4030                                          const DOMString &toNick,
4031                                          const DOMString &msg)
4033     if (!checkConnect())
4034         return false;
4036     DOMString xmlMsg = toXml(msg);
4038     char *fmt =
4039     "<message from='%s' to='%s/%s' type='chat'>"
4040     "<body>%s</body></message>\n";
4041     if (!write(fmt, jid.c_str(), groupJid.c_str(),
4042                toNick.c_str(), xmlMsg.c_str()))
4043         return false;
4044     return true;
4047 bool XmppClient::groupChatPresence(const DOMString &groupJid,
4048                                    const DOMString &myNick,
4049                                    const DOMString &presence)
4051     if (!checkConnect())
4052         return false;
4054     DOMString user = myNick;
4055     if (user.size()<1)
4056         user = username;
4058     DOMString xmlPresence = toXml(presence);
4060     char *fmt =
4061     "<presence from='%s' to='%s/%s' type='unavailable'>"
4062     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
4063     if (!write(fmt, jid.c_str(), groupJid.c_str(), user.c_str(), xmlPresence.c_str()))
4064         return true;
4065     return true;
4070 //#######################
4071 //# S T R E A M S
4072 //#######################
4075 /**
4076  *
4077  */
4078 int XmppClient::outputStreamOpen(const DOMString &destId,
4079                                  const DOMString &streamIdArg)
4081     int i;
4082     for (i=0; i<outputStreamCount ; i++)
4083         if (outputStreams[i]->getState() == STREAM_AVAILABLE)
4084             break;
4085     if (i>=outputStreamCount)
4086         {
4087         error("No available output streams");
4088         return -1;
4089         }
4090     int streamNr = i;
4091     XmppStream *outs = outputStreams[streamNr];
4093     outs->setState(STREAM_OPENING);
4095     char buf[32];
4096     snprintf(buf, 31, "inband%d", getMsgId());
4097     DOMString iqId = buf;
4099     DOMString streamId = streamIdArg;
4100     if (streamId.size()<1)
4101         {
4102         snprintf(buf, 31, "stream%d", getMsgId());
4103         DOMString streamId = buf;
4104         }
4105     outs->setIqId(iqId);
4106     outs->setStreamId(streamId);
4107     outs->setPeerId(destId);
4109     char *fmt =
4110     "<iq type='set' from='%s' to='%s' id='%s'>"
4111     "<open sid='%s' block-size='4096'"
4112     " xmlns='http://jabber.org/protocol/ibb'/></iq>\n";
4113     if (!write(fmt, jid.c_str(),
4114               destId.c_str(), iqId.c_str(),
4115               streamId.c_str()))
4116         {
4117         outs->reset();
4118         return -1;
4119         }
4121     int state = outs->getState();
4122     for (int tim=0 ; tim<20 ; tim++)
4123         {
4124         if (state == STREAM_OPEN)
4125             break;
4126         else if (state == STREAM_ERROR)
4127             {
4128             printf("ERROR\n");
4129             outs->reset();
4130             return -1;
4131             }
4132         Thread::sleep(1000);
4133         state = outs->getState();
4134         }
4135     if (state != STREAM_OPEN)
4136         {
4137         printf("TIMEOUT ERROR\n");
4138         outs->reset();
4139         return -1;
4140         }
4142     return streamNr;
4145 /**
4146  *
4147  */
4148 int XmppClient::outputStreamWrite(int streamNr,
4149                       const unsigned char *buf, unsigned long len)
4151     XmppStream *outs = outputStreams[streamNr];
4153     unsigned long outLen = 0;
4154     unsigned char *p = (unsigned char *)buf;
4156     while (outLen < len)
4157         {
4158         unsigned long chunksize = 1024;
4159         if (chunksize + outLen > len)
4160             chunksize = len - outLen;
4162         Base64Encoder encoder;
4163         encoder.append(p, chunksize);
4164         DOMString b64data = encoder.finish();
4165         p      += chunksize;
4166         outLen += chunksize;
4168         char *fmt =
4169         "<message from='%s' to='%s' id='msg%d'>"
4170         "<data xmlns='http://jabber.org/protocol/ibb' sid='%s' seq='%d'>"
4171         "%s"
4172         "</data>"
4173         "<amp xmlns='http://jabber.org/protocol/amp'>"
4174         "<rule condition='deliver-at' value='stored' action='error'/>"
4175         "<rule condition='match-resource' value='exact' action='error'/>"
4176         "</amp>"
4177         "</message>\n";
4178         if (!write(fmt, jid.c_str(),
4179               outs->getPeerId().c_str(),
4180               getMsgId(),
4181               outs->getStreamId().c_str(),
4182               outs->getSeqNr(),
4183               b64data.c_str()))
4184             {
4185             outs->reset();
4186             return -1;
4187             }
4188         pause(5000);
4189         }
4190     return outLen;
4193 /**
4194  *
4195  */
4196 int XmppClient::outputStreamClose(int streamNr)
4198     XmppStream *outs = outputStreams[streamNr];
4200     char buf[32];
4201     snprintf(buf, 31, "inband%d", getMsgId());
4202     DOMString iqId = buf;
4203     outs->setIqId(iqId);
4205     outs->setState(STREAM_CLOSING);
4206     char *fmt =
4207     "<iq type='set' from='%s' to='%s' id='%s'>"
4208     "<close sid='%s' xmlns='http://jabber.org/protocol/ibb'/></iq>\n";
4209     if (!write(fmt, jid.c_str(),
4210                     outs->getPeerId().c_str(),
4211                     iqId.c_str(),
4212                     outs->getStreamId().c_str()))
4213         return false;
4215     int state = outs->getState();
4216     for (int tim=0 ; tim<20 ; tim++)
4217         {
4218         if (state == STREAM_CLOSED)
4219             break;
4220         else if (state == STREAM_ERROR)
4221             {
4222             printf("ERROR\n");
4223             outs->reset();
4224             return -1;
4225             }
4226         Thread::sleep(1000);
4227         state = outs->getState();
4228         }
4229     if (state != STREAM_CLOSED)
4230         {
4231         printf("TIMEOUT ERROR\n");
4232         outs->reset();
4233         return -1;
4234         }
4236     outs->reset();
4237     return 1;
4241 /**
4242  *
4243  */
4244 int XmppClient::inputStreamOpen(const DOMString &fromJid, const DOMString &streamId,
4245                                 const DOMString &iqId)
4247     int i;
4248     for (i=0 ; i<inputStreamCount ; i++)
4249         {
4250         if (inputStreams[i]->getState() == STREAM_AVAILABLE)
4251             break;
4252         }
4253     if (i>=inputStreamCount)
4254         {
4255         error("No available input streams");
4256         return -1;
4257         }
4258     int streamNr = i;
4259     XmppStream *ins = inputStreams[streamNr];
4260     ins->reset();
4261     ins->setPeerId(fromJid);
4262     ins->setState(STREAM_CLOSED);
4263     ins->setStreamId(streamId);
4265     int state = ins->getState();
4266     for (int tim=0 ; tim<20 ; tim++)
4267         {
4268         if (state == STREAM_OPENING)
4269             break;
4270         else if (state == STREAM_ERROR)
4271             {
4272             printf("ERROR\n");
4273             ins->reset();
4274             return -1;
4275             }
4276         Thread::sleep(1000);
4277         state = ins->getState();
4278         }
4279     if (state != STREAM_OPENING)
4280         {
4281         printf("TIMEOUT ERROR\n");
4282         ins->reset();
4283         return -1;
4284         }
4285     char *fmt =
4286     "<iq type='result' from='%s' to='%s' id='%s'/>\n";
4287     if (!write(fmt, jid.c_str(),  fromJid.c_str(), ins->getIqId().c_str()))
4288         {
4289         return -1;
4290         }
4292     ins->setState(STREAM_OPEN);
4293     return streamNr;
4296 /**
4297  *
4298  */
4299 int XmppClient::inputStreamAvailable(int streamNr)
4301     XmppStream *ins = inputStreams[streamNr];
4302     return ins->available();
4305 /**
4306  *
4307  */
4308 std::vector<unsigned char> XmppClient::inputStreamRead(int streamNr)
4310     XmppStream *ins = inputStreams[streamNr];
4311     return ins->read();
4314 /**
4315  *
4316  */
4317 bool XmppClient::inputStreamClosing(int streamNr)
4319     XmppStream *ins = inputStreams[streamNr];
4320     if (ins->getState() == STREAM_CLOSING)
4321         return true;
4322     return false;
4326 /**
4327  *
4328  */
4329 int XmppClient::inputStreamClose(int streamNr)
4331     int ret=1;
4332     XmppStream *ins = inputStreams[streamNr];
4333     if (ins->getState() == STREAM_CLOSING)
4334         {
4335         char *fmt =
4336         "<iq type='result' from='%s' to='%s' id='%s'/>\n";
4337         if (!write(fmt, jid.c_str(),  ins->getPeerId().c_str(),
4338                     ins->getIqId().c_str()))
4339             {
4340             ret = -1;
4341             }
4342         }
4343     ins->reset();
4344     return ret;
4350 //#######################
4351 //# FILE   TRANSFERS
4352 //#######################
4355 /**
4356  *
4357  */
4358 bool XmppClient::fileSend(const DOMString &destJidArg,
4359                           const DOMString &offeredNameArg,
4360                           const DOMString &fileNameArg,
4361                           const DOMString &descriptionArg)
4363     DOMString destJid     = destJidArg;
4364     DOMString offeredName = offeredNameArg;
4365     DOMString fileName    = fileNameArg;
4366     DOMString description = descriptionArg;
4368     int i;
4369     for (i=0; i<fileSendCount ; i++)
4370         if (fileSends[i]->getState() == STREAM_AVAILABLE)
4371             break;
4372     if (i>=fileSendCount)
4373         {
4374         error("No available file send streams");
4375         return false;
4376         }
4377     int fileSendNr = i;
4378     XmppStream *outf = fileSends[fileSendNr];
4380     outf->setState(STREAM_OPENING);
4382     struct stat finfo;
4383     if (stat(fileName.c_str(), &finfo)<0)
4384         {
4385         error("Cannot stat file '%s' for sending", fileName.c_str());
4386         return false;
4387         }
4388     long fileLen = finfo.st_size;
4389     if (!fileLen > 1000000)
4390         {
4391         error("'%s' too large", fileName.c_str());
4392         return false;
4393         }
4394     if (!S_ISREG(finfo.st_mode))
4395         {
4396         error("'%s' is not a regular file", fileName.c_str());
4397         return false;
4398         }
4399     FILE *f = fopen(fileName.c_str(), "rb");
4400     if (!f)
4401         {
4402         error("cannot open '%s' for sending", fileName.c_str());
4403         return false;
4404         }
4405     unsigned char *sendBuf = (unsigned char *)malloc(fileLen+1);
4406     if (!sendBuf)
4407         {
4408         error("cannot cannot allocate send buffer for %s", fileName.c_str());
4409         return false;
4410         }
4411     for (long i=0 ; i<fileLen && !feof(f); i++)
4412         {
4413         sendBuf[i] = fgetc(f);
4414         }
4415     fclose(f);
4417     //## get the last path segment from the whole path
4418     if (offeredName.size()<1)
4419         {
4420         int slashPos = -1;
4421         for (unsigned int i=0 ; i<fileName.size() ; i++)
4422             {
4423             int ch = fileName[i];
4424             if (ch == '/' || ch == '\\')
4425                 slashPos = i;
4426             }
4427         if (slashPos>=0 && slashPos<=(int)(fileName.size()-1))
4428             {
4429             offeredName = fileName.substr(slashPos+1,
4430                                           fileName.size()-slashPos-1);
4431             printf("offeredName:%s\n", offeredName.c_str());
4432             }
4433         }
4435     char buf[32];
4436     snprintf(buf, 31, "file%d", getMsgId());
4437     DOMString iqId = buf;
4438     outf->setIqId(iqId);
4440     snprintf(buf, 31, "stream%d", getMsgId());
4441     DOMString streamId = buf;
4442     //outf->setStreamId(streamId);
4444     DOMString hash = Md5::hashHex(sendBuf, fileLen);
4445     printf("Hash:%s\n", hash.c_str());
4447     outf->setPeerId(destJid);
4449     char dtgBuf[81];
4450     struct tm *timeVal = gmtime(&(finfo.st_mtime));
4451     strftime(dtgBuf, 80, "%Y-%m-%dT%H:%M:%Sz", timeVal);
4453     char *fmt =
4454     "<iq type='set' id='%s' to='%s'>"
4455     "<si xmlns='http://jabber.org/protocol/si' id='%s'"
4456       " mime-type='text/plain'"
4457       " profile='http://jabber.org/protocol/si/profile/file-transfer'>"
4458     "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'"
4459           " name='%s' size='%d' hash='%s' date='%s'><desc>%s</desc></file>"
4460     "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
4461     "<x xmlns='jabber:x:data' type='form'>"
4462     "<field var='stream-method' type='list-single'>"
4463     //"<option><value>http://jabber.org/protocol/bytestreams</value></option>"
4464     "<option><value>http://jabber.org/protocol/ibb</value></option>"
4465     "</field></x></feature></si></iq>\n";
4466     if (!write(fmt, iqId.c_str(), destJid.c_str(),
4467          streamId.c_str(), offeredName.c_str(), fileLen,
4468          hash.c_str(), dtgBuf, description.c_str()))
4469         {
4470         free(sendBuf);
4471         return false;
4472         }
4474     int state = outf->getState();
4475     for (int tim=0 ; tim<20 ; tim++)
4476         {
4477         printf("##### waiting for open\n");
4478         if (state == STREAM_OPEN)
4479             {
4480             outf->reset();
4481             break;
4482             }
4483         else if (state == STREAM_ERROR)
4484             {
4485             printf("ERROR\n");
4486             outf->reset();
4487             return false;
4488             }
4489         Thread::sleep(1000);
4490         state = outf->getState();
4491         }
4492     if (state != STREAM_OPEN)
4493         {
4494         printf("TIMEOUT ERROR\n");
4495         outf->reset();
4496         return false;
4497         }
4499     //free up this reqource
4500     outf->reset();
4502     int  streamNr = outputStreamOpen(destJid, streamId);
4503     if (streamNr<0)
4504         {
4505         error("cannot open output stream %s", streamId.c_str());
4506         outf->reset();
4507         return false;
4508         }
4510     int ret = outputStreamWrite(streamNr, sendBuf, fileLen);
4512     if (ret<0)
4513         {
4514         }
4516     outputStreamClose(streamNr);
4518     free(sendBuf);
4519     return true;
4523 class FileSendThread : public Thread
4525 public:
4527     FileSendThread(XmppClient &par,
4528                    const DOMString &destJidArg,
4529                    const DOMString &offeredNameArg,
4530                    const DOMString &fileNameArg,
4531                    const DOMString &descriptionArg) : client(par)
4532         {
4533         destJid     = destJidArg;
4534         offeredName = offeredNameArg;
4535         fileName    = fileNameArg;
4536         description = descriptionArg;
4537         }
4539     virtual ~FileSendThread() {}
4541     void run()
4542       {
4543       client.fileSend(destJid, offeredName,
4544                       fileName, description);
4545       }
4547 private:
4549     XmppClient &client;
4550     DOMString destJid;
4551     DOMString offeredName;
4552     DOMString fileName;
4553     DOMString description;
4554 };
4556 /**
4557  *
4558  */
4559 bool XmppClient::fileSendBackground(const DOMString &destJid,
4560                                     const DOMString &offeredName,
4561                                     const DOMString &fileName,
4562                                     const DOMString &description)
4564     FileSendThread thread(*this, destJid, offeredName,
4565                            fileName, description);
4566     thread.start();
4567     return true;
4571 /**
4572  *
4573  */
4574 bool XmppClient::fileReceive(const DOMString &fromJid,
4575                              const DOMString &iqId,
4576                              const DOMString &streamId,
4577                              const DOMString &fileName,
4578                              long  fileSize,
4579                              const DOMString &fileHash)
4581     char *fmt =
4582     "<iq type='result' to='%s' id='%s'>"
4583     "<si xmlns='http://jabber.org/protocol/si'>"
4584     "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'/>"
4585     "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
4586     "<x xmlns='jabber:x:data' type='submit'>"
4587     "<field var='stream-method'>"
4588     "<value>http://jabber.org/protocol/ibb</value>"
4589     "</field></x></feature></si></iq>\n";
4590     if (!write(fmt, fromJid.c_str(), iqId.c_str()))
4591         {
4592         return false;
4593         }
4595     int streamNr = inputStreamOpen(fromJid, streamId, iqId);
4596     if (streamNr < 0)
4597         {
4598         return false;
4599         }
4602     Md5 md5;
4603     FILE *f = fopen(fileName.c_str(), "wb");
4604     if (!f)
4605         {
4606         return false;
4607         }
4609     while (true)
4610         {
4611         if (inputStreamAvailable(streamNr)<1)
4612             {
4613             if (inputStreamClosing(streamNr))
4614                 break;
4615             pause(100);
4616             continue;
4617             }
4618         std::vector<unsigned char> ret = inputStreamRead(streamNr);
4619         std::vector<unsigned char>::iterator iter;
4620         for (iter=ret.begin() ; iter!=ret.end() ; iter++)
4621             {
4622             unsigned char ch = *iter;
4623             md5.append(&ch, 1);
4624             fwrite(&ch, 1, 1, f);
4625             }
4626         }
4628     inputStreamClose(streamNr);
4629     fclose(f);
4631     DOMString hash = md5.finishHex();
4632     printf("received file hash:%s\n", hash.c_str());
4634     return true;
4639 class FileReceiveThread : public Thread
4641 public:
4643     FileReceiveThread(XmppClient &par,
4644                       const DOMString &fromJidArg,
4645                       const DOMString &iqIdArg,
4646                       const DOMString &streamIdArg,
4647                       const DOMString &fileNameArg,
4648                       long  fileSizeArg,
4649                       const DOMString &fileHashArg) : client(par)
4650         {
4651         fromJid     = fromJidArg;
4652         iqId        = iqIdArg;
4653         streamId    = streamIdArg;
4654         fileName    = fileNameArg;
4655         fileSize    = fileSizeArg;
4656         fileHash    = fileHashArg;
4657         }
4659     virtual ~FileReceiveThread() {}
4661     void run()
4662       {
4663       client.fileReceive(fromJid, iqId, streamId,
4664                         fileName, fileSize, fileHash);
4665       }
4667 private:
4669     XmppClient &client;
4670     DOMString fromJid;
4671     DOMString iqId;
4672     DOMString streamId;
4673     DOMString fileName;
4674     long      fileSize;
4675     DOMString fileHash;
4676 };
4678 /**
4679  *
4680  */
4681 bool XmppClient::fileReceiveBackground(const DOMString &fromJid,
4682                                        const DOMString &iqId,
4683                                        const DOMString &streamId,
4684                                        const DOMString &fileName,
4685                                        long  fileSize,
4686                                        const DOMString &fileHash)
4688     FileReceiveThread thread(*this, fromJid, iqId, streamId,
4689                   fileName, fileSize, fileHash);
4690     thread.start();
4691     return true;
4696 //########################################################################
4697 //# X M P P    G R O U P    C H A T
4698 //########################################################################
4700 /**
4701  *
4702  */
4703 XmppGroupChat::XmppGroupChat(const DOMString &groupJidArg)
4705     groupJid = groupJidArg;
4708 /**
4709  *
4710  */
4711 XmppGroupChat::XmppGroupChat(const XmppGroupChat &other)
4713     groupJid = other.groupJid;
4714     userList = other.userList;
4717 /**
4718  *
4719  */
4720 XmppGroupChat::~XmppGroupChat()
4725 /**
4726  *
4727  */
4728 DOMString XmppGroupChat::getGroupJid()
4730     return groupJid;
4734 void XmppGroupChat::userAdd(const DOMString &jid, const DOMString &nick)
4736     std::vector<XmppUser>::iterator iter;
4737     for (iter= userList.begin() ; iter!=userList.end() ; iter++)
4738         {
4739         if (iter->nick == nick)
4740             return;
4741         }
4742     XmppUser user(jid, nick);
4743     userList.push_back(user);
4746 void XmppGroupChat::userDelete(const DOMString &jid, const DOMString &nick)
4748     std::vector<XmppUser>::iterator iter;
4749     for (iter= userList.begin() ; iter!=userList.end() ; )
4750         {
4751         if (iter->nick == nick)
4752             iter = userList.erase(iter);
4753         else
4754             iter++;
4755         }
4758 std::vector<XmppUser> XmppGroupChat::getUserList() const
4760     return userList;
4768 } //namespace Pedro
4769 //########################################################################
4770 //# E N D    O F     F I L E
4771 //########################################################################