Code

f5ce69e0b04a7438c7db3e93790e3dce373e717c
[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 HAVE_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 Md5::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         {}
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();
1062     return 0;
1066 void Thread::start()
1068     DWORD dwThreadId;
1069     HANDLE hThread = CreateThread(NULL, 0, WinThreadFunction,
1070                (LPVOID)this,  0,  &dwThreadId);
1071     //Make sure the thread is started before 'this' is deallocated
1072     while (!started)
1073         sleep(10);
1074     CloseHandle(hThread);
1077 void Thread::sleep(unsigned long millis)
1079     Sleep(millis);
1082 #else /* UNIX */
1085 void *PthreadThreadFunction(void *context)
1087     Thread *thread = (Thread *)context;
1088     thread->execute();
1089     return NULL;
1093 void Thread::start()
1095     pthread_t thread;
1097     int ret = pthread_create(&thread, NULL,
1098             PthreadThreadFunction, (void *)this);
1099     if (ret != 0)
1100         printf("Thread::start: thread creation failed: %s\n", strerror(ret));
1102     //Make sure the thread is started before 'this' is deallocated
1103     while (!started)
1104         sleep(10);
1108 void Thread::sleep(unsigned long millis)
1110     timespec requested;
1111     requested.tv_sec = millis / 1000;
1112     requested.tv_nsec = (millis % 1000 ) * 1000000L;
1113     nanosleep(&requested, NULL);
1116 #endif
1119 //########################################################################
1120 //########################################################################
1121 //### S O C K E T
1122 //########################################################################
1123 //########################################################################
1129 class TcpSocket
1131 public:
1133     TcpSocket();
1135     TcpSocket(const std::string &hostname, int port);
1137     TcpSocket(const char *hostname, int port);
1139     TcpSocket(const TcpSocket &other);
1141     virtual ~TcpSocket();
1143     bool isConnected();
1145     void enableSSL(bool val);
1147     bool connect(const std::string &hostname, int portno);
1149     bool connect(const char *hostname, int portno);
1151     bool startTls();
1153     bool connect();
1155     bool disconnect();
1157     bool setReceiveTimeout(unsigned long millis);
1159     long available();
1161     bool write(int ch);
1163     bool write(char *str);
1165     bool write(const std::string &str);
1167     int read();
1169     std::string readLine();
1171 private:
1172     void init();
1174     std::string hostname;
1175     int  portno;
1176     int  sock;
1177     bool connected;
1179     bool sslEnabled;
1181     unsigned long receiveTimeout;
1183 #ifdef HAVE_SSL
1184     SSL_CTX *sslContext;
1185     SSL *sslStream;
1186 #endif
1188 };
1192 //#########################################################################
1193 //# U T I L I T Y
1194 //#########################################################################
1196 static void mybzero(void *s, size_t n)
1198     unsigned char *p = (unsigned char *)s;
1199     while (n > 0)
1200         {
1201         *p++ = (unsigned char)0;
1202         n--;
1203         }
1206 static void mybcopy(void *src, void *dest, size_t n)
1208     unsigned char *p = (unsigned char *)dest;
1209     unsigned char *q = (unsigned char *)src;
1210     while (n > 0)
1211         {
1212         *p++ = *q++;
1213         n--;
1214         }
1219 //#########################################################################
1220 //# T C P    C O N N E C T I O N
1221 //#########################################################################
1223 TcpSocket::TcpSocket()
1225     init();
1229 TcpSocket::TcpSocket(const char *hostnameArg, int port)
1231     init();
1232     hostname  = hostnameArg;
1233     portno    = port;
1236 TcpSocket::TcpSocket(const std::string &hostnameArg, int port)
1238     init();
1239     hostname  = hostnameArg;
1240     portno    = port;
1244 #ifdef HAVE_SSL
1246 static void cryptoLockCallback(int mode, int type, const char *file, int line)
1248     //printf("########### LOCK\n");
1249     static int modes[CRYPTO_NUM_LOCKS]; /* = {0, 0, ... } */
1250     const char *errstr = NULL;
1252     int rw = mode & (CRYPTO_READ|CRYPTO_WRITE);
1253     if (!((rw == CRYPTO_READ) || (rw == CRYPTO_WRITE)))
1254         {
1255         errstr = "invalid mode";
1256         goto err;
1257         }
1259     if (type < 0 || type >= CRYPTO_NUM_LOCKS)
1260         {
1261         errstr = "type out of bounds";
1262         goto err;
1263         }
1265     if (mode & CRYPTO_LOCK)
1266         {
1267         if (modes[type])
1268             {
1269             errstr = "already locked";
1270             /* must not happen in a single-threaded program
1271              * (would deadlock)
1272              */
1273             goto err;
1274             }
1276         modes[type] = rw;
1277         }
1278     else if (mode & CRYPTO_UNLOCK)
1279         {
1280         if (!modes[type])
1281             {
1282              errstr = "not locked";
1283              goto err;
1284              }
1286         if (modes[type] != rw)
1287             {
1288             errstr = (rw == CRYPTO_READ) ?
1289                   "CRYPTO_r_unlock on write lock" :
1290                   "CRYPTO_w_unlock on read lock";
1291             }
1293         modes[type] = 0;
1294         }
1295     else
1296         {
1297         errstr = "invalid mode";
1298         goto err;
1299         }
1301     err:
1302     if (errstr)
1303         {
1304         /* we cannot use bio_err here */
1305         fprintf(stderr, "openssl (lock_dbg_cb): %s (mode=%d, type=%d) at %s:%d\n",
1306                 errstr, mode, type, file, line);
1307         }
1310 static unsigned long cryptoIdCallback()
1312 #ifdef __WIN32__
1313     unsigned long ret = (unsigned long) GetCurrentThreadId();
1314 #else
1315     unsigned long ret = (unsigned long) pthread_self();
1316 #endif
1317     return ret;
1320 #endif
1323 TcpSocket::TcpSocket(const TcpSocket &other)
1325     init();
1326     sock      = other.sock;
1327     hostname  = other.hostname;
1328     portno    = other.portno;
1331 static bool tcp_socket_inited = false;
1333 void TcpSocket::init()
1335     if (!tcp_socket_inited)
1336         {
1337 #ifdef __WIN32__
1338         WORD wVersionRequested = MAKEWORD( 2, 2 );
1339         WSADATA wsaData;
1340         WSAStartup( wVersionRequested, &wsaData );
1341 #endif
1342 #ifdef HAVE_SSL
1343         sslStream  = NULL;
1344         sslContext = NULL;
1345             CRYPTO_set_locking_callback(cryptoLockCallback);
1346         CRYPTO_set_id_callback(cryptoIdCallback);
1347         SSL_library_init();
1348         SSL_load_error_strings();
1349 #endif
1350         tcp_socket_inited = true;
1351         }
1352     sock           = -1;
1353     connected      = false;
1354     hostname       = "";
1355     portno         = -1;
1356     sslEnabled     = false;
1357     receiveTimeout = 0;
1360 TcpSocket::~TcpSocket()
1362     disconnect();
1365 bool TcpSocket::isConnected()
1367     if (!connected || sock < 0)
1368         return false;
1369     return true;
1372 void TcpSocket::enableSSL(bool val)
1374     sslEnabled = val;
1378 bool TcpSocket::connect(const char *hostnameArg, int portnoArg)
1380     hostname = hostnameArg;
1381     portno   = portnoArg;
1382     return connect();
1385 bool TcpSocket::connect(const std::string &hostnameArg, int portnoArg)
1387     hostname = hostnameArg;
1388     portno   = portnoArg;
1389     return connect();
1394 #ifdef HAVE_SSL
1395 /*
1396 static int password_cb(char *buf, int bufLen, int rwflag, void *userdata)
1398     char *password = "password";
1399     if (bufLen < (int)(strlen(password)+1))
1400         return 0;
1402     strcpy(buf,password);
1403     int ret = strlen(password);
1404     return ret;
1407 static void infoCallback(const SSL *ssl, int where, int ret)
1409     switch (where)
1410         {
1411         case SSL_CB_ALERT:
1412             {
1413             printf("## %d SSL ALERT: %s\n",  where, SSL_alert_desc_string_long(ret));
1414             break;
1415             }
1416         default:
1417             {
1418             printf("## %d SSL: %s\n",  where, SSL_state_string_long(ssl));
1419             break;
1420             }
1421         }
1423 */
1424 #endif
1427 bool TcpSocket::startTls()
1429 #ifdef HAVE_SSL
1430     sslStream  = NULL;
1431     sslContext = NULL;
1433     //SSL_METHOD *meth = SSLv23_method();
1434     //SSL_METHOD *meth = SSLv3_client_method();
1435     SSL_METHOD *meth = TLSv1_client_method();
1436     sslContext = SSL_CTX_new(meth);
1437     //SSL_CTX_set_info_callback(sslContext, infoCallback);
1439 #if 0
1440     char *keyFile  = "client.pem";
1441     char *caList   = "root.pem";
1442     /* Load our keys and certificates*/
1443     if (!(SSL_CTX_use_certificate_chain_file(sslContext, keyFile)))
1444         {
1445         fprintf(stderr, "Can't read certificate file\n");
1446         disconnect();
1447         return false;
1448         }
1450     SSL_CTX_set_default_passwd_cb(sslContext, password_cb);
1452     if (!(SSL_CTX_use_PrivateKey_file(sslContext, keyFile, SSL_FILETYPE_PEM)))
1453         {
1454         fprintf(stderr, "Can't read key file\n");
1455         disconnect();
1456         return false;
1457         }
1459     /* Load the CAs we trust*/
1460     if (!(SSL_CTX_load_verify_locations(sslContext, caList, 0)))
1461         {
1462         fprintf(stderr, "Can't read CA list\n");
1463         disconnect();
1464         return false;
1465         }
1466 #endif
1468     /* Connect the SSL socket */
1469     sslStream  = SSL_new(sslContext);
1470     SSL_set_fd(sslStream, sock);
1472     if (SSL_connect(sslStream)<=0)
1473         {
1474         fprintf(stderr, "SSL connect error\n");
1475         disconnect();
1476         return false;
1477         }
1479     sslEnabled = true;
1480 #endif /*HAVE_SSL*/
1481     return true;
1485 bool TcpSocket::connect()
1487     if (hostname.size()<1)
1488         {
1489         printf("open: null hostname\n");
1490         return false;
1491         }
1493     if (portno<1)
1494         {
1495         printf("open: bad port number\n");
1496         return false;
1497         }
1499     sock = socket(PF_INET, SOCK_STREAM, 0);
1500     if (sock < 0)
1501         {
1502         printf("open: error creating socket\n");
1503         return false;
1504         }
1506     char *c_hostname = (char *)hostname.c_str();
1507     struct hostent *server = gethostbyname(c_hostname);
1508     if (!server)
1509         {
1510         printf("open: could not locate host '%s'\n", c_hostname);
1511         return false;
1512         }
1514     struct sockaddr_in serv_addr;
1515     mybzero((char *) &serv_addr, sizeof(serv_addr));
1516     serv_addr.sin_family = AF_INET;
1517     mybcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr,
1518          server->h_length);
1519     serv_addr.sin_port = htons(portno);
1521     int ret = ::connect(sock, (const sockaddr *)&serv_addr, sizeof(serv_addr));
1522     if (ret < 0)
1523         {
1524         printf("open: could not connect to host '%s'\n", c_hostname);
1525         return false;
1526         }
1528      if (sslEnabled)
1529         {
1530         if (!startTls())
1531             return false;
1532         }
1533     connected = true;
1534     return true;
1537 bool TcpSocket::disconnect()
1539     bool ret  = true;
1540     connected = false;
1541 #ifdef HAVE_SSL
1542     if (sslEnabled)
1543         {
1544         if (sslStream)
1545             {
1546             int r = SSL_shutdown(sslStream);
1547             switch(r)
1548                 {
1549                 case 1:
1550                     break; /* Success */
1551                 case 0:
1552                 case -1:
1553                 default:
1554                     //printf("Shutdown failed");
1555                     ret = false;
1556                 }
1557             SSL_free(sslStream);
1558             }
1559         if (sslContext)
1560             SSL_CTX_free(sslContext);
1561         }
1562     sslStream  = NULL;
1563     sslContext = NULL;
1564 #endif /*HAVE_SSL*/
1566 #ifdef __WIN32__
1567     closesocket(sock);
1568 #else
1569     ::close(sock);
1570 #endif
1571     sock = -1;
1572     sslEnabled = false;
1574     return ret;
1579 bool TcpSocket::setReceiveTimeout(unsigned long millis)
1581     receiveTimeout = millis;
1582     return true;
1585 /**
1586  * For normal sockets, return the number of bytes waiting to be received.
1587  * For SSL, just return >0 when something is ready to be read.
1588  */
1589 long TcpSocket::available()
1591     if (!isConnected())
1592         return -1;
1594     long count = 0;
1595 #ifdef __WIN32__
1596     if (ioctlsocket(sock, FIONREAD, (unsigned long *)&count) != 0)
1597         return -1;
1598 #else
1599     if (ioctl(sock, FIONREAD, &count) != 0)
1600         return -1;
1601 #endif
1602     if (count<=0 && sslEnabled)
1603         {
1604 #ifdef HAVE_SSL
1605         return SSL_pending(sslStream);
1606 #endif
1607         }
1608     return count;
1613 bool TcpSocket::write(int ch)
1615     if (!isConnected())
1616         {
1617         printf("write: socket closed\n");
1618         return false;
1619         }
1620     unsigned char c = (unsigned char)ch;
1622     if (sslEnabled)
1623         {
1624 #ifdef HAVE_SSL
1625         int r = SSL_write(sslStream, &c, 1);
1626         if (r<=0)
1627             {
1628             switch(SSL_get_error(sslStream, r))
1629                 {
1630                 default:
1631                     printf("SSL write problem");
1632                     return -1;
1633                 }
1634             }
1635 #endif
1636         }
1637     else
1638         {
1639         if (send(sock, (const char *)&c, 1, 0) < 0)
1640         //if (send(sock, &c, 1, 0) < 0)
1641             {
1642             printf("write: could not send data\n");
1643             return false;
1644             }
1645         }
1646     return true;
1649 bool TcpSocket::write(char *str)
1651    if (!isConnected())
1652         {
1653         printf("write(str): socket closed\n");
1654         return false;
1655         }
1656     int len = strlen(str);
1658     if (sslEnabled)
1659         {
1660 #ifdef HAVE_SSL
1661         int r = SSL_write(sslStream, (unsigned char *)str, 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, str, 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 HAVE_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 DOMString XmppClient::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 parseJid(const DOMString &fullJid,
2644              DOMString &jid, DOMString &resource)
2646     int p = strIndex(fullJid, "/");
2647     if (p < 0)
2648         {
2649         jid = fullJid;
2650         resource = "";
2651         return true;
2652         }
2653     jid = fullJid.substr(0, p);
2654     resource = fullJid.substr(p+1, fullJid.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         parseJid(from, fromGid, fromNick);
2714         //printf("fromGid:%s  fromNick:%s\n",
2715         //        fromGid.c_str(), fromNick.c_str());
2716         DOMString toGid;
2717         DOMString toNick;
2718         parseJid(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 fullJid     = 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         parseJid(fullJid, 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, "");
2788                 groupChatUserShow(fromGid, fromNick, "available");
2790                 XmppEvent event(XmppEvent::EVENT_MUC_JOIN);
2791                 event.setGroup(fromGid);
2792                 event.setFrom(fromNick);
2793                 event.setPresence(presence);
2794                 event.setShow(show);
2795                 event.setStatus(status);
2796                 dispatchXmppEvent(event);
2797                 }
2798             else
2799                 {
2800                 groupChatDelete(fromGid);
2801                 groupChatUserDelete(fromGid, fromNick);
2803                 XmppEvent event(XmppEvent::EVENT_MUC_LEAVE);
2804                 event.setGroup(fromGid);
2805                 event.setFrom(fromNick);
2806                 event.setPresence(presence);
2807                 event.setShow(show);
2808                 event.setStatus(status);
2809                 dispatchXmppEvent(event);
2810                 }
2811             }
2812         else // someone else
2813             {
2814             if (presence)
2815                 {
2816                 groupChatUserAdd(fromGid, fromNick, "");
2817                 }
2818             else
2819                 groupChatUserDelete(fromGid, fromNick);               
2820             groupChatUserShow(fromGid, fromNick, show);
2821             XmppEvent event(XmppEvent::EVENT_MUC_PRESENCE);
2822             event.setGroup(fromGid);
2823             event.setFrom(fromNick);
2824             event.setPresence(presence);
2825             event.setShow(show);
2826             event.setStatus(status);
2827             dispatchXmppEvent(event);
2828             }
2829         }
2830     else
2831         {
2832         DOMString shortJid;
2833         DOMString dummy;
2834         parseJid(fullJid, shortJid, dummy);
2835         rosterShow(shortJid, show); //users in roster do not have resource
2836         
2837         XmppEvent event(XmppEvent::EVENT_PRESENCE);
2838         event.setFrom(fullJid);
2839         event.setPresence(presence);
2840         event.setShow(show);
2841         event.setStatus(status);
2842         dispatchXmppEvent(event);
2843         }
2845     return true;
2850 bool XmppClient::processIq(Element *root)
2852     DOMString from  = root->getTagAttribute("iq", "from");
2853     DOMString id    = root->getTagAttribute("iq", "id");
2854     DOMString type  = root->getTagAttribute("iq", "type");
2855     DOMString xmlns = root->getTagAttribute("query", "xmlns");
2857     if (id.size()<1)
2858         return true;
2860     //Group chat
2861     if (strIndex(xmlns, "http://jabber.org/protocol/muc") >=0 )
2862         {
2863         printf("results of MUC query\n");
2864         }
2865     //printf("###IQ xmlns:%s\n", xmlns.c_str());
2867     //### FILE TRANSFERS
2868     DOMString siNamespace = "http://jabber.org/protocol/si";
2869     if (root->getTagAttribute("si", "xmlns") == siNamespace)
2870         {
2871         if (type == "set")
2872             {
2873             DOMString streamId = root->getTagAttribute("si", "id");
2874             DOMString fname    = root->getTagAttribute("file", "name");
2875             DOMString sizeStr  = root->getTagAttribute("file", "size");
2876             DOMString hash     = root->getTagAttribute("file", "hash");
2877             XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_RECEIVE);
2878             event.setFrom(from);
2879             event.setIqId(id);
2880             event.setStreamId(streamId);
2881             event.setFileName(fname);
2882             event.setFileHash(hash);
2883             event.setFileSize(atol(sizeStr.c_str()));
2884             dispatchXmppEvent(event);
2885             }
2886         else  //result
2887             {
2888             printf("Got result\n");
2889             for (int i=0 ; i<fileSendCount ; i++)
2890                 {
2891                 XmppStream *outf = fileSends[i];
2892                 if (outf->getIqId() == id &&
2893                     from == outf->getPeerId())
2894                     {
2895                     if (type == "error")
2896                         {
2897                         outf->setState(STREAM_ERROR);
2898                         error("user '%s' rejected file", from.c_str());
2899                         return true;
2900                         }
2901                     else if (type == "result")
2902                         {
2903                         if (outf->getState() == STREAM_OPENING)
2904                             {
2905                             XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_ACCEPTED);
2906                             event.setFrom(from);
2907                             dispatchXmppEvent(event);
2908                             outf->setState(STREAM_OPEN);
2909                             }
2910                         else if (outf->getState() == STREAM_CLOSING)
2911                             {
2912                             outf->setState(STREAM_CLOSED);
2913                             }
2914                         return true;
2915                         }
2916                     }
2917                 }
2918             }
2919         return true;
2920         }
2923     //### IN-BAND BYTESTREAMS
2924     //### Incoming stream requests
2925     DOMString ibbNamespace = "http://jabber.org/protocol/ibb";
2926     if (root->getTagAttribute("open", "xmlns") == ibbNamespace)
2927         {
2928         DOMString streamId = root->getTagAttribute("open", "sid");
2929         XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_INIT);
2930         dispatchXmppEvent(event);
2931         if (streamId.size()>0)
2932             {
2933             for (int i=0 ; i<inputStreamCount ; i++)
2934                 {
2935                 XmppStream *ins = inputStreams[i];
2936                 if (ins->getStreamId() == streamId)
2937                     {
2938                     ins->setState(STREAM_OPENING);
2939                     ins->setIqId(id);
2940                     return true;
2941                     }
2942                 }
2943             }
2944         return true;
2945         }
2946     else if (root->getTagAttribute("close", "xmlns") == ibbNamespace)
2947         {
2948         XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_CLOSE);
2949         dispatchXmppEvent(event);
2950         DOMString streamId = root->getTagAttribute("close", "sid");
2951         if (streamId.size()>0)
2952             {
2953             for (int i=0 ; i<inputStreamCount ; i++)
2954                 {
2955                 XmppStream *ins = inputStreams[i];
2956                 if (ins->getStreamId() == streamId &&
2957                     from == ins->getPeerId())
2958                     {
2959                     ins->setState(STREAM_CLOSING);
2960                     ins->setIqId(id);
2961                     return true;
2962                     }
2963                 }
2964             }
2965         return true;
2966         }
2967     //### Responses to outgoing requests
2968     for (int i=0 ; i<outputStreamCount ; i++)
2969         {
2970         XmppStream *outs = outputStreams[i];
2971         if (outs->getIqId() == id)
2972             {
2973             if (type == "error")
2974                 {
2975                 outs->setState(STREAM_ERROR);
2976                 return true;
2977                 }
2978             else if (type == "result")
2979                 {
2980                 if (outs->getState() == STREAM_OPENING)
2981                     {
2982                     outs->setState(STREAM_OPEN);
2983                     }
2984                 else if (outs->getState() == STREAM_CLOSING)
2985                     {
2986                     outs->setState(STREAM_CLOSED);
2987                     }
2988                 return true;
2989                 }
2990             }
2991         }
2993     //###Normal Roster stuff
2994     if (root->getTagAttribute("query", "xmlns") == "jabber:iq:roster")
2995         {
2996         roster.clear();
2997         std::vector<Element *>elems = root->findElements("item");
2998         for (unsigned int i=0 ; i<elems.size() ; i++)
2999             {
3000             Element *item = elems[i];
3001             DOMString userJid      = item->getAttribute("jid");
3002             DOMString name         = item->getAttribute("name");
3003             DOMString subscription = item->getAttribute("subscription");
3004             DOMString group        = item->getTagValue("group");
3005             //printf("jid:%s name:%s sub:%s group:%s\n", userJid.c_str(), name.c_str(),
3006             //        subscription.c_str(), group.c_str());
3007             XmppUser user(userJid, name, subscription, group);
3008             roster.push_back(user);
3009             }
3010         XmppEvent event(XmppEvent::XmppEvent::EVENT_ROSTER);
3011         dispatchXmppEvent(event);
3012         }
3014     return true;
3019 bool XmppClient::receiveAndProcess()
3021     if (!keepGoing)
3022         return false;
3024     Parser parser;
3026     DOMString recvBuf = readStanza();
3027     recvBuf = trim(recvBuf);
3028     if (recvBuf.size() < 1)
3029         return true;
3031     //Ugly hack.  Apparently the first char can be dropped on timeouts
3032     //if (recvBuf[0] != '<')
3033     //    recvBuf.insert(0, "<");
3035     status("RECV: %s", recvBuf.c_str());
3036     Element *root = parser.parse(recvBuf);
3037     if (!root)
3038         {
3039         printf("Bad elem\n");
3040         return true;
3041         }
3043     //#### MESSAGE
3044     std::vector<Element *>elems = root->findElements("message");
3045     if (elems.size()>0)
3046         {
3047         if (!processMessage(root))
3048             return false;
3049         }
3051     //#### PRESENCE
3052     elems = root->findElements("presence");
3053     if (elems.size()>0)
3054         {
3055         if (!processPresence(root))
3056             return false;
3057         }
3059     //#### INFO
3060     elems = root->findElements("iq");
3061     if (elems.size()>0)
3062         {
3063         if (!processIq(root))
3064             return false;
3065         }
3067     delete root;
3069     return true;
3073 bool XmppClient::receiveAndProcessLoop()
3075     keepGoing = true;
3076     while (true)
3077         {
3078         if (!keepGoing)
3079             {
3080             printf("Abort requested\n");
3081             break;
3082             }
3083         if (!receiveAndProcess())
3084             return false;
3085         }
3086     return true;
3089 //#######################
3090 //# SENDING
3091 //#######################
3093 bool XmppClient::write(char *fmt, ...)
3095     va_list args;
3096     va_start(args,fmt);
3097     vsnprintf((char *)writeBuf, writeBufLen, fmt,args);
3098     va_end(args) ;
3099     status("SEND: %s", writeBuf);
3100     if (!sock->write((char *)writeBuf))
3101         {
3102         error("Cannot write to socket");
3103         return false;
3104         }
3105     return true;
3108 //#######################
3109 //# CONNECT
3110 //#######################
3112 bool XmppClient::checkConnect()
3114     if (!connected)
3115         {
3116         XmppEvent evt(XmppEvent::EVENT_ERROR);
3117         evt.setData("Attempted operation while disconnected");
3118         dispatchXmppEvent(evt);
3119         return false;
3120         }
3121     return true;
3124 bool XmppClient::iqAuthenticate(const DOMString &streamId)
3126     Parser parser;
3128     char *fmt =
3129     "<iq type='get' to='%s' id='auth%d'>"
3130     "<query xmlns='jabber:iq:auth'><username>%s</username></query>"
3131     "</iq>\n";
3132     if (!write(fmt, realm.c_str(), msgId++, username.c_str()))
3133         return false;
3135     DOMString recbuf = readStanza();
3136     //printf("iq received: '%s'\n", recbuf.c_str());
3137     Element *elem = parser.parse(recbuf);
3138     //elem->print();
3139     DOMString iqType = elem->getTagAttribute("iq", "type");
3140     //printf("##iqType:%s\n", iqType.c_str());
3141     delete elem;
3143     if (iqType != "result")
3144         {
3145         error("error:server does not allow login");
3146         return false;
3147         }
3149     bool digest = true;
3150     if (digest)
3151         {
3152         //## Digest authentication
3153         DOMString digest = streamId;
3154         digest.append(password);
3155         digest = Sha1::hashHex((unsigned char *)digest.c_str(), digest.size());
3156         //printf("digest:%s\n", digest.c_str());
3157         fmt =
3158         "<iq type='set' id='auth%d'>"
3159         "<query xmlns='jabber:iq:auth'>"
3160         "<username>%s</username>"
3161         "<digest>%s</digest>"
3162         "<resource>%s</resource>"
3163         "</query>"
3164         "</iq>\n";
3165         if (!write(fmt, msgId++, username.c_str(),
3166                     digest.c_str(), resource.c_str()))
3167             return false;
3168         }
3169     else
3170         {
3172         //## Plaintext authentication
3173         fmt =
3174         "<iq type='set' id='auth%d'>"
3175         "<query xmlns='jabber:iq:auth'>"
3176         "<username>%s</username>"
3177         "<password>%s</password>"
3178         "<resource>%s</resource>"
3179         "</query>"
3180         "</iq>\n";
3181         if (!write(fmt, msgId++, username.c_str(),
3182                    password.c_str(), resource.c_str()))
3183             return false;
3184         }
3186     recbuf = readStanza();
3187     //printf("iq received: '%s'\n", recbuf.c_str());
3188     elem = parser.parse(recbuf);
3189     //elem->print();
3190     iqType = elem->getTagAttribute("iq", "type");
3191     //printf("##iqType:%s\n", iqType.c_str());
3192     delete elem;
3194     if (iqType != "result")
3195         {
3196         error("server does not allow login");
3197         return false;
3198         }
3200     return true;
3205 bool XmppClient::saslMd5Authenticate()
3207     Parser parser;
3208     char *fmt =
3209     "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
3210     "mechanism='DIGEST-MD5'/>\n";
3211     if (!write(fmt))
3212         return false;
3214     DOMString recbuf = readStanza();
3215     status("challenge received: '%s'", recbuf.c_str());
3216     Element *elem = parser.parse(recbuf);
3217     //elem->print();
3218     DOMString b64challenge = elem->getTagValue("challenge");
3219     delete elem;
3221     if (b64challenge.size() < 1)
3222         {
3223         error("login: no SASL challenge offered by server");
3224         return false;
3225         }
3226     DOMString challenge = Base64Decoder::decodeToString(b64challenge);
3227     status("challenge:'%s'", challenge.c_str());
3229     unsigned int p1 = challenge.find("nonce=\"");
3230     if (p1 == DOMString::npos)
3231         {
3232         error("login: no SASL nonce sent by server");
3233         return false;
3234         }
3235     p1 += 7;
3236     unsigned int p2 = challenge.find("\"", p1);
3237     if (p2 == DOMString::npos)
3238         {
3239         error("login: unterminated SASL nonce sent by server");
3240         return false;
3241         }
3242     DOMString nonce = challenge.substr(p1, p2-p1);
3243     //printf("nonce: '%s'\n", nonce.c_str());
3244     char idBuf[7];
3245     snprintf(idBuf, 6, "%dsasl", msgId++);
3246     DOMString cnonce = Sha1::hashHex((unsigned char *)idBuf, 7);
3247     DOMString authzid = username; authzid.append("@"); authzid.append(host);
3248     DOMString digest_uri = "xmpp/"; digest_uri.append(host);
3250     //## Make A1
3251     Md5 md5;
3252     md5.append(username);
3253     md5.append(":");
3254     md5.append(realm);
3255     md5.append(":");
3256     md5.append(password);
3257     unsigned char a1tmp[16];
3258     md5.finish(a1tmp);
3259     md5.init();
3260     md5.append(a1tmp, 16);
3261     md5.append(":");
3262     md5.append(nonce);
3263     md5.append(":");
3264     md5.append(cnonce);
3265     md5.append(":");
3266     md5.append(authzid);
3267     DOMString a1 = md5.finishHex();
3268     status("##a1:'%s'", a1.c_str());
3270     //# Make A2
3271     md5.init();
3272     md5.append("AUTHENTICATE:");
3273     md5.append(digest_uri);
3274     DOMString a2 = md5.finishHex();
3275     status("##a2:'%s'", a2.c_str());
3277     //# Now make the response
3278     md5.init();
3279     md5.append(a1);
3280     md5.append(":");
3281     md5.append(nonce);
3282     md5.append(":");
3283     md5.append("00000001");//nc
3284     md5.append(":");
3285     md5.append(cnonce);
3286     md5.append(":");
3287     md5.append("auth");//qop
3288     md5.append(":");
3289     md5.append(a2);
3290     DOMString response = md5.finishHex();
3292     DOMString resp;
3293     resp.append("username=\""); resp.append(username); resp.append("\",");
3294     resp.append("realm=\"");    resp.append(realm);    resp.append("\",");
3295     resp.append("nonce=\"");    resp.append(nonce);    resp.append("\",");
3296     resp.append("cnonce=\"");   resp.append(cnonce);   resp.append("\",");
3297     resp.append("nc=00000001,qop=auth,");
3298     resp.append("digest-uri=\""); resp.append(digest_uri); resp.append("\"," );
3299     resp.append("authzid=\"");  resp.append(authzid);  resp.append("\",");
3300     resp.append("response=\""); resp.append(response); resp.append("\",");
3301     resp.append("charset=utf-8");
3302     status("sending response:'%s'", resp.c_str());
3303     resp = Base64Encoder::encode(resp);
3304     status("base64 response:'%s'", resp.c_str());
3305     fmt =
3306     "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>%s</response>\n";
3307     if (!write(fmt, resp.c_str()))
3308         return false;
3310     recbuf = readStanza();
3311     status("server says:: '%s'", recbuf.c_str());
3312     elem = parser.parse(recbuf);
3313     //elem->print();
3314     b64challenge = elem->getTagValue("challenge");
3315     delete elem;
3317     if (b64challenge.size() < 1)
3318         {
3319         error("login: no second SASL challenge offered by server");
3320         return false;
3321         }
3323     challenge = Base64Decoder::decodeToString(b64challenge);
3324     status("challenge: '%s'", challenge.c_str());
3325     p1 = challenge.find("rspauth=");
3326     if (p1 == DOMString::npos)
3327         {
3328         error("login: no SASL respauth sent by server\n");
3329         return false;
3330         }
3332     fmt =
3333     "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>\n";
3334     if (!write(fmt))
3335         return false;
3337     recbuf = readStanza();
3338     status("server says: '%s", recbuf.c_str());
3339     elem = parser.parse(recbuf);
3340     //elem->print();
3341     b64challenge = elem->getTagValue("challenge");
3342     bool success = (elem->findElements("success").size() > 0);
3343     delete elem;
3345     return success;
3348 bool XmppClient::saslPlainAuthenticate()
3350     Parser parser;
3352     DOMString id = username;
3353     //id.append("@");
3354     //id.append(host);
3355     Base64Encoder encoder;
3356     encoder.append('\0');
3357     encoder.append(id);
3358     encoder.append('\0');
3359     encoder.append(password);
3360     DOMString base64Auth = encoder.finish();
3361     //printf("authbuf:%s\n", base64Auth.c_str());
3363     char *fmt =
3364     "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
3365     "mechanism='PLAIN'>%s</auth>\n";
3366     if (!write(fmt, base64Auth.c_str()))
3367         return false;
3368     DOMString recbuf = readStanza();
3369     status("challenge received: '%s'", recbuf.c_str());
3370     Element *elem = parser.parse(recbuf);
3372     bool success = (elem->findElements("success").size() > 0);
3373     delete elem;
3375     return success;
3380 bool XmppClient::saslAuthenticate()
3382     Parser parser;
3384     DOMString recbuf = readStanza();
3385     status("RECV: '%s'\n", recbuf.c_str());
3386     Element *elem = parser.parse(recbuf);
3387     //elem->print();
3389     //Check for starttls
3390     bool wantStartTls = false;
3391     if (elem->findElements("starttls").size() > 0)
3392         {
3393         wantStartTls = true;
3394         if (elem->findElements("required").size() > 0)
3395             status("login: STARTTLS required");
3396         else
3397             status("login: STARTTLS available");
3398         }
3400     if (wantStartTls)
3401         {
3402         delete elem;
3403         char *fmt =
3404         "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n";
3405         if (!write(fmt))
3406             return false;
3407         recbuf = readStanza();
3408         status("RECV: '%s'\n", recbuf.c_str());
3409         elem = parser.parse(recbuf);
3410         if (elem->getTagAttribute("proceed", "xmlns").size()<1)
3411             {
3412             error("Server rejected TLS negotiation");
3413             disconnect();
3414             return false;
3415             }
3416         delete elem;
3417         if (!sock->startTls())
3418             {
3419             error("Could not start TLS");
3420             disconnect();
3421             return false;
3422             }
3424         fmt =
3425          "<stream:stream xmlns='jabber:client' "
3426          "xmlns:stream='http://etherx.jabber.org/streams' "
3427          "to='%s' version='1.0'>\n\n";
3428         if (!write(fmt, realm.c_str()))
3429             return false;
3431         recbuf = readStanza();
3432         status("RECVx: '%s'", recbuf.c_str());
3433         recbuf.append("</stream:stream>");
3434         elem = parser.parse(recbuf);
3435         bool success =
3436         (elem->getTagAttribute("stream:stream", "id").size()>0);
3437         if (!success)
3438             {
3439             error("STARTTLS negotiation failed");
3440             disconnect();
3441             return false;
3442             }
3443         delete elem;
3444         recbuf = readStanza();
3445         status("RECV: '%s'\n", recbuf.c_str());
3446         elem = parser.parse(recbuf);
3447         }
3449     //check for sasl authentication mechanisms
3450     std::vector<Element *> elems =
3451                elem->findElements("mechanism");
3452     if (elems.size() < 1)
3453         {
3454         error("login: no SASL mechanism offered by server");
3455         return false;
3456         }
3457     bool md5Found = false;
3458     bool plainFound = false;
3459     for (unsigned int i=0 ; i<elems.size() ; i++)
3460         {
3461         DOMString mech = elems[i]->getValue();
3462         if (mech == "DIGEST-MD5")
3463             {
3464             status("MD5 authentication offered");
3465             md5Found = true;
3466             }
3467         else if (mech == "PLAIN")
3468             {
3469             status("PLAIN authentication offered");
3470             plainFound = true;
3471             }
3472         }
3473     delete elem;
3475     bool success = false;
3476     if (md5Found)
3477         {
3478         success = saslMd5Authenticate();
3479         }
3480     else if (plainFound)
3481         {
3482         success = saslPlainAuthenticate();
3483         }
3484     else
3485         {
3486         error("not able to handle sasl authentication mechanisms");
3487         return false;
3488         }
3490     if (success)
3491         status("###### SASL authentication success\n");
3492     else
3493         error("###### SASL authentication failure\n");
3495     return success;
3502 bool XmppClient::createSession()
3505     Parser parser;
3506     if (port==443 || port==5223)
3507         sock->enableSSL(true);
3508     if (!sock->connect(host, port))
3509         {
3510         return false;
3511         }
3513     char *fmt =
3514      "<stream:stream "
3515           "to='%s' "
3516           "xmlns='jabber:client' "
3517           "xmlns:stream='http://etherx.jabber.org/streams' "
3518           "version='1.0'>\n\n";
3519     if (!write(fmt, realm.c_str()))
3520         return false;
3522     DOMString recbuf = readStanza();
3523     //printf("received: '%s'\n", recbuf.c_str());
3524     recbuf.append("</stream:stream>");
3525     Element *elem = parser.parse(recbuf);
3526     //elem->print();
3527     bool useSasl = false;
3528     DOMString streamId = elem->getTagAttribute("stream:stream", "id");
3529     //printf("### StreamID: %s\n", streamId.c_str());
3530     DOMString streamVersion = elem->getTagAttribute("stream:stream", "version");
3531     if (streamVersion == "1.0")
3532         useSasl = true;
3534     if (useSasl)
3535         {
3536         if (!saslAuthenticate())
3537             return false;
3538         fmt =
3539           "<stream:stream "
3540           "to='%s' "
3541           "xmlns='jabber:client' "
3542           "xmlns:stream='http://etherx.jabber.org/streams' "
3543           "version='1.0'>\n\n";
3545         if (!write(fmt, realm.c_str()))
3546             return false;
3547         recbuf = readStanza();
3548         recbuf.append("</stream:stream>\n");
3549         //printf("now server says:: '%s'\n", recbuf.c_str());
3550         elem = parser.parse(recbuf);
3551         //elem->print();
3552         delete elem;
3554         recbuf = readStanza();
3555         //printf("now server says:: '%s'\n", recbuf.c_str());
3556         elem = parser.parse(recbuf);
3557         bool hasBind = (elem->findElements("bind").size() > 0);
3558         //elem->print();
3559         delete elem;
3561         if (!hasBind)
3562             {
3563             error("no binding provided by server");
3564             return false;
3565             }
3568         }
3569     else // not SASL
3570         {
3571         if (!iqAuthenticate(streamId))
3572             return false;
3573         }
3576     //### Resource binding
3577     fmt =
3578     "<iq type='set' id='bind%d'>"
3579     "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
3580     "<resource>%s</resource>"
3581     "</bind></iq>\n";
3582     if (!write(fmt, msgId++, resource.c_str()))
3583         return false;
3585     recbuf = readStanza();
3586     status("bind result: '%s'", recbuf.c_str());
3587     elem = parser.parse(recbuf);
3588     //elem->print();
3589     DOMString bindType = elem->getTagAttribute("iq", "type");
3590     //printf("##bindType:%s\n", bindType.c_str());
3591     delete elem;
3593     if (bindType != "result")
3594         {
3595         error("no binding with server failed");
3596         return false;
3597         }
3599     fmt =
3600     "<iq type='set' id='sess%d'>"
3601     "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
3602     "</iq>\n";
3603     if (!write(fmt, msgId++))
3604         return false;
3606     recbuf = readStanza();
3607     status("session received: '%s'", recbuf.c_str());
3608     elem = parser.parse(recbuf);
3609     //elem->print();
3610     DOMString sessionType = elem->getTagAttribute("iq", "type");
3611     //printf("##sessionType:%s\n", sessionType.c_str());
3612     delete elem;
3614     if (sessionType != "result")
3615         {
3616         error("no session provided by server");
3617         return false;
3618         }
3620     //printf("########## COOL #########\n");
3621     //Now that we are bound, we have a valid JID
3622     jid = username;
3623     jid.append("@");
3624     jid.append(realm);
3625     jid.append("/");
3626     jid.append(resource);
3628     //We are now done with the synchronous handshaking.  Let's go into
3629     //async mode
3631     fmt =
3632      "<iq type='get' id='roster%d'><query xmlns='jabber:iq:roster'/></iq>\n";
3633     if (!write(fmt, msgId++))
3634         return false;
3636     fmt =
3637      "<iq type='get' id='discoItems%d' to='%s'>"
3638      "<query xmlns='http://jabber.org/protocol/disco#items'/></iq>\n";
3639     if (!write(fmt, msgId++, realm.c_str()))
3640         return false;
3642     fmt =
3643     "<iq type='get' id='discoInfo%d' to='conference.%s'>"
3644     "<query xmlns='http://jabber.org/protocol/disco#info'/></iq>\n";
3645     if (!write(fmt, msgId++, realm.c_str()))
3646         return false;
3648     fmt =
3649      "<presence/>\n";
3650     if (!write(fmt))
3651         return false;
3653     /*
3654     recbuf = readStanza();
3655     status("stream received: '%s'", recbuf.c_str());
3656     elem = parser.parse(recbuf);
3657     //elem->print();
3658     delete elem;
3659     */
3661     //We are now logged in
3662     status("Connected");
3663     connected = true;
3664     XmppEvent evt(XmppEvent::EVENT_CONNECTED);
3665     dispatchXmppEvent(evt);
3666     //Thread::sleep(1000000);
3668     sock->setReceiveTimeout(1000);
3669     ReceiverThread runner(*this);
3670     Thread thread(runner);
3671     thread.start();
3673     return true;
3676 bool XmppClient::connect()
3678     if (!createSession())
3679         {
3680         disconnect();
3681         return false;
3682         }
3683     return true;
3687 bool XmppClient::connect(DOMString hostArg, int portArg,
3688                          DOMString usernameArg,
3689                          DOMString passwordArg,
3690                          DOMString resourceArg)
3692     host     = hostArg;
3693     port     = portArg;
3694     password = passwordArg;
3695     resource = resourceArg;
3697     //parse this one
3698     setUsername(usernameArg);
3700     bool ret = connect();
3701     return ret;
3704 bool XmppClient::disconnect()
3706     if (connected)
3707         {
3708         char *fmt =
3709         "<presence from='%s' type='unavailable'/>\n";
3710         write(fmt, jid.c_str());
3711         }
3712     keepGoing = false;
3713     connected = false;
3714     Thread::sleep(3000); //allow receiving thread to quit
3715     sock->disconnect();
3716     roster.clear();
3717     groupChatsClear();
3718     XmppEvent event(XmppEvent::EVENT_DISCONNECTED);
3719     dispatchXmppEvent(event);
3720     return true;
3723 //#######################
3724 //# ROSTER
3725 //#######################
3727 bool XmppClient::rosterAdd(const DOMString &rosterGroup,
3728                            const DOMString &otherJid,
3729                            const DOMString &name)
3731     if (!checkConnect())
3732         return false;
3733     char *fmt =
3734     "<iq from='%s' type='set' id='roster_%d'>"
3735     "<query xmlns='jabber:iq:roster'>"
3736     "<item jid='%s' name='%s'><group>%s</group></item>"
3737     "</query></iq>\n";
3738     if (!write(fmt, jid.c_str(), msgId++, otherJid.c_str(),
3739          name.c_str(), rosterGroup.c_str()))
3740         {
3741         return false;
3742         }
3743     return true;
3746 bool XmppClient::rosterDelete(const DOMString &otherJid)
3748     if (!checkConnect())
3749         return false;
3750     char *fmt =
3751     "<iq from='%s' type='set' id='roster_%d'>"
3752     "<query xmlns='jabber:iq:roster'>"
3753     "<item jid='%s' subscription='remove'><group>%s</group></item>"
3754     "</query></iq>\n";
3755     if (!write(fmt, jid.c_str(), msgId++, otherJid.c_str()))
3756         {
3757         return false;
3758         }
3759     return true;
3763 static bool xmppRosterCompare(const XmppUser& p1, const XmppUser& p2)
3765     DOMString s1 = p1.group;
3766     DOMString s2 = p2.group;
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         }
3774     s1 = p1.jid;
3775     s2 = p2.jid;
3776     for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
3777         {
3778         int comp = tolower(s1[len]) - tolower(s2[len]);
3779         if (comp)
3780             return (comp<0);
3781         }
3782     return false;
3785 std::vector<XmppUser> XmppClient::getRoster()
3787     std::vector<XmppUser> ros = roster;
3788     std::sort(ros.begin(), ros.end(), xmppRosterCompare);
3789     return ros;
3792 void XmppClient::rosterShow(const DOMString &jid, const DOMString &show)
3794     DOMString theShow = show;
3795     if (theShow == "")
3796         theShow = "available";
3797     
3798     std::vector<XmppUser>::iterator iter;
3799     for (iter=roster.begin() ; iter != roster.end() ; iter++)
3800         {
3801         if (iter->jid == jid)
3802             iter->show = theShow;
3803         }
3806 //#######################
3807 //# CHAT (individual)
3808 //#######################
3810 bool XmppClient::message(const DOMString &user, const DOMString &subj,
3811                          const DOMString &msg)
3813     if (!checkConnect())
3814         return false;
3816     DOMString xmlSubj = toXml(subj);
3817     DOMString xmlMsg  = toXml(msg);
3819     if (xmlSubj.size() > 0)
3820         {
3821         char *fmt =
3822         "<message from='%s' to='%s' type='chat'>"
3823         "<subject>%s</subject><body>%s</body></message>\n";
3824         if (!write(fmt, jid.c_str(), user.c_str(),
3825                 xmlSubj.c_str(), xmlMsg.c_str()))
3826             return false;
3827         }
3828     else
3829         {
3830         char *fmt =
3831         "<message from='%s' to='%s'>"
3832         "<body>%s</body></message>\n";
3833         if (!write(fmt, jid.c_str(), user.c_str(), xmlMsg.c_str()))
3834             return false;
3835         }
3836     return true;
3841 bool XmppClient::message(const DOMString &user, const DOMString &msg)
3843     return message(user, "", msg);
3848 bool XmppClient::presence(const DOMString &presence)
3850     if (!checkConnect())
3851         return false;
3853     DOMString xmlPres = toXml(presence);
3855     char *fmt =
3856     "<presence from='%s'><show>%s</show></presence>\n";
3857     if (!write(fmt, jid.c_str(), xmlPres.c_str()))
3858         return false;
3859     return true;
3862 //#######################
3863 //# GROUP  CHAT
3864 //#######################
3866 bool XmppClient::groupChatCreate(const DOMString &groupJid)
3868     std::vector<XmppGroupChat *>::iterator iter;
3869     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
3870         {
3871         if ((*iter)->getGroupJid() == groupJid)
3872             {
3873             error("Group chat '%s' already exists", groupJid.c_str());
3874             return false;
3875             }
3876         }
3877     XmppGroupChat *chat = new XmppGroupChat(groupJid);
3878     groupChats.push_back(chat);
3879     return true;
3882 /**
3883  *
3884  */
3885 void XmppClient::groupChatDelete(const DOMString &groupJid)
3887     std::vector<XmppGroupChat *>::iterator iter;
3888     for (iter=groupChats.begin() ; iter!=groupChats.end() ; )
3889         {
3890         XmppGroupChat *chat = *iter;
3891         if (chat->getGroupJid() == groupJid)
3892             {
3893             iter = groupChats.erase(iter);
3894             delete chat;
3895             }
3896         else
3897             iter++;
3898         }
3901 /**
3902  *
3903  */
3904 bool XmppClient::groupChatExists(const DOMString &groupJid)
3906     std::vector<XmppGroupChat *>::iterator iter;
3907     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
3908         if ((*iter)->getGroupJid() == groupJid)
3909             return true;
3910     return false;
3913 /**
3914  *
3915  */
3916 void XmppClient::groupChatsClear()
3918     std::vector<XmppGroupChat *>::iterator iter;
3919     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
3920         delete (*iter);
3921     groupChats.clear();
3925 /**
3926  *
3927  */
3928 void XmppClient::groupChatUserAdd(const DOMString &groupJid,
3929                                   const DOMString &nick,
3930                                   const DOMString &jid)
3932     std::vector<XmppGroupChat *>::iterator iter;
3933     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
3934         {
3935         if ((*iter)->getGroupJid() == groupJid)
3936             {
3937             (*iter)->userAdd(nick, jid);
3938             }
3939         }
3942 /**
3943  *
3944  */
3945 void XmppClient::groupChatUserShow(const DOMString &groupJid,
3946                                    const DOMString &nick,
3947                                    const DOMString &show)
3949     std::vector<XmppGroupChat *>::iterator iter;
3950     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
3951         {
3952         if ((*iter)->getGroupJid() == groupJid)
3953             {
3954             (*iter)->userShow(nick, show);
3955             }
3956         }
3959 /**
3960  *
3961  */
3962 void XmppClient::groupChatUserDelete(const DOMString &groupJid,
3963                                      const DOMString &nick)
3965     std::vector<XmppGroupChat *>::iterator iter;
3966     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
3967         {
3968         if ((*iter)->getGroupJid() == groupJid)
3969             {
3970             (*iter)->userDelete(nick);
3971             }
3972         }
3975 static bool xmppUserCompare(const XmppUser& p1, const XmppUser& p2)
3977     DOMString s1 = p1.nick;
3978     DOMString s2 = p2.nick;
3979     int comp = 0;
3980     for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
3981         {
3982         comp = tolower(s1[len]) - tolower(s2[len]);
3983         if (comp)
3984             break;
3985         }
3986     return (comp<0);
3990 std::vector<XmppUser> XmppClient::groupChatGetUserList(
3991                               const DOMString &groupJid)
3993     if (!checkConnect())
3994         {
3995         std::vector<XmppUser> dummy;
3996         return dummy;
3997         }
3999     std::vector<XmppGroupChat *>::iterator iter;
4000     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
4001         {
4002         if ((*iter)->getGroupJid() == groupJid )
4003             {
4004             std::vector<XmppUser> uList = (*iter)->getUserList();
4005             std::sort(uList.begin(), uList.end(), xmppUserCompare);
4006             return uList;
4007             }
4008         }
4009     std::vector<XmppUser> dummy;
4010     return dummy;
4013 bool XmppClient::groupChatJoin(const DOMString &groupJid,
4014                                const DOMString &nick,
4015                                const DOMString &pass)
4017     if (!checkConnect())
4018         return false;
4020     DOMString user = nick;
4021     if (user.size()<1)
4022         user = username;
4024     char *fmt =
4025     "<presence to='%s/%s'>"
4026     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
4027     if (!write(fmt, groupJid.c_str(), user.c_str()))
4028         return false;
4029     return true;
4033 bool XmppClient::groupChatLeave(const DOMString &groupJid,
4034                                 const DOMString &nick)
4036     if (!checkConnect())
4037         return false;
4039     DOMString user = nick;
4040     if (user.size()<1)
4041         user = username;
4043     char *fmt =
4044     "<presence to='%s/%s' type='unavailable'>"
4045     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
4046     if (!write(fmt, groupJid.c_str(), user.c_str()))
4047         return false;
4048     return true;
4052 bool XmppClient::groupChatMessage(const DOMString &groupJid,
4053                                   const DOMString &msg)
4055     if (!checkConnect())
4056         {
4057         return false;
4058         }
4060     DOMString xmlMsg = toXml(msg);
4062     char *fmt =
4063     "<message from='%s' to='%s' type='groupchat'>"
4064     "<body>%s</body></message>\n";
4065     if (!write(fmt, jid.c_str(), groupJid.c_str(), xmlMsg.c_str()))
4066         return false;
4067     return true;
4070 bool XmppClient::groupChatPrivateMessage(const DOMString &groupJid,
4071                                          const DOMString &toNick,
4072                                          const DOMString &msg)
4074     if (!checkConnect())
4075         return false;
4077     DOMString xmlMsg = toXml(msg);
4079     char *fmt =
4080     "<message from='%s' to='%s/%s' type='chat'>"
4081     "<body>%s</body></message>\n";
4082     if (!write(fmt, jid.c_str(), groupJid.c_str(),
4083                toNick.c_str(), xmlMsg.c_str()))
4084         return false;
4085     return true;
4088 bool XmppClient::groupChatPresence(const DOMString &groupJid,
4089                                    const DOMString &myNick,
4090                                    const DOMString &presence)
4092     if (!checkConnect())
4093         return false;
4095     DOMString user = myNick;
4096     if (user.size()<1)
4097         user = username;
4099     DOMString xmlPresence = toXml(presence);
4101     char *fmt =
4102     "<presence from='%s' to='%s/%s' type='unavailable'>"
4103     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
4104     if (!write(fmt, jid.c_str(), groupJid.c_str(), user.c_str(), xmlPresence.c_str()))
4105         return true;
4106     return true;
4111 //#######################
4112 //# S T R E A M S
4113 //#######################
4116 /**
4117  *
4118  */
4119 int XmppClient::outputStreamOpen(const DOMString &destId,
4120                                  const DOMString &streamIdArg)
4122     int i;
4123     for (i=0; i<outputStreamCount ; i++)
4124         if (outputStreams[i]->getState() == STREAM_AVAILABLE)
4125             break;
4126     if (i>=outputStreamCount)
4127         {
4128         error("No available output streams");
4129         return -1;
4130         }
4131     int streamNr = i;
4132     XmppStream *outs = outputStreams[streamNr];
4134     outs->setState(STREAM_OPENING);
4136     char buf[32];
4137     snprintf(buf, 31, "inband%d", getMsgId());
4138     DOMString iqId = buf;
4140     DOMString streamId = streamIdArg;
4141     if (streamId.size()<1)
4142         {
4143         snprintf(buf, 31, "stream%d", getMsgId());
4144         DOMString streamId = buf;
4145         }
4146     outs->setIqId(iqId);
4147     outs->setStreamId(streamId);
4148     outs->setPeerId(destId);
4150     char *fmt =
4151     "<iq type='set' from='%s' to='%s' id='%s'>"
4152     "<open sid='%s' block-size='4096'"
4153     " xmlns='http://jabber.org/protocol/ibb'/></iq>\n";
4154     if (!write(fmt, jid.c_str(),
4155               destId.c_str(), iqId.c_str(),
4156               streamId.c_str()))
4157         {
4158         outs->reset();
4159         return -1;
4160         }
4162     int state = outs->getState();
4163     for (int tim=0 ; tim<20 ; tim++)
4164         {
4165         if (state == STREAM_OPEN)
4166             break;
4167         else if (state == STREAM_ERROR)
4168             {
4169             printf("ERROR\n");
4170             outs->reset();
4171             return -1;
4172             }
4173         Thread::sleep(1000);
4174         state = outs->getState();
4175         }
4176     if (state != STREAM_OPEN)
4177         {
4178         printf("TIMEOUT ERROR\n");
4179         outs->reset();
4180         return -1;
4181         }
4183     return streamNr;
4186 /**
4187  *
4188  */
4189 int XmppClient::outputStreamWrite(int streamNr,
4190                       const unsigned char *buf, unsigned long len)
4192     XmppStream *outs = outputStreams[streamNr];
4194     unsigned long outLen = 0;
4195     unsigned char *p = (unsigned char *)buf;
4197     while (outLen < len)
4198         {
4199         unsigned long chunksize = 1024;
4200         if (chunksize + outLen > len)
4201             chunksize = len - outLen;
4203         Base64Encoder encoder;
4204         encoder.append(p, chunksize);
4205         DOMString b64data = encoder.finish();
4206         p      += chunksize;
4207         outLen += chunksize;
4209         char *fmt =
4210         "<message from='%s' to='%s' id='msg%d'>"
4211         "<data xmlns='http://jabber.org/protocol/ibb' sid='%s' seq='%d'>"
4212         "%s"
4213         "</data>"
4214         "<amp xmlns='http://jabber.org/protocol/amp'>"
4215         "<rule condition='deliver-at' value='stored' action='error'/>"
4216         "<rule condition='match-resource' value='exact' action='error'/>"
4217         "</amp>"
4218         "</message>\n";
4219         if (!write(fmt, jid.c_str(),
4220               outs->getPeerId().c_str(),
4221               getMsgId(),
4222               outs->getStreamId().c_str(),
4223               outs->getSeqNr(),
4224               b64data.c_str()))
4225             {
4226             outs->reset();
4227             return -1;
4228             }
4229         pause(5000);
4230         }
4231     return outLen;
4234 /**
4235  *
4236  */
4237 int XmppClient::outputStreamClose(int streamNr)
4239     XmppStream *outs = outputStreams[streamNr];
4241     char buf[32];
4242     snprintf(buf, 31, "inband%d", getMsgId());
4243     DOMString iqId = buf;
4244     outs->setIqId(iqId);
4246     outs->setState(STREAM_CLOSING);
4247     char *fmt =
4248     "<iq type='set' from='%s' to='%s' id='%s'>"
4249     "<close sid='%s' xmlns='http://jabber.org/protocol/ibb'/></iq>\n";
4250     if (!write(fmt, jid.c_str(),
4251                     outs->getPeerId().c_str(),
4252                     iqId.c_str(),
4253                     outs->getStreamId().c_str()))
4254         return false;
4256     int state = outs->getState();
4257     for (int tim=0 ; tim<20 ; tim++)
4258         {
4259         if (state == STREAM_CLOSED)
4260             break;
4261         else if (state == STREAM_ERROR)
4262             {
4263             printf("ERROR\n");
4264             outs->reset();
4265             return -1;
4266             }
4267         Thread::sleep(1000);
4268         state = outs->getState();
4269         }
4270     if (state != STREAM_CLOSED)
4271         {
4272         printf("TIMEOUT ERROR\n");
4273         outs->reset();
4274         return -1;
4275         }
4277     outs->reset();
4278     return 1;
4282 /**
4283  *
4284  */
4285 int XmppClient::inputStreamOpen(const DOMString &fromJid, const DOMString &streamId,
4286                                 const DOMString &iqId)
4288     int i;
4289     for (i=0 ; i<inputStreamCount ; i++)
4290         {
4291         if (inputStreams[i]->getState() == STREAM_AVAILABLE)
4292             break;
4293         }
4294     if (i>=inputStreamCount)
4295         {
4296         error("No available input streams");
4297         return -1;
4298         }
4299     int streamNr = i;
4300     XmppStream *ins = inputStreams[streamNr];
4301     ins->reset();
4302     ins->setPeerId(fromJid);
4303     ins->setState(STREAM_CLOSED);
4304     ins->setStreamId(streamId);
4306     int state = ins->getState();
4307     for (int tim=0 ; tim<20 ; tim++)
4308         {
4309         if (state == STREAM_OPENING)
4310             break;
4311         else if (state == STREAM_ERROR)
4312             {
4313             printf("ERROR\n");
4314             ins->reset();
4315             return -1;
4316             }
4317         Thread::sleep(1000);
4318         state = ins->getState();
4319         }
4320     if (state != STREAM_OPENING)
4321         {
4322         printf("TIMEOUT ERROR\n");
4323         ins->reset();
4324         return -1;
4325         }
4326     char *fmt =
4327     "<iq type='result' from='%s' to='%s' id='%s'/>\n";
4328     if (!write(fmt, jid.c_str(),  fromJid.c_str(), ins->getIqId().c_str()))
4329         {
4330         return -1;
4331         }
4333     ins->setState(STREAM_OPEN);
4334     return streamNr;
4337 /**
4338  *
4339  */
4340 int XmppClient::inputStreamAvailable(int streamNr)
4342     XmppStream *ins = inputStreams[streamNr];
4343     return ins->available();
4346 /**
4347  *
4348  */
4349 std::vector<unsigned char> XmppClient::inputStreamRead(int streamNr)
4351     XmppStream *ins = inputStreams[streamNr];
4352     return ins->read();
4355 /**
4356  *
4357  */
4358 bool XmppClient::inputStreamClosing(int streamNr)
4360     XmppStream *ins = inputStreams[streamNr];
4361     if (ins->getState() == STREAM_CLOSING)
4362         return true;
4363     return false;
4367 /**
4368  *
4369  */
4370 int XmppClient::inputStreamClose(int streamNr)
4372     int ret=1;
4373     XmppStream *ins = inputStreams[streamNr];
4374     if (ins->getState() == STREAM_CLOSING)
4375         {
4376         char *fmt =
4377         "<iq type='result' from='%s' to='%s' id='%s'/>\n";
4378         if (!write(fmt, jid.c_str(),  ins->getPeerId().c_str(),
4379                     ins->getIqId().c_str()))
4380             {
4381             ret = -1;
4382             }
4383         }
4384     ins->reset();
4385     return ret;
4391 //#######################
4392 //# FILE   TRANSFERS
4393 //#######################
4396 /**
4397  *
4398  */
4399 bool XmppClient::fileSend(const DOMString &destJidArg,
4400                           const DOMString &offeredNameArg,
4401                           const DOMString &fileNameArg,
4402                           const DOMString &descriptionArg)
4404     DOMString destJid     = destJidArg;
4405     DOMString offeredName = offeredNameArg;
4406     DOMString fileName    = fileNameArg;
4407     DOMString description = descriptionArg;
4409     int i;
4410     for (i=0; i<fileSendCount ; i++)
4411         if (fileSends[i]->getState() == STREAM_AVAILABLE)
4412             break;
4413     if (i>=fileSendCount)
4414         {
4415         error("No available file send streams");
4416         return false;
4417         }
4418     int fileSendNr = i;
4419     XmppStream *outf = fileSends[fileSendNr];
4421     outf->setState(STREAM_OPENING);
4423     struct stat finfo;
4424     if (stat(fileName.c_str(), &finfo)<0)
4425         {
4426         error("Cannot stat file '%s' for sending", fileName.c_str());
4427         return false;
4428         }
4429     long fileLen = finfo.st_size;
4430     if (!fileLen > 1000000)
4431         {
4432         error("'%s' too large", fileName.c_str());
4433         return false;
4434         }
4435     if (!S_ISREG(finfo.st_mode))
4436         {
4437         error("'%s' is not a regular file", fileName.c_str());
4438         return false;
4439         }
4440     FILE *f = fopen(fileName.c_str(), "rb");
4441     if (!f)
4442         {
4443         error("cannot open '%s' for sending", fileName.c_str());
4444         return false;
4445         }
4446     unsigned char *sendBuf = (unsigned char *)malloc(fileLen+1);
4447     if (!sendBuf)
4448         {
4449         error("cannot cannot allocate send buffer for %s", fileName.c_str());
4450         return false;
4451         }
4452     for (long i=0 ; i<fileLen && !feof(f); i++)
4453         {
4454         sendBuf[i] = fgetc(f);
4455         }
4456     fclose(f);
4458     //## get the last path segment from the whole path
4459     if (offeredName.size()<1)
4460         {
4461         int slashPos = -1;
4462         for (unsigned int i=0 ; i<fileName.size() ; i++)
4463             {
4464             int ch = fileName[i];
4465             if (ch == '/' || ch == '\\')
4466                 slashPos = i;
4467             }
4468         if (slashPos>=0 && slashPos<=(int)(fileName.size()-1))
4469             {
4470             offeredName = fileName.substr(slashPos+1,
4471                                           fileName.size()-slashPos-1);
4472             printf("offeredName:%s\n", offeredName.c_str());
4473             }
4474         }
4476     char buf[32];
4477     snprintf(buf, 31, "file%d", getMsgId());
4478     DOMString iqId = buf;
4479     outf->setIqId(iqId);
4481     snprintf(buf, 31, "stream%d", getMsgId());
4482     DOMString streamId = buf;
4483     //outf->setStreamId(streamId);
4485     DOMString hash = Md5::hashHex(sendBuf, fileLen);
4486     printf("Hash:%s\n", hash.c_str());
4488     outf->setPeerId(destJid);
4490     char dtgBuf[81];
4491     struct tm *timeVal = gmtime(&(finfo.st_mtime));
4492     strftime(dtgBuf, 80, "%Y-%m-%dT%H:%M:%Sz", timeVal);
4494     char *fmt =
4495     "<iq type='set' id='%s' to='%s'>"
4496     "<si xmlns='http://jabber.org/protocol/si' id='%s'"
4497       " mime-type='text/plain'"
4498       " profile='http://jabber.org/protocol/si/profile/file-transfer'>"
4499     "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'"
4500           " name='%s' size='%d' hash='%s' date='%s'><desc>%s</desc></file>"
4501     "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
4502     "<x xmlns='jabber:x:data' type='form'>"
4503     "<field var='stream-method' type='list-single'>"
4504     //"<option><value>http://jabber.org/protocol/bytestreams</value></option>"
4505     "<option><value>http://jabber.org/protocol/ibb</value></option>"
4506     "</field></x></feature></si></iq>\n";
4507     if (!write(fmt, iqId.c_str(), destJid.c_str(),
4508          streamId.c_str(), offeredName.c_str(), fileLen,
4509          hash.c_str(), dtgBuf, description.c_str()))
4510         {
4511         free(sendBuf);
4512         return false;
4513         }
4515     int state = outf->getState();
4516     for (int tim=0 ; tim<20 ; tim++)
4517         {
4518         printf("##### waiting for open\n");
4519         if (state == STREAM_OPEN)
4520             {
4521             outf->reset();
4522             break;
4523             }
4524         else if (state == STREAM_ERROR)
4525             {
4526             printf("ERROR\n");
4527             outf->reset();
4528             return false;
4529             }
4530         Thread::sleep(1000);
4531         state = outf->getState();
4532         }
4533     if (state != STREAM_OPEN)
4534         {
4535         printf("TIMEOUT ERROR\n");
4536         outf->reset();
4537         return false;
4538         }
4540     //free up this reqource
4541     outf->reset();
4543     int  streamNr = outputStreamOpen(destJid, streamId);
4544     if (streamNr<0)
4545         {
4546         error("cannot open output stream %s", streamId.c_str());
4547         outf->reset();
4548         return false;
4549         }
4551     int ret = outputStreamWrite(streamNr, sendBuf, fileLen);
4553     if (ret<0)
4554         {
4555         }
4557     outputStreamClose(streamNr);
4559     free(sendBuf);
4560     return true;
4564 class FileSendThread : public Thread
4566 public:
4568     FileSendThread(XmppClient &par,
4569                    const DOMString &destJidArg,
4570                    const DOMString &offeredNameArg,
4571                    const DOMString &fileNameArg,
4572                    const DOMString &descriptionArg) : client(par)
4573         {
4574         destJid     = destJidArg;
4575         offeredName = offeredNameArg;
4576         fileName    = fileNameArg;
4577         description = descriptionArg;
4578         }
4580     virtual ~FileSendThread() {}
4582     void run()
4583       {
4584       client.fileSend(destJid, offeredName,
4585                       fileName, description);
4586       }
4588 private:
4590     XmppClient &client;
4591     DOMString destJid;
4592     DOMString offeredName;
4593     DOMString fileName;
4594     DOMString description;
4595 };
4597 /**
4598  *
4599  */
4600 bool XmppClient::fileSendBackground(const DOMString &destJid,
4601                                     const DOMString &offeredName,
4602                                     const DOMString &fileName,
4603                                     const DOMString &description)
4605     FileSendThread thread(*this, destJid, offeredName,
4606                            fileName, description);
4607     thread.start();
4608     return true;
4612 /**
4613  *
4614  */
4615 bool XmppClient::fileReceive(const DOMString &fromJid,
4616                              const DOMString &iqId,
4617                              const DOMString &streamId,
4618                              const DOMString &fileName,
4619                              long  fileSize,
4620                              const DOMString &fileHash)
4622     char *fmt =
4623     "<iq type='result' to='%s' id='%s'>"
4624     "<si xmlns='http://jabber.org/protocol/si'>"
4625     "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'/>"
4626     "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
4627     "<x xmlns='jabber:x:data' type='submit'>"
4628     "<field var='stream-method'>"
4629     "<value>http://jabber.org/protocol/ibb</value>"
4630     "</field></x></feature></si></iq>\n";
4631     if (!write(fmt, fromJid.c_str(), iqId.c_str()))
4632         {
4633         return false;
4634         }
4636     int streamNr = inputStreamOpen(fromJid, streamId, iqId);
4637     if (streamNr < 0)
4638         {
4639         return false;
4640         }
4643     Md5 md5;
4644     FILE *f = fopen(fileName.c_str(), "wb");
4645     if (!f)
4646         {
4647         return false;
4648         }
4650     while (true)
4651         {
4652         if (inputStreamAvailable(streamNr)<1)
4653             {
4654             if (inputStreamClosing(streamNr))
4655                 break;
4656             pause(100);
4657             continue;
4658             }
4659         std::vector<unsigned char> ret = inputStreamRead(streamNr);
4660         std::vector<unsigned char>::iterator iter;
4661         for (iter=ret.begin() ; iter!=ret.end() ; iter++)
4662             {
4663             unsigned char ch = *iter;
4664             md5.append(&ch, 1);
4665             fwrite(&ch, 1, 1, f);
4666             }
4667         }
4669     inputStreamClose(streamNr);
4670     fclose(f);
4672     DOMString hash = md5.finishHex();
4673     printf("received file hash:%s\n", hash.c_str());
4675     return true;
4680 class FileReceiveThread : public Thread
4682 public:
4684     FileReceiveThread(XmppClient &par,
4685                       const DOMString &fromJidArg,
4686                       const DOMString &iqIdArg,
4687                       const DOMString &streamIdArg,
4688                       const DOMString &fileNameArg,
4689                       long  fileSizeArg,
4690                       const DOMString &fileHashArg) : client(par)
4691         {
4692         fromJid     = fromJidArg;
4693         iqId        = iqIdArg;
4694         streamId    = streamIdArg;
4695         fileName    = fileNameArg;
4696         fileSize    = fileSizeArg;
4697         fileHash    = fileHashArg;
4698         }
4700     virtual ~FileReceiveThread() {}
4702     void run()
4703       {
4704       client.fileReceive(fromJid, iqId, streamId,
4705                         fileName, fileSize, fileHash);
4706       }
4708 private:
4710     XmppClient &client;
4711     DOMString fromJid;
4712     DOMString iqId;
4713     DOMString streamId;
4714     DOMString fileName;
4715     long      fileSize;
4716     DOMString fileHash;
4717 };
4719 /**
4720  *
4721  */
4722 bool XmppClient::fileReceiveBackground(const DOMString &fromJid,
4723                                        const DOMString &iqId,
4724                                        const DOMString &streamId,
4725                                        const DOMString &fileName,
4726                                        long  fileSize,
4727                                        const DOMString &fileHash)
4729     FileReceiveThread thread(*this, fromJid, iqId, streamId,
4730                   fileName, fileSize, fileHash);
4731     thread.start();
4732     return true;
4737 //########################################################################
4738 //# X M P P    G R O U P    C H A T
4739 //########################################################################
4741 /**
4742  *
4743  */
4744 XmppGroupChat::XmppGroupChat(const DOMString &groupJidArg)
4746     groupJid = groupJidArg;
4749 /**
4750  *
4751  */
4752 XmppGroupChat::XmppGroupChat(const XmppGroupChat &other)
4754     groupJid = other.groupJid;
4755     userList = other.userList;
4758 /**
4759  *
4760  */
4761 XmppGroupChat::~XmppGroupChat()
4766 /**
4767  *
4768  */
4769 DOMString XmppGroupChat::getGroupJid()
4771     return groupJid;
4775 void XmppGroupChat::userAdd(const DOMString &nick, 
4776                             const DOMString &jid)
4778     std::vector<XmppUser>::iterator iter;
4779     for (iter= userList.begin() ; iter!=userList.end() ; iter++)
4780         {
4781         if (iter->nick == nick)
4782             return;
4783         }
4784     XmppUser user(jid, nick);
4785     userList.push_back(user);
4788 void XmppGroupChat::userShow(const DOMString &nick, 
4789                              const DOMString &show)
4791     DOMString theShow = show;
4792     if (theShow == "")
4793         theShow = "available"; // a join message will now have a show
4794     std::vector<XmppUser>::iterator iter;
4795     for (iter= userList.begin() ; iter!=userList.end() ; iter++)
4796         {
4797         if (iter->nick == nick)
4798             iter->show = theShow;
4799         }
4802 void XmppGroupChat::userDelete(const DOMString &nick)
4804     std::vector<XmppUser>::iterator iter;
4805     for (iter= userList.begin() ; iter!=userList.end() ; )
4806         {
4807         if (iter->nick == nick)
4808             iter = userList.erase(iter);
4809         else
4810             iter++;
4811         }
4814 std::vector<XmppUser> XmppGroupChat::getUserList() const
4816     return userList;
4824 } //namespace Pedro
4825 //########################################################################
4826 //# E N D    O F     F I L E
4827 //########################################################################