1 /*
2 * Implementation the Pedro mini-XMPP client
3 *
4 * Authors:
5 * Bob Jamison
6 *
7 * Copyright (C) 2005 Bob Jamison
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
25 #include <stdio.h>
26 #include <stdarg.h>
28 #include <sys/stat.h>
30 #include "pedroxmpp.h"
31 #include "pedrodom.h"
33 #ifdef __WIN32__
35 #include <windows.h>
37 #else /* UNIX */
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <netdb.h>
43 #include <unistd.h>
44 #include <sys/ioctl.h>
46 #include <pthread.h>
48 #endif
50 #ifdef WITH_SSL
51 #include <openssl/ssl.h>
52 #include <openssl/err.h>
53 #endif
56 namespace Pedro
57 {
59 //########################################################################
60 //########################################################################
61 //### U T I L I T Y
62 //########################################################################
63 //########################################################################
66 //########################################################################
67 //# B A S E 6 4
68 //########################################################################
70 //#################
71 //# ENCODER
72 //#################
75 static char *base64encode =
76 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
78 /**
79 * This class is for Base-64 encoding
80 */
81 class Base64Encoder
82 {
84 public:
86 Base64Encoder()
87 {
88 reset();
89 }
91 virtual ~Base64Encoder()
92 {}
94 virtual void reset()
95 {
96 outBuf = 0L;
97 bitCount = 0;
98 buf = "";
99 }
101 virtual void append(int ch);
103 virtual void append(char *str);
105 virtual void append(unsigned char *str, int len);
107 virtual void append(const DOMString &str);
109 virtual DOMString finish();
111 static DOMString encode(const DOMString &str);
114 private:
117 unsigned long outBuf;
119 int bitCount;
121 DOMString buf;
123 };
127 /**
128 * Writes the specified byte to the output buffer
129 */
130 void Base64Encoder::append(int ch)
131 {
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 }
156 }
158 /**
159 * Writes the specified string to the output buffer
160 */
161 void Base64Encoder::append(char *str)
162 {
163 while (*str)
164 append((int)*str++);
165 }
167 /**
168 * Writes the specified string to the output buffer
169 */
170 void Base64Encoder::append(unsigned char *str, int len)
171 {
172 while (len>0)
173 {
174 append((int)*str++);
175 len--;
176 }
177 }
179 /**
180 * Writes the specified string to the output buffer
181 */
182 void Base64Encoder::append(const DOMString &str)
183 {
184 append((char *)str.c_str());
185 }
187 /**
188 * Closes this output stream and releases any system resources
189 * associated with this stream.
190 */
191 DOMString Base64Encoder::finish()
192 {
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;
231 }
234 DOMString Base64Encoder::encode(const DOMString &str)
235 {
236 Base64Encoder encoder;
237 encoder.append(str);
238 DOMString ret = encoder.finish();
239 return ret;
240 }
244 //#################
245 //# DECODER
246 //#################
248 static int base64decode[] =
249 {
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
269 {
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)
309 {
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 }
338 }
340 void Base64Decoder::append(char *str)
341 {
342 while (*str)
343 append((int)*str++);
344 }
346 void Base64Decoder::append(const DOMString &str)
347 {
348 append((char *)str.c_str());
349 }
351 std::vector<unsigned char> Base64Decoder::finish()
352 {
353 std::vector<unsigned char> ret = buf;
354 reset();
355 return ret;
356 }
358 std::vector<unsigned char> Base64Decoder::decode(const DOMString &str)
359 {
360 Base64Decoder decoder;
361 decoder.append(str);
362 std::vector<unsigned char> ret = decoder.finish();
363 return ret;
364 }
366 DOMString Base64Decoder::decodeToString(const DOMString &str)
367 {
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;
375 }
379 //########################################################################
380 //# S H A 1
381 //########################################################################
383 class Sha1
384 {
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)
444 {
445 Sha1 sha1;
446 sha1.append(dataIn, len);
447 sha1.finish(digest);
448 }
450 static char *sha1hex = "0123456789abcdef";
452 DOMString Sha1::hashHex(unsigned char *dataIn, int len)
453 {
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;
464 }
467 void Sha1::init()
468 {
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;
483 }
486 void Sha1::append(unsigned char *dataIn, int len)
487 {
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 }
501 }
504 void Sha1::finish(unsigned char hashout[20])
505 {
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();
535 }
538 #define SHA_ROTL(X,n) ((((X) << (n)) | ((X) >> (32-(n)))) & 0xffffffffL)
540 void Sha1::hashblock()
541 {
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;
584 }
590 //########################################################################
591 //# M D 5
592 //########################################################################
594 class Md5
595 {
596 public:
598 /**
599 *
600 */
601 Md5()
602 { init(); }
604 /**
605 *
606 */
607 virtual ~Md5()
608 {}
610 /**
611 * Static convenience method.
612 * @parm digest points to an buffer of 16 unsigned chars
613 */
614 static void hash(unsigned char *dataIn,
615 unsigned long len, unsigned char *digest);
617 static DOMString hashHex(unsigned char *dataIn, unsigned long len);
619 /**
620 * Initialize the context (also zeroizes contents)
621 */
622 virtual void init();
624 /**
625 *
626 */
627 virtual void append(unsigned char *dataIn, unsigned long len);
629 /**
630 *
631 */
632 virtual void append(const DOMString &str);
634 /**
635 * Finalize and output the hash.
636 * @parm digest points to an buffer of 16 unsigned chars
637 */
638 virtual void finish(unsigned char *digest);
641 /**
642 * Same as above , but hex to an output String
643 */
644 virtual DOMString finishHex();
646 private:
648 void transform(unsigned long *buf, unsigned long *in);
650 unsigned long buf[4];
651 unsigned long bits[2];
652 unsigned char in[64];
654 };
659 void Md5::hash(unsigned char *dataIn, unsigned long len, unsigned char *digest)
660 {
661 Md5 md5;
662 md5.append(dataIn, len);
663 md5.finish(digest);
664 }
666 DOMString Md5::hashHex(unsigned char *dataIn, unsigned long len)
667 {
668 Md5 md5;
669 md5.append(dataIn, len);
670 DOMString ret = md5.finishHex();
671 return ret;
672 }
676 /*
677 * Note: this code is harmless on little-endian machines.
678 */
679 /*
680 static void byteReverse(unsigned char *buf, unsigned long longs)
681 {
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);
690 }
691 */
693 static void md5_memcpy(void *dest, void *src, int n)
694 {
695 unsigned char *s1 = (unsigned char *)dest;
696 unsigned char *s2 = (unsigned char *)src;
697 while (n--)
698 *s1++ = *s2++;
699 }
701 static void md5_memset(void *dest, char v, int n)
702 {
703 unsigned char *s = (unsigned char *)dest;
704 while (n--)
705 *s++ = v;
706 }
708 /**
709 * Initialize MD5 polynomials and storage
710 */
711 void Md5::init()
712 {
713 buf[0] = 0x67452301;
714 buf[1] = 0xefcdab89;
715 buf[2] = 0x98badcfe;
716 buf[3] = 0x10325476;
718 bits[0] = 0;
719 bits[1] = 0;
720 }
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)
727 {
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);
768 }
770 /*
771 * Update context to reflect the concatenation of another string
772 */
773 void Md5::append(const DOMString &str)
774 {
775 append((unsigned char *)str.c_str(), str.size());
776 }
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)
783 {
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! ;-)
821 }
823 static char *md5hex = "0123456789abcdef";
825 DOMString Md5::finishHex()
826 {
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;
837 }
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)
861 {
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;
939 }
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
965 {
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
990 {
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)
1059 {
1060 Thread *thread = (Thread *)context;
1061 thread->execute();
1062 }
1065 void Thread::start()
1066 {
1067 DWORD dwThreadId;
1068 HANDLE hThread = CreateThread(NULL, 0, WinThreadFunction,
1069 (LPVOID)this, 0, &dwThreadId);
1070 //Make sure the thread is started before 'this' is deallocated
1071 while (!started)
1072 sleep(10);
1073 CloseHandle(hThread);
1074 }
1076 void Thread::sleep(unsigned long millis)
1077 {
1078 Sleep(millis);
1079 }
1081 #else /* UNIX */
1084 void *PthreadThreadFunction(void *context)
1085 {
1086 Thread *thread = (Thread *)context;
1087 thread->execute();
1088 return NULL;
1089 }
1092 void Thread::start()
1093 {
1094 pthread_t thread;
1096 int ret = pthread_create(&thread, NULL,
1097 PthreadThreadFunction, (void *)this);
1098 if (ret != 0)
1099 printf("Thread::start: thread creation failed: %s\n", strerror(ret));
1101 //Make sure the thread is started before 'this' is deallocated
1102 while (!started)
1103 sleep(10);
1105 }
1107 void Thread::sleep(unsigned long millis)
1108 {
1109 timespec requested;
1110 requested.tv_sec = millis / 1000;
1111 requested.tv_nsec = (millis % 1000 ) * 1000000L;
1112 nanosleep(&requested, NULL);
1113 }
1115 #endif
1118 //########################################################################
1119 //########################################################################
1120 //### S O C K E T
1121 //########################################################################
1122 //########################################################################
1128 class TcpSocket
1129 {
1130 public:
1132 TcpSocket();
1134 TcpSocket(const std::string &hostname, int port);
1136 TcpSocket(const char *hostname, int port);
1138 TcpSocket(const TcpSocket &other);
1140 virtual ~TcpSocket();
1142 bool isConnected();
1144 void enableSSL(bool val);
1146 bool connect(const std::string &hostname, int portno);
1148 bool connect(const char *hostname, int portno);
1150 bool startTls();
1152 bool connect();
1154 bool disconnect();
1156 bool setReceiveTimeout(unsigned long millis);
1158 long available();
1160 bool write(int ch);
1162 bool write(char *str);
1164 bool write(const std::string &str);
1166 int read();
1168 std::string readLine();
1170 private:
1171 void init();
1173 std::string hostname;
1174 int portno;
1175 int sock;
1176 bool connected;
1178 bool sslEnabled;
1180 unsigned long receiveTimeout;
1182 #ifdef WITH_SSL
1183 SSL_CTX *sslContext;
1184 SSL *sslStream;
1185 #endif
1187 };
1191 //#########################################################################
1192 //# U T I L I T Y
1193 //#########################################################################
1195 static void mybzero(void *s, size_t n)
1196 {
1197 unsigned char *p = (unsigned char *)s;
1198 while (n > 0)
1199 {
1200 *p++ = (unsigned char)0;
1201 n--;
1202 }
1203 }
1205 static void mybcopy(void *src, void *dest, size_t n)
1206 {
1207 unsigned char *p = (unsigned char *)dest;
1208 unsigned char *q = (unsigned char *)src;
1209 while (n > 0)
1210 {
1211 *p++ = *q++;
1212 n--;
1213 }
1214 }
1218 //#########################################################################
1219 //# T C P C O N N E C T I O N
1220 //#########################################################################
1222 TcpSocket::TcpSocket()
1223 {
1224 init();
1225 }
1228 TcpSocket::TcpSocket(const char *hostnameArg, int port)
1229 {
1230 init();
1231 hostname = hostnameArg;
1232 portno = port;
1233 }
1235 TcpSocket::TcpSocket(const std::string &hostnameArg, int port)
1236 {
1237 init();
1238 hostname = hostnameArg;
1239 portno = port;
1240 }
1243 #ifdef WITH_SSL
1245 static void cryptoLockCallback(int mode, int type, const char *file, int line)
1246 {
1247 //printf("########### LOCK\n");
1248 static int modes[CRYPTO_NUM_LOCKS]; /* = {0, 0, ... } */
1249 const char *errstr = NULL;
1251 int rw = mode & (CRYPTO_READ|CRYPTO_WRITE);
1252 if (!((rw == CRYPTO_READ) || (rw == CRYPTO_WRITE)))
1253 {
1254 errstr = "invalid mode";
1255 goto err;
1256 }
1258 if (type < 0 || type >= CRYPTO_NUM_LOCKS)
1259 {
1260 errstr = "type out of bounds";
1261 goto err;
1262 }
1264 if (mode & CRYPTO_LOCK)
1265 {
1266 if (modes[type])
1267 {
1268 errstr = "already locked";
1269 /* must not happen in a single-threaded program
1270 * (would deadlock)
1271 */
1272 goto err;
1273 }
1275 modes[type] = rw;
1276 }
1277 else if (mode & CRYPTO_UNLOCK)
1278 {
1279 if (!modes[type])
1280 {
1281 errstr = "not locked";
1282 goto err;
1283 }
1285 if (modes[type] != rw)
1286 {
1287 errstr = (rw == CRYPTO_READ) ?
1288 "CRYPTO_r_unlock on write lock" :
1289 "CRYPTO_w_unlock on read lock";
1290 }
1292 modes[type] = 0;
1293 }
1294 else
1295 {
1296 errstr = "invalid mode";
1297 goto err;
1298 }
1300 err:
1301 if (errstr)
1302 {
1303 /* we cannot use bio_err here */
1304 fprintf(stderr, "openssl (lock_dbg_cb): %s (mode=%d, type=%d) at %s:%d\n",
1305 errstr, mode, type, file, line);
1306 }
1307 }
1309 static unsigned long cryptoIdCallback()
1310 {
1311 #ifdef __WIN32__
1312 unsigned long ret = (unsigned long) GetCurrentThreadId();
1313 #else
1314 unsigned long ret = (unsigned long) pthread_self();
1315 #endif
1316 return ret;
1317 }
1319 #endif
1322 TcpSocket::TcpSocket(const TcpSocket &other)
1323 {
1324 init();
1325 sock = other.sock;
1326 hostname = other.hostname;
1327 portno = other.portno;
1328 }
1330 static bool tcp_socket_inited = false;
1332 void TcpSocket::init()
1333 {
1334 if (!tcp_socket_inited)
1335 {
1336 #ifdef __WIN32__
1337 WORD wVersionRequested = MAKEWORD( 2, 2 );
1338 WSADATA wsaData;
1339 int err = WSAStartup( wVersionRequested, &wsaData );
1340 #endif
1341 #ifdef WITH_SSL
1342 sslStream = NULL;
1343 sslContext = NULL;
1344 CRYPTO_set_locking_callback(cryptoLockCallback);
1345 CRYPTO_set_id_callback(cryptoIdCallback);
1346 SSL_library_init();
1347 SSL_load_error_strings();
1348 #endif
1349 tcp_socket_inited = true;
1350 }
1351 sock = -1;
1352 connected = false;
1353 hostname = "";
1354 portno = -1;
1355 sslEnabled = false;
1356 receiveTimeout = 0;
1357 }
1359 TcpSocket::~TcpSocket()
1360 {
1361 disconnect();
1362 }
1364 bool TcpSocket::isConnected()
1365 {
1366 if (!connected || sock < 0)
1367 return false;
1368 return true;
1369 }
1371 void TcpSocket::enableSSL(bool val)
1372 {
1373 sslEnabled = val;
1374 }
1377 bool TcpSocket::connect(const char *hostnameArg, int portnoArg)
1378 {
1379 hostname = hostnameArg;
1380 portno = portnoArg;
1381 return connect();
1382 }
1384 bool TcpSocket::connect(const std::string &hostnameArg, int portnoArg)
1385 {
1386 hostname = hostnameArg;
1387 portno = portnoArg;
1388 return connect();
1389 }
1393 #ifdef WITH_SSL
1394 /*
1395 static int password_cb(char *buf, int bufLen, int rwflag, void *userdata)
1396 {
1397 char *password = "password";
1398 if (bufLen < (int)(strlen(password)+1))
1399 return 0;
1401 strcpy(buf,password);
1402 int ret = strlen(password);
1403 return ret;
1404 }
1406 static void infoCallback(const SSL *ssl, int where, int ret)
1407 {
1408 switch (where)
1409 {
1410 case SSL_CB_ALERT:
1411 {
1412 printf("## %d SSL ALERT: %s\n", where, SSL_alert_desc_string_long(ret));
1413 break;
1414 }
1415 default:
1416 {
1417 printf("## %d SSL: %s\n", where, SSL_state_string_long(ssl));
1418 break;
1419 }
1420 }
1421 }
1422 */
1423 #endif
1426 bool TcpSocket::startTls()
1427 {
1428 #ifdef WITH_SSL
1429 sslStream = NULL;
1430 sslContext = NULL;
1432 //SSL_METHOD *meth = SSLv23_method();
1433 //SSL_METHOD *meth = SSLv3_client_method();
1434 SSL_METHOD *meth = TLSv1_client_method();
1435 sslContext = SSL_CTX_new(meth);
1436 //SSL_CTX_set_info_callback(sslContext, infoCallback);
1438 #if 0
1439 char *keyFile = "client.pem";
1440 char *caList = "root.pem";
1441 /* Load our keys and certificates*/
1442 if (!(SSL_CTX_use_certificate_chain_file(sslContext, keyFile)))
1443 {
1444 fprintf(stderr, "Can't read certificate file\n");
1445 disconnect();
1446 return false;
1447 }
1449 SSL_CTX_set_default_passwd_cb(sslContext, password_cb);
1451 if (!(SSL_CTX_use_PrivateKey_file(sslContext, keyFile, SSL_FILETYPE_PEM)))
1452 {
1453 fprintf(stderr, "Can't read key file\n");
1454 disconnect();
1455 return false;
1456 }
1458 /* Load the CAs we trust*/
1459 if (!(SSL_CTX_load_verify_locations(sslContext, caList, 0)))
1460 {
1461 fprintf(stderr, "Can't read CA list\n");
1462 disconnect();
1463 return false;
1464 }
1465 #endif
1467 /* Connect the SSL socket */
1468 sslStream = SSL_new(sslContext);
1469 SSL_set_fd(sslStream, sock);
1471 if (SSL_connect(sslStream)<=0)
1472 {
1473 fprintf(stderr, "SSL connect error\n");
1474 disconnect();
1475 return false;
1476 }
1478 sslEnabled = true;
1479 #endif /*WITH_SSL*/
1480 return true;
1481 }
1484 bool TcpSocket::connect()
1485 {
1486 if (hostname.size()<1)
1487 {
1488 printf("open: null hostname\n");
1489 return false;
1490 }
1492 if (portno<1)
1493 {
1494 printf("open: bad port number\n");
1495 return false;
1496 }
1498 sock = socket(PF_INET, SOCK_STREAM, 0);
1499 if (sock < 0)
1500 {
1501 printf("open: error creating socket\n");
1502 return false;
1503 }
1505 char *c_hostname = (char *)hostname.c_str();
1506 struct hostent *server = gethostbyname(c_hostname);
1507 if (!server)
1508 {
1509 printf("open: could not locate host '%s'\n", c_hostname);
1510 return false;
1511 }
1513 struct sockaddr_in serv_addr;
1514 mybzero((char *) &serv_addr, sizeof(serv_addr));
1515 serv_addr.sin_family = AF_INET;
1516 mybcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr,
1517 server->h_length);
1518 serv_addr.sin_port = htons(portno);
1520 int ret = ::connect(sock, (const sockaddr *)&serv_addr, sizeof(serv_addr));
1521 if (ret < 0)
1522 {
1523 printf("open: could not connect to host '%s'\n", c_hostname);
1524 return false;
1525 }
1527 if (sslEnabled)
1528 {
1529 if (!startTls())
1530 return false;
1531 }
1532 connected = true;
1533 return true;
1534 }
1536 bool TcpSocket::disconnect()
1537 {
1538 bool ret = true;
1539 connected = false;
1540 #ifdef WITH_SSL
1541 if (sslEnabled)
1542 {
1543 if (sslStream)
1544 {
1545 int r = SSL_shutdown(sslStream);
1546 switch(r)
1547 {
1548 case 1:
1549 break; /* Success */
1550 case 0:
1551 case -1:
1552 default:
1553 //printf("Shutdown failed");
1554 ret = false;
1555 }
1556 SSL_free(sslStream);
1557 }
1558 if (sslContext)
1559 SSL_CTX_free(sslContext);
1560 }
1561 sslStream = NULL;
1562 sslContext = NULL;
1563 #endif /*WITH_SSL*/
1565 #ifdef __WIN32__
1566 closesocket(sock);
1567 #else
1568 ::close(sock);
1569 #endif
1570 sock = -1;
1571 sslEnabled = false;
1573 return ret;
1574 }
1578 bool TcpSocket::setReceiveTimeout(unsigned long millis)
1579 {
1580 receiveTimeout = millis;
1581 return true;
1582 }
1584 /**
1585 * For normal sockets, return the number of bytes waiting to be received.
1586 * For SSL, just return >0 when something is ready to be read.
1587 */
1588 long TcpSocket::available()
1589 {
1590 if (!isConnected())
1591 return -1;
1593 long count = 0;
1594 #ifdef __WIN32__
1595 if (ioctlsocket(sock, FIONREAD, (unsigned long *)&count) != 0)
1596 return -1;
1597 #else
1598 if (ioctl(sock, FIONREAD, &count) != 0)
1599 return -1;
1600 #endif
1601 if (count<=0 && sslEnabled)
1602 {
1603 #ifdef WITH_SSL
1604 return SSL_pending(sslStream);
1605 #endif
1606 }
1607 return count;
1608 }
1612 bool TcpSocket::write(int ch)
1613 {
1614 if (!isConnected())
1615 {
1616 printf("write: socket closed\n");
1617 return false;
1618 }
1619 unsigned char c = (unsigned char)ch;
1621 if (sslEnabled)
1622 {
1623 #ifdef WITH_SSL
1624 int r = SSL_write(sslStream, &c, 1);
1625 if (r<=0)
1626 {
1627 switch(SSL_get_error(sslStream, r))
1628 {
1629 default:
1630 printf("SSL write problem");
1631 return -1;
1632 }
1633 }
1634 #endif
1635 }
1636 else
1637 {
1638 if (send(sock, (const char *)&c, 1, 0) < 0)
1639 //if (send(sock, &c, 1, 0) < 0)
1640 {
1641 printf("write: could not send data\n");
1642 return false;
1643 }
1644 }
1645 return true;
1646 }
1648 bool TcpSocket::write(char *str)
1649 {
1650 if (!isConnected())
1651 {
1652 printf("write(str): socket closed\n");
1653 return false;
1654 }
1655 unsigned char *s = (unsigned char *)str;
1656 int len = strlen(str);
1658 if (sslEnabled)
1659 {
1660 #ifdef WITH_SSL
1661 int r = SSL_write(sslStream, s, len);
1662 if (r<=0)
1663 {
1664 switch(SSL_get_error(sslStream, r))
1665 {
1666 default:
1667 printf("SSL write problem");
1668 return -1;
1669 }
1670 }
1671 #endif
1672 }
1673 else
1674 {
1675 if (send(sock, s, len, 0) < 0)
1676 //if (send(sock, &c, 1, 0) < 0)
1677 {
1678 printf("write: could not send data\n");
1679 return false;
1680 }
1681 }
1682 return true;
1683 }
1685 bool TcpSocket::write(const std::string &str)
1686 {
1687 return write((char *)str.c_str());
1688 }
1690 int TcpSocket::read()
1691 {
1692 if (!isConnected())
1693 return -1;
1695 //We'll use this loop for timeouts, so that SSL and plain sockets
1696 //will behave the same way
1697 if (receiveTimeout > 0)
1698 {
1699 unsigned long tim = 0;
1700 while (true)
1701 {
1702 int avail = available();
1703 if (avail > 0)
1704 break;
1705 if (tim >= receiveTimeout)
1706 return -2;
1707 Thread::sleep(20);
1708 tim += 20;
1709 }
1710 }
1712 //check again
1713 if (!isConnected())
1714 return -1;
1716 unsigned char ch;
1717 if (sslEnabled)
1718 {
1719 #ifdef WITH_SSL
1720 if (!sslStream)
1721 return -1;
1722 int r = SSL_read(sslStream, &ch, 1);
1723 unsigned long err = SSL_get_error(sslStream, r);
1724 switch (err)
1725 {
1726 case SSL_ERROR_NONE:
1727 break;
1728 case SSL_ERROR_ZERO_RETURN:
1729 return -1;
1730 case SSL_ERROR_SYSCALL:
1731 printf("SSL read problem(syscall) %s\n",
1732 ERR_error_string(ERR_get_error(), NULL));
1733 return -1;
1734 default:
1735 printf("SSL read problem %s\n",
1736 ERR_error_string(ERR_get_error(), NULL));
1737 return -1;
1738 }
1739 #endif
1740 }
1741 else
1742 {
1743 if (recv(sock, (char *)&ch, 1, 0) <= 0)
1744 {
1745 printf("read: could not receive data\n");
1746 disconnect();
1747 return -1;
1748 }
1749 }
1750 return (int)ch;
1751 }
1753 std::string TcpSocket::readLine()
1754 {
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;
1768 }
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)
1786 {
1787 eventType = type;
1788 presence = false;
1789 dom = NULL;
1790 }
1792 XmppEvent::XmppEvent(const XmppEvent &other)
1793 {
1794 assign(other);
1795 }
1797 XmppEvent &XmppEvent::operator=(const XmppEvent &other)
1798 {
1799 assign(other);
1800 return (*this);
1801 }
1803 XmppEvent::~XmppEvent()
1804 {
1805 if (dom)
1806 delete dom;
1807 }
1809 void XmppEvent::assign(const XmppEvent &other)
1810 {
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);
1824 }
1826 int XmppEvent::getType() const
1827 {
1828 return eventType;
1829 }
1831 DOMString XmppEvent::getIqId() const
1832 {
1833 return iqId;
1834 }
1836 void XmppEvent::setIqId(const DOMString &val)
1837 {
1838 iqId = val;
1839 }
1841 DOMString XmppEvent::getStreamId() const
1842 {
1843 return streamId;
1844 }
1846 void XmppEvent::setStreamId(const DOMString &val)
1847 {
1848 streamId = val;
1849 }
1851 bool XmppEvent::getPresence() const
1852 {
1853 return presence;
1854 }
1856 void XmppEvent::setPresence(bool val)
1857 {
1858 presence = val;
1859 }
1861 DOMString XmppEvent::getShow() const
1862 {
1863 return show;
1864 }
1866 void XmppEvent::setShow(const DOMString &val)
1867 {
1868 show = val;
1869 }
1871 DOMString XmppEvent::getStatus() const
1872 {
1873 return status;
1874 }
1876 void XmppEvent::setStatus(const DOMString &val)
1877 {
1878 status = val;
1879 }
1881 DOMString XmppEvent::getTo() const
1882 {
1883 return to;
1884 }
1886 void XmppEvent::setTo(const DOMString &val)
1887 {
1888 to = val;
1889 }
1891 DOMString XmppEvent::getFrom() const
1892 {
1893 return from;
1894 }
1896 void XmppEvent::setFrom(const DOMString &val)
1897 {
1898 from = val;
1899 }
1901 DOMString XmppEvent::getGroup() const
1902 {
1903 return group;
1904 }
1906 void XmppEvent::setGroup(const DOMString &val)
1907 {
1908 group = val;
1909 }
1911 DOMString XmppEvent::getData() const
1912 {
1913 return data;
1914 }
1916 void XmppEvent::setData(const DOMString &val)
1917 {
1918 data = val;
1919 }
1921 DOMString XmppEvent::getFileName() const
1922 {
1923 return fileName;
1924 }
1926 void XmppEvent::setFileName(const DOMString &val)
1927 {
1928 fileName = val;
1929 }
1931 DOMString XmppEvent::getFileDesc() const
1932 {
1933 return fileDesc;
1934 }
1936 void XmppEvent::setFileDesc(const DOMString &val)
1937 {
1938 fileDesc = val;
1939 }
1941 long XmppEvent::getFileSize() const
1942 {
1943 return fileSize;
1944 }
1946 void XmppEvent::setFileSize(long val)
1947 {
1948 fileSize = val;
1949 }
1951 DOMString XmppEvent::getFileHash() const
1952 {
1953 return fileHash;
1954 }
1956 void XmppEvent::setFileHash(const DOMString &val)
1957 {
1958 fileHash = val;
1959 }
1961 Element *XmppEvent::getDOM() const
1962 {
1963 return dom;
1964 }
1966 void XmppEvent::setDOM(const Element *val)
1967 {
1968 if (!val)
1969 dom = NULL;
1970 else
1971 dom = ((Element *)val)->clone();
1972 }
1975 std::vector<XmppUser> XmppEvent::getUserList() const
1976 {
1977 return userList;
1978 }
1980 void XmppEvent::setUserList(const std::vector<XmppUser> &val)
1981 {
1982 userList = val;
1983 }
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()
1994 {
1995 eventQueueEnabled = false;
1996 }
1999 XmppEventTarget::XmppEventTarget(const XmppEventTarget &other)
2000 {
2001 listeners = other.listeners;
2002 eventQueueEnabled = other.eventQueueEnabled;
2003 }
2005 XmppEventTarget::~XmppEventTarget()
2006 {
2007 }
2010 //###########################
2011 //# M E S S A G E S
2012 //###########################
2014 void XmppEventTarget::error(char *fmt, ...)
2015 {
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);
2024 }
2026 void XmppEventTarget::status(char *fmt, ...)
2027 {
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);
2036 }
2040 //###########################
2041 //# L I S T E N E R S
2042 //###########################
2044 void XmppEventTarget::dispatchXmppEvent(const XmppEvent &event)
2045 {
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);
2051 }
2053 void XmppEventTarget::addXmppEventListener(const XmppEventListener &listener)
2054 {
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);
2061 }
2063 void XmppEventTarget::removeXmppEventListener(const XmppEventListener &listener)
2064 {
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);
2070 }
2072 void XmppEventTarget::clearXmppEventListeners()
2073 {
2074 listeners.clear();
2075 }
2078 //###########################
2079 //# E V E N T Q U E U E
2080 //###########################
2082 void XmppEventTarget::eventQueueEnable(bool val)
2083 {
2084 eventQueueEnabled = val;
2085 if (!eventQueueEnabled)
2086 eventQueue.clear();
2087 }
2089 int XmppEventTarget::eventQueueAvailable()
2090 {
2091 return eventQueue.size();
2092 }
2094 XmppEvent XmppEventTarget::eventQueuePop()
2095 {
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;
2104 }
2107 //########################################################################
2108 //# X M P P S T R E A M
2109 //########################################################################
2111 /**
2112 *
2113 */
2114 class XmppStream
2115 {
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()
2214 {
2215 reset();
2216 }
2218 /**
2219 *
2220 */
2221 XmppStream::~XmppStream()
2222 {
2223 reset();
2224 }
2226 /**
2227 *
2228 */
2229 void XmppStream::reset()
2230 {
2231 state = XmppClient::STREAM_AVAILABLE;
2232 seqNr = 0;
2233 data.clear();
2234 }
2236 /**
2237 *
2238 */
2239 int XmppStream::getState()
2240 {
2241 return state;
2242 }
2244 /**
2245 *
2246 */
2247 void XmppStream::setState(int val)
2248 {
2249 state = val;
2250 }
2252 /**
2253 *
2254 */
2255 DOMString XmppStream::getStreamId()
2256 {
2257 return streamId;
2258 }
2260 /**
2261 *
2262 */
2263 void XmppStream::setStreamId(const DOMString &val)
2264 {
2265 streamId = val;
2266 }
2268 /**
2269 *
2270 */
2271 DOMString XmppStream::getIqId()
2272 {
2273 return iqId;
2274 }
2277 /**
2278 *
2279 */
2280 void XmppStream::setIqId(const DOMString &val)
2281 {
2282 iqId = val;
2283 }
2285 /**
2286 * Source or destination JID
2287 */
2288 void XmppStream::setPeerId(const DOMString &val)
2289 {
2290 sourceId = val;
2291 }
2293 /**
2294 * Source or destination JID
2295 */
2296 DOMString XmppStream::getPeerId()
2297 {
2298 return sourceId;
2299 }
2301 /**
2302 * Stream packet sequence number
2303 */
2304 int XmppStream::getSeqNr()
2305 {
2306 seqNr++;
2307 if (seqNr >= 65535)
2308 seqNr = 0;
2309 return seqNr;
2310 }
2312 /**
2313 *
2314 */
2315 int XmppStream::available()
2316 {
2317 return data.size();
2318 }
2320 /**
2321 *
2322 */
2323 void XmppStream::receiveData(std::vector<unsigned char> &newData)
2324 {
2325 std::vector<unsigned char>::iterator iter;
2326 for (iter=newData.begin() ; iter!=newData.end() ; iter++)
2327 data.push_back(*iter);
2328 }
2330 /**
2331 *
2332 */
2333 std::vector<unsigned char> XmppStream::read()
2334 {
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;
2343 }
2350 //########################################################################
2351 //# X M P P C L I E N T
2352 //########################################################################
2353 class ReceiverThread : public Runnable
2354 {
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()
2375 {
2376 init();
2377 }
2380 XmppClient::XmppClient(const XmppClient &other) : XmppEventTarget(other)
2381 {
2382 init();
2383 assign(other);
2384 }
2386 void XmppClient::assign(const XmppClient &other)
2387 {
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;
2397 }
2400 void XmppClient::init()
2401 {
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 }
2418 }
2420 XmppClient::~XmppClient()
2421 {
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();
2437 }
2439 //##############################################
2440 //# UTILILY
2441 //##############################################
2443 /**
2444 *
2445 */
2446 bool XmppClient::pause(unsigned long millis)
2447 {
2448 Thread::sleep(millis);
2449 return true;
2450 }
2453 static int strIndex(const DOMString &str, char *key)
2454 {
2455 unsigned int p = str.find(key);
2456 if (p == DOMString::npos)
2457 return -1;
2458 return p;
2459 }
2462 static DOMString toXml(const DOMString &str)
2463 {
2464 return Parser::encode(str);
2465 }
2467 static DOMString trim(const DOMString &str)
2468 {
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);
2481 }
2483 //##############################################
2484 //# VARIABLES (ones that need special handling)
2485 //##############################################
2486 /**
2487 *
2488 */
2489 DOMString XmppClient::getUsername()
2490 {
2491 return username;
2492 }
2494 /**
2495 *
2496 */
2497 void XmppClient::setUsername(const DOMString &val)
2498 {
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 }
2510 }
2512 //##############################################
2513 //# CONNECTION
2514 //##############################################
2516 //#######################
2517 //# RECEIVING
2518 //#######################
2519 DOMString XmppClient::readStanza()
2520 {
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;
2621 }
2625 static bool isGroupChat(Element *root)
2626 {
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;
2638 }
2643 static bool getGroupIds(const DOMString &jid,
2644 DOMString &groupJid, DOMString &userNick)
2645 {
2646 int p = strIndex(jid, "/");
2647 if (p < 0)
2648 {
2649 groupJid = jid;
2650 userNick = "";
2651 return true;
2652 }
2653 groupJid = jid.substr(0, p);
2654 userNick = jid.substr(p+1, jid.size()-p-1);
2655 return true;
2656 }
2661 bool XmppClient::processMessage(Element *root)
2662 {
2663 DOMString from = root->getTagAttribute("message", "from");
2664 DOMString to = root->getTagAttribute("message", "to");
2665 DOMString type = root->getTagAttribute("message", "type");
2667 //####Check for embedded namespaces here
2668 //# IN BAND BYTESTREAMS
2669 DOMString ibbNamespace = "http://jabber.org/protocol/ibb";
2670 if (root->getTagAttribute("data", "xmlns") == ibbNamespace)
2671 {
2672 DOMString streamId = root->getTagAttribute("data", "sid");
2673 if (streamId.size() > 0)
2674 {
2675 for (int i=0 ; i<inputStreamCount ; i++)
2676 {
2677 XmppStream *ins = inputStreams[i];
2678 //printf("##ins:%s streamid:%s\n",
2679 // ins->getStreamId().c_str(), streamId.c_str());
2680 if (ins->getStreamId() == streamId)
2681 {
2682 //# We have a winner
2683 if (ins->getState() != STREAM_OPEN)
2684 {
2685 XmppEvent event(XmppEvent::EVENT_ERROR);
2686 event.setFrom(from);
2687 event.setData("received unrequested stream data");
2688 dispatchXmppEvent(event);
2689 return true;
2690 }
2691 DOMString data = root->getTagValue("data");
2692 std::vector<unsigned char>binData =
2693 Base64Decoder::decode(data);
2694 ins->receiveData(binData);
2695 }
2696 }
2697 }
2698 }
2701 //#### NORMAL MESSAGES
2702 DOMString subject = root->getTagValue("subject");
2703 DOMString body = root->getTagValue("body");
2704 DOMString thread = root->getTagValue("thread");
2705 //##rfc 3921, para 2.4. ignore if no recognizable info
2706 if (subject.size() < 1 && body.size()<1 && thread.size()<1)
2707 return true;
2709 if (type == "groupchat")
2710 {
2711 DOMString fromGid;
2712 DOMString fromNick;
2713 getGroupIds(from, fromGid, fromNick);
2714 //printf("fromGid:%s fromNick:%s\n",
2715 // fromGid.c_str(), fromNick.c_str());
2716 DOMString toGid;
2717 DOMString toNick;
2718 getGroupIds(to, toGid, toNick);
2719 //printf("toGid:%s toNick:%s\n",
2720 // toGid.c_str(), toNick.c_str());
2722 if (fromNick.size() > 0)//normal group message
2723 {
2724 XmppEvent event(XmppEvent::EVENT_MUC_MESSAGE);
2725 event.setGroup(fromGid);
2726 event.setFrom(fromNick);
2727 event.setData(body);
2728 event.setDOM(root);
2729 dispatchXmppEvent(event);
2730 }
2731 else // from the server itself
2732 {
2733 //note the space before, so it doesnt match 'unlocked'
2734 if (strIndex(body, " locked") >= 0)
2735 {
2736 printf("LOCKED!! ;)\n");
2737 char *fmt =
2738 "<iq from='%s' id='create%d' to='%s' type='set'>"
2739 "<query xmlns='http://jabber.org/protocol/muc#owner'>"
2740 "<x xmlns='jabber:x:data' type='submit'/>"
2741 "</query></iq>\n";
2742 if (!write(fmt, jid.c_str(), msgId++, fromGid.c_str()))
2743 return false;
2744 }
2745 }
2746 }
2747 else
2748 {
2749 XmppEvent event(XmppEvent::EVENT_MESSAGE);
2750 event.setFrom(from);
2751 event.setData(body);
2752 event.setDOM(root);
2753 dispatchXmppEvent(event);
2754 }
2756 return true;
2757 }
2762 bool XmppClient::processPresence(Element *root)
2763 {
2765 DOMString from = root->getTagAttribute("presence", "from");
2766 DOMString to = root->getTagAttribute("presence", "to");
2767 DOMString presenceStr = root->getTagAttribute("presence", "type");
2768 bool presence = true;
2769 if (presenceStr == "unavailable")
2770 presence = false;
2771 DOMString status = root->getTagValue("status");
2772 DOMString show = root->getTagValue("show");
2774 if (isGroupChat(root))
2775 {
2776 DOMString fromGid;
2777 DOMString fromNick;
2778 getGroupIds(from, fromGid, fromNick);
2779 //printf("fromGid:%s fromNick:%s\n",
2780 // fromGid.c_str(), fromNick.c_str());
2781 DOMString item_jid = root->getTagAttribute("item", "jid");
2782 if (item_jid == jid) //Me
2783 {
2784 if (presence)
2785 {
2786 groupChatCreate(fromGid);
2787 groupChatUserAdd(fromGid, fromNick);
2789 XmppEvent event(XmppEvent::EVENT_MUC_JOIN);
2790 event.setGroup(fromGid);
2791 event.setFrom(fromNick);
2792 event.setPresence(presence);
2793 event.setShow(show);
2794 event.setStatus(status);
2795 dispatchXmppEvent(event);
2796 }
2797 else
2798 {
2799 groupChatDelete(fromGid);
2800 groupChatUserDelete(fromGid, fromNick);
2802 XmppEvent event(XmppEvent::EVENT_MUC_LEAVE);
2803 event.setGroup(fromGid);
2804 event.setFrom(fromNick);
2805 event.setPresence(presence);
2806 event.setShow(show);
2807 event.setStatus(status);
2808 dispatchXmppEvent(event);
2809 }
2810 }
2811 else
2812 {
2813 if (presence)
2814 groupChatUserAdd(fromGid, fromNick);
2815 else
2816 groupChatUserDelete(fromGid, fromNick);
2817 XmppEvent event(XmppEvent::EVENT_MUC_PRESENCE);
2818 event.setGroup(fromGid);
2819 event.setFrom(fromNick);
2820 event.setPresence(presence);
2821 event.setShow(show);
2822 event.setStatus(status);
2823 dispatchXmppEvent(event);
2824 }
2825 }
2826 else
2827 {
2828 XmppEvent event(XmppEvent::EVENT_PRESENCE);
2829 event.setFrom(from);
2830 event.setPresence(presence);
2831 event.setShow(show);
2832 event.setStatus(status);
2833 dispatchXmppEvent(event);
2834 }
2836 return true;
2837 }
2841 bool XmppClient::processIq(Element *root)
2842 {
2843 DOMString from = root->getTagAttribute("iq", "from");
2844 DOMString id = root->getTagAttribute("iq", "id");
2845 DOMString type = root->getTagAttribute("iq", "type");
2846 DOMString xmlns = root->getTagAttribute("query", "xmlns");
2848 if (id.size()<1)
2849 return true;
2851 //Group chat
2852 if (strIndex(xmlns, "http://jabber.org/protocol/muc") >=0 )
2853 {
2854 printf("results of MUC query\n");
2855 }
2856 //printf("###IQ xmlns:%s\n", xmlns.c_str());
2858 //### FILE TRANSFERS
2859 DOMString siNamespace = "http://jabber.org/protocol/si";
2860 if (root->getTagAttribute("si", "xmlns") == siNamespace)
2861 {
2862 if (type == "set")
2863 {
2864 DOMString streamId = root->getTagAttribute("si", "id");
2865 DOMString fname = root->getTagAttribute("file", "name");
2866 DOMString sizeStr = root->getTagAttribute("file", "size");
2867 DOMString hash = root->getTagAttribute("file", "hash");
2868 XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_RECEIVE);
2869 event.setFrom(from);
2870 event.setIqId(id);
2871 event.setStreamId(streamId);
2872 event.setFileName(fname);
2873 event.setFileHash(hash);
2874 event.setFileSize(atol(sizeStr.c_str()));
2875 dispatchXmppEvent(event);
2876 }
2877 else //result
2878 {
2879 printf("Got result\n");
2880 for (int i=0 ; i<fileSendCount ; i++)
2881 {
2882 XmppStream *outf = fileSends[i];
2883 if (outf->getIqId() == id &&
2884 from == outf->getPeerId())
2885 {
2886 if (type == "error")
2887 {
2888 outf->setState(STREAM_ERROR);
2889 error("user '%s' rejected file", from.c_str());
2890 return true;
2891 }
2892 else if (type == "result")
2893 {
2894 if (outf->getState() == STREAM_OPENING)
2895 {
2896 XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_ACCEPTED);
2897 event.setFrom(from);
2898 dispatchXmppEvent(event);
2899 outf->setState(STREAM_OPEN);
2900 }
2901 else if (outf->getState() == STREAM_CLOSING)
2902 {
2903 outf->setState(STREAM_CLOSED);
2904 }
2905 return true;
2906 }
2907 }
2908 }
2909 }
2910 return true;
2911 }
2914 //### IN-BAND BYTESTREAMS
2915 //### Incoming stream requests
2916 DOMString ibbNamespace = "http://jabber.org/protocol/ibb";
2917 if (root->getTagAttribute("open", "xmlns") == ibbNamespace)
2918 {
2919 DOMString streamId = root->getTagAttribute("open", "sid");
2920 XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_INIT);
2921 dispatchXmppEvent(event);
2922 if (streamId.size()>0)
2923 {
2924 for (int i=0 ; i<inputStreamCount ; i++)
2925 {
2926 XmppStream *ins = inputStreams[i];
2927 if (ins->getStreamId() == streamId)
2928 {
2929 ins->setState(STREAM_OPENING);
2930 ins->setIqId(id);
2931 return true;
2932 }
2933 }
2934 }
2935 return true;
2936 }
2937 else if (root->getTagAttribute("close", "xmlns") == ibbNamespace)
2938 {
2939 XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_CLOSE);
2940 dispatchXmppEvent(event);
2941 DOMString streamId = root->getTagAttribute("close", "sid");
2942 if (streamId.size()>0)
2943 {
2944 for (int i=0 ; i<inputStreamCount ; i++)
2945 {
2946 XmppStream *ins = inputStreams[i];
2947 if (ins->getStreamId() == streamId &&
2948 from == ins->getPeerId())
2949 {
2950 ins->setState(STREAM_CLOSING);
2951 ins->setIqId(id);
2952 return true;
2953 }
2954 }
2955 }
2956 return true;
2957 }
2958 //### Responses to outgoing requests
2959 for (int i=0 ; i<outputStreamCount ; i++)
2960 {
2961 XmppStream *outs = outputStreams[i];
2962 if (outs->getIqId() == id)
2963 {
2964 if (type == "error")
2965 {
2966 outs->setState(STREAM_ERROR);
2967 return true;
2968 }
2969 else if (type == "result")
2970 {
2971 if (outs->getState() == STREAM_OPENING)
2972 {
2973 outs->setState(STREAM_OPEN);
2974 }
2975 else if (outs->getState() == STREAM_CLOSING)
2976 {
2977 outs->setState(STREAM_CLOSED);
2978 }
2979 return true;
2980 }
2981 }
2982 }
2984 //###Normal Roster stuff
2985 if (root->getTagAttribute("query", "xmlns") == "jabber:iq:roster")
2986 {
2987 roster.clear();
2988 std::vector<Element *>elems = root->findElements("item");
2989 for (unsigned int i=0 ; i<elems.size() ; i++)
2990 {
2991 Element *item = elems[i];
2992 DOMString userJid = item->getAttribute("jid");
2993 DOMString name = item->getAttribute("name");
2994 DOMString subscription = item->getAttribute("subscription");
2995 DOMString group = item->getTagValue("group");
2996 //printf("jid:%s name:%s sub:%s group:%s\n", userJid.c_str(), name.c_str(),
2997 // subscription.c_str(), group.c_str());
2998 XmppUser user(userJid, name, subscription, group);
2999 roster.push_back(user);
3000 }
3001 XmppEvent event(XmppEvent::XmppEvent::EVENT_ROSTER);
3002 dispatchXmppEvent(event);
3003 }
3005 return true;
3006 }
3010 bool XmppClient::receiveAndProcess()
3011 {
3012 if (!keepGoing)
3013 return false;
3015 Parser parser;
3017 DOMString recvBuf = readStanza();
3018 recvBuf = trim(recvBuf);
3019 if (recvBuf.size() < 1)
3020 return true;
3022 //Ugly hack. Apparently the first char can be dropped on timeouts
3023 //if (recvBuf[0] != '<')
3024 // recvBuf.insert(0, "<");
3026 status("RECV: %s", recvBuf.c_str());
3027 Element *root = parser.parse(recvBuf);
3028 if (!root)
3029 {
3030 printf("Bad elem\n");
3031 return true;
3032 }
3034 //#### MESSAGE
3035 std::vector<Element *>elems = root->findElements("message");
3036 if (elems.size()>0)
3037 {
3038 if (!processMessage(root))
3039 return false;
3040 }
3042 //#### PRESENCE
3043 elems = root->findElements("presence");
3044 if (elems.size()>0)
3045 {
3046 if (!processPresence(root))
3047 return false;
3048 }
3050 //#### INFO
3051 elems = root->findElements("iq");
3052 if (elems.size()>0)
3053 {
3054 if (!processIq(root))
3055 return false;
3056 }
3058 delete root;
3060 return true;
3061 }
3064 bool XmppClient::receiveAndProcessLoop()
3065 {
3066 keepGoing = true;
3067 while (true)
3068 {
3069 if (!keepGoing)
3070 {
3071 printf("Abort requested\n");
3072 break;
3073 }
3074 if (!receiveAndProcess())
3075 return false;
3076 }
3077 return true;
3078 }
3080 //#######################
3081 //# SENDING
3082 //#######################
3084 bool XmppClient::write(char *fmt, ...)
3085 {
3086 va_list args;
3087 va_start(args,fmt);
3088 vsnprintf((char *)writeBuf, writeBufLen, fmt,args);
3089 va_end(args) ;
3090 status("SEND: %s", writeBuf);
3091 if (!sock->write((char *)writeBuf))
3092 {
3093 error("Cannot write to socket");
3094 return false;
3095 }
3096 return true;
3097 }
3099 //#######################
3100 //# CONNECT
3101 //#######################
3103 bool XmppClient::checkConnect()
3104 {
3105 if (!connected)
3106 {
3107 XmppEvent evt(XmppEvent::EVENT_ERROR);
3108 evt.setData("Attempted operation while disconnected");
3109 dispatchXmppEvent(evt);
3110 return false;
3111 }
3112 return true;
3113 }
3115 bool XmppClient::iqAuthenticate(const DOMString &streamId)
3116 {
3117 Parser parser;
3119 char *fmt =
3120 "<iq type='get' to='%s' id='auth%d'>"
3121 "<query xmlns='jabber:iq:auth'><username>%s</username></query>"
3122 "</iq>\n";
3123 if (!write(fmt, realm.c_str(), msgId++, username.c_str()))
3124 return false;
3126 DOMString recbuf = readStanza();
3127 //printf("iq received: '%s'\n", recbuf.c_str());
3128 Element *elem = parser.parse(recbuf);
3129 //elem->print();
3130 DOMString iqType = elem->getTagAttribute("iq", "type");
3131 //printf("##iqType:%s\n", iqType.c_str());
3132 delete elem;
3134 if (iqType != "result")
3135 {
3136 error("error:server does not allow login");
3137 return false;
3138 }
3140 bool digest = true;
3141 if (digest)
3142 {
3143 //## Digest authentication
3144 DOMString digest = streamId;
3145 digest.append(password);
3146 digest = Sha1::hashHex((unsigned char *)digest.c_str(), digest.size());
3147 //printf("digest:%s\n", digest.c_str());
3148 fmt =
3149 "<iq type='set' id='auth%d'>"
3150 "<query xmlns='jabber:iq:auth'>"
3151 "<username>%s</username>"
3152 "<digest>%s</digest>"
3153 "<resource>%s</resource>"
3154 "</query>"
3155 "</iq>\n";
3156 if (!write(fmt, msgId++, username.c_str(),
3157 digest.c_str(), resource.c_str()))
3158 return false;
3159 }
3160 else
3161 {
3163 //## Plaintext authentication
3164 fmt =
3165 "<iq type='set' id='auth%d'>"
3166 "<query xmlns='jabber:iq:auth'>"
3167 "<username>%s</username>"
3168 "<password>%s</password>"
3169 "<resource>%s</resource>"
3170 "</query>"
3171 "</iq>\n";
3172 if (!write(fmt, msgId++, username.c_str(),
3173 password.c_str(), resource.c_str()))
3174 return false;
3175 }
3177 recbuf = readStanza();
3178 //printf("iq received: '%s'\n", recbuf.c_str());
3179 elem = parser.parse(recbuf);
3180 //elem->print();
3181 iqType = elem->getTagAttribute("iq", "type");
3182 //printf("##iqType:%s\n", iqType.c_str());
3183 delete elem;
3185 if (iqType != "result")
3186 {
3187 error("server does not allow login");
3188 return false;
3189 }
3191 return true;
3192 }
3196 bool XmppClient::saslMd5Authenticate()
3197 {
3198 Parser parser;
3199 char *fmt =
3200 "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
3201 "mechanism='DIGEST-MD5'/>\n";
3202 if (!write(fmt))
3203 return false;
3205 DOMString recbuf = readStanza();
3206 status("challenge received: '%s'", recbuf.c_str());
3207 Element *elem = parser.parse(recbuf);
3208 //elem->print();
3209 DOMString b64challenge = elem->getTagValue("challenge");
3210 delete elem;
3212 if (b64challenge.size() < 1)
3213 {
3214 error("login: no SASL challenge offered by server");
3215 return false;
3216 }
3217 DOMString challenge = Base64Decoder::decodeToString(b64challenge);
3218 status("challenge:'%s'", challenge.c_str());
3220 unsigned int p1 = challenge.find("nonce=\"");
3221 if (p1 == DOMString::npos)
3222 {
3223 error("login: no SASL nonce sent by server");
3224 return false;
3225 }
3226 p1 += 7;
3227 unsigned int p2 = challenge.find("\"", p1);
3228 if (p2 == DOMString::npos)
3229 {
3230 error("login: unterminated SASL nonce sent by server");
3231 return false;
3232 }
3233 DOMString nonce = challenge.substr(p1, p2-p1);
3234 //printf("nonce: '%s'\n", nonce.c_str());
3235 char idBuf[7];
3236 snprintf(idBuf, 6, "%dsasl", msgId++);
3237 DOMString cnonce = Sha1::hashHex((unsigned char *)idBuf, 7);
3238 DOMString authzid = username; authzid.append("@"); authzid.append(host);
3239 DOMString digest_uri = "xmpp/"; digest_uri.append(host);
3241 //## Make A1
3242 Md5 md5;
3243 md5.append(username);
3244 md5.append(":");
3245 md5.append(realm);
3246 md5.append(":");
3247 md5.append(password);
3248 unsigned char a1tmp[16];
3249 md5.finish(a1tmp);
3250 md5.init();
3251 md5.append(a1tmp, 16);
3252 md5.append(":");
3253 md5.append(nonce);
3254 md5.append(":");
3255 md5.append(cnonce);
3256 md5.append(":");
3257 md5.append(authzid);
3258 DOMString a1 = md5.finishHex();
3259 status("##a1:'%s'", a1.c_str());
3261 //# Make A2
3262 md5.init();
3263 md5.append("AUTHENTICATE:");
3264 md5.append(digest_uri);
3265 DOMString a2 = md5.finishHex();
3266 status("##a2:'%s'", a2.c_str());
3268 //# Now make the response
3269 md5.init();
3270 md5.append(a1);
3271 md5.append(":");
3272 md5.append(nonce);
3273 md5.append(":");
3274 md5.append("00000001");//nc
3275 md5.append(":");
3276 md5.append(cnonce);
3277 md5.append(":");
3278 md5.append("auth");//qop
3279 md5.append(":");
3280 md5.append(a2);
3281 DOMString response = md5.finishHex();
3283 DOMString resp;
3284 resp.append("username=\""); resp.append(username); resp.append("\",");
3285 resp.append("realm=\""); resp.append(realm); resp.append("\",");
3286 resp.append("nonce=\""); resp.append(nonce); resp.append("\",");
3287 resp.append("cnonce=\""); resp.append(cnonce); resp.append("\",");
3288 resp.append("nc=00000001,qop=auth,");
3289 resp.append("digest-uri=\""); resp.append(digest_uri); resp.append("\"," );
3290 resp.append("authzid=\""); resp.append(authzid); resp.append("\",");
3291 resp.append("response=\""); resp.append(response); resp.append("\",");
3292 resp.append("charset=utf-8");
3293 status("sending response:'%s'", resp.c_str());
3294 resp = Base64Encoder::encode(resp);
3295 status("base64 response:'%s'", resp.c_str());
3296 fmt =
3297 "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>%s</response>\n";
3298 if (!write(fmt, resp.c_str()))
3299 return false;
3301 recbuf = readStanza();
3302 status("server says:: '%s'", recbuf.c_str());
3303 elem = parser.parse(recbuf);
3304 //elem->print();
3305 b64challenge = elem->getTagValue("challenge");
3306 delete elem;
3308 if (b64challenge.size() < 1)
3309 {
3310 error("login: no second SASL challenge offered by server");
3311 return false;
3312 }
3314 challenge = Base64Decoder::decodeToString(b64challenge);
3315 status("challenge: '%s'", challenge.c_str());
3316 p1 = challenge.find("rspauth=");
3317 if (p1 == DOMString::npos)
3318 {
3319 error("login: no SASL respauth sent by server\n");
3320 return false;
3321 }
3323 fmt =
3324 "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>\n";
3325 if (!write(fmt))
3326 return false;
3328 recbuf = readStanza();
3329 status("server says: '%s", recbuf.c_str());
3330 elem = parser.parse(recbuf);
3331 //elem->print();
3332 b64challenge = elem->getTagValue("challenge");
3333 bool success = (elem->findElements("success").size() > 0);
3334 delete elem;
3336 return success;
3337 }
3339 bool XmppClient::saslPlainAuthenticate()
3340 {
3341 Parser parser;
3343 DOMString id = username;
3344 //id.append("@");
3345 //id.append(host);
3346 Base64Encoder encoder;
3347 encoder.append('\0');
3348 encoder.append(id);
3349 encoder.append('\0');
3350 encoder.append(password);
3351 DOMString base64Auth = encoder.finish();
3352 //printf("authbuf:%s\n", base64Auth.c_str());
3354 char *fmt =
3355 "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
3356 "mechanism='PLAIN'>%s</auth>\n";
3357 if (!write(fmt, base64Auth.c_str()))
3358 return false;
3359 DOMString recbuf = readStanza();
3360 status("challenge received: '%s'", recbuf.c_str());
3361 Element *elem = parser.parse(recbuf);
3363 bool success = (elem->findElements("success").size() > 0);
3364 delete elem;
3366 return success;
3367 }
3371 bool XmppClient::saslAuthenticate()
3372 {
3373 Parser parser;
3375 DOMString recbuf = readStanza();
3376 status("RECV: '%s'\n", recbuf.c_str());
3377 Element *elem = parser.parse(recbuf);
3378 //elem->print();
3380 //Check for starttls
3381 bool wantStartTls = false;
3382 if (elem->findElements("starttls").size() > 0)
3383 {
3384 wantStartTls = true;
3385 if (elem->findElements("required").size() > 0)
3386 status("login: STARTTLS required");
3387 else
3388 status("login: STARTTLS available");
3389 }
3391 if (wantStartTls)
3392 {
3393 delete elem;
3394 char *fmt =
3395 "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n";
3396 if (!write(fmt))
3397 return false;
3398 recbuf = readStanza();
3399 status("RECV: '%s'\n", recbuf.c_str());
3400 elem = parser.parse(recbuf);
3401 if (elem->getTagAttribute("proceed", "xmlns").size()<1)
3402 {
3403 error("Server rejected TLS negotiation");
3404 disconnect();
3405 return false;
3406 }
3407 delete elem;
3408 if (!sock->startTls())
3409 {
3410 error("Could not start TLS");
3411 disconnect();
3412 return false;
3413 }
3415 fmt =
3416 "<stream:stream xmlns='jabber:client' "
3417 "xmlns:stream='http://etherx.jabber.org/streams' "
3418 "to='%s' version='1.0'>\n\n";
3419 if (!write(fmt, realm.c_str()))
3420 return false;
3422 recbuf = readStanza();
3423 status("RECVx: '%s'", recbuf.c_str());
3424 recbuf.append("</stream:stream>");
3425 elem = parser.parse(recbuf);
3426 bool success =
3427 (elem->getTagAttribute("stream:stream", "id").size()>0);
3428 if (!success)
3429 {
3430 error("STARTTLS negotiation failed");
3431 disconnect();
3432 return false;
3433 }
3434 delete elem;
3435 recbuf = readStanza();
3436 status("RECV: '%s'\n", recbuf.c_str());
3437 elem = parser.parse(recbuf);
3438 }
3440 //check for sasl authentication mechanisms
3441 std::vector<Element *> elems =
3442 elem->findElements("mechanism");
3443 if (elems.size() < 1)
3444 {
3445 error("login: no SASL mechanism offered by server");
3446 return false;
3447 }
3448 bool md5Found = false;
3449 bool plainFound = false;
3450 for (unsigned int i=0 ; i<elems.size() ; i++)
3451 {
3452 DOMString mech = elems[i]->getValue();
3453 if (mech == "DIGEST-MD5")
3454 {
3455 status("MD5 authentication offered");
3456 md5Found = true;
3457 }
3458 else if (mech == "PLAIN")
3459 {
3460 status("PLAIN authentication offered");
3461 plainFound = true;
3462 }
3463 }
3464 delete elem;
3466 bool success = false;
3467 if (md5Found)
3468 {
3469 success = saslMd5Authenticate();
3470 }
3471 else if (plainFound)
3472 {
3473 success = saslPlainAuthenticate();
3474 }
3475 else
3476 {
3477 error("not able to handle sasl authentication mechanisms");
3478 return false;
3479 }
3481 if (success)
3482 status("###### SASL authentication success\n");
3483 else
3484 error("###### SASL authentication failure\n");
3486 return success;
3487 }
3493 bool XmppClient::createSession()
3494 {
3496 Parser parser;
3497 if (port==443 || port==5223)
3498 sock->enableSSL(true);
3499 if (!sock->connect(host, port))
3500 {
3501 return false;
3502 }
3504 char *fmt =
3505 "<stream:stream "
3506 "to='%s' "
3507 "xmlns='jabber:client' "
3508 "xmlns:stream='http://etherx.jabber.org/streams' "
3509 "version='1.0'>\n\n";
3510 if (!write(fmt, realm.c_str()))
3511 return false;
3513 DOMString recbuf = readStanza();
3514 //printf("received: '%s'\n", recbuf.c_str());
3515 recbuf.append("</stream:stream>");
3516 Element *elem = parser.parse(recbuf);
3517 //elem->print();
3518 bool useSasl = false;
3519 DOMString streamId = elem->getTagAttribute("stream:stream", "id");
3520 //printf("### StreamID: %s\n", streamId.c_str());
3521 DOMString streamVersion = elem->getTagAttribute("stream:stream", "version");
3522 if (streamVersion == "1.0")
3523 useSasl = true;
3525 if (useSasl)
3526 {
3527 if (!saslAuthenticate())
3528 return false;
3529 fmt =
3530 "<stream:stream "
3531 "to='%s' "
3532 "xmlns='jabber:client' "
3533 "xmlns:stream='http://etherx.jabber.org/streams' "
3534 "version='1.0'>\n\n";
3536 if (!write(fmt, realm.c_str()))
3537 return false;
3538 recbuf = readStanza();
3539 recbuf.append("</stream:stream>\n");
3540 //printf("now server says:: '%s'\n", recbuf.c_str());
3541 elem = parser.parse(recbuf);
3542 //elem->print();
3543 delete elem;
3545 recbuf = readStanza();
3546 //printf("now server says:: '%s'\n", recbuf.c_str());
3547 elem = parser.parse(recbuf);
3548 bool hasBind = (elem->findElements("bind").size() > 0);
3549 //elem->print();
3550 delete elem;
3552 if (!hasBind)
3553 {
3554 error("no binding provided by server");
3555 return false;
3556 }
3559 }
3560 else // not SASL
3561 {
3562 if (!iqAuthenticate(streamId))
3563 return false;
3564 }
3567 //### Resource binding
3568 fmt =
3569 "<iq type='set' id='bind%d'>"
3570 "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
3571 "<resource>%s</resource>"
3572 "</bind></iq>\n";
3573 if (!write(fmt, msgId++, resource.c_str()))
3574 return false;
3576 recbuf = readStanza();
3577 status("bind result: '%s'", recbuf.c_str());
3578 elem = parser.parse(recbuf);
3579 //elem->print();
3580 DOMString bindType = elem->getTagAttribute("iq", "type");
3581 //printf("##bindType:%s\n", bindType.c_str());
3582 delete elem;
3584 if (bindType != "result")
3585 {
3586 error("no binding with server failed");
3587 return false;
3588 }
3590 fmt =
3591 "<iq type='set' id='sess%d'>"
3592 "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
3593 "</iq>\n";
3594 if (!write(fmt, msgId++))
3595 return false;
3597 recbuf = readStanza();
3598 status("session received: '%s'", recbuf.c_str());
3599 elem = parser.parse(recbuf);
3600 //elem->print();
3601 DOMString sessionType = elem->getTagAttribute("iq", "type");
3602 //printf("##sessionType:%s\n", sessionType.c_str());
3603 delete elem;
3605 if (sessionType != "result")
3606 {
3607 error("no session provided by server");
3608 return false;
3609 }
3611 //printf("########## COOL #########\n");
3612 //Now that we are bound, we have a valid JID
3613 jid = username;
3614 jid.append("@");
3615 jid.append(realm);
3616 jid.append("/");
3617 jid.append(resource);
3619 //We are now done with the synchronous handshaking. Let's go into
3620 //async mode
3622 fmt =
3623 "<presence/>\n";
3624 if (!write(fmt))
3625 return false;
3627 fmt =
3628 "<iq type='get' id='roster%d'><query xmlns='jabber:iq:roster'/></iq>\n";
3629 if (!write(fmt, msgId++))
3630 return false;
3632 fmt =
3633 "<iq type='get' id='discoItems%d' to='%s'>"
3634 "<query xmlns='http://jabber.org/protocol/disco#items'/></iq>\n";
3635 if (!write(fmt, msgId++, realm.c_str()))
3636 return false;
3638 fmt =
3639 "<iq type='get' id='discoInfo%d' to='conference.%s'>"
3640 "<query xmlns='http://jabber.org/protocol/disco#info'/></iq>\n";
3641 if (!write(fmt, msgId++, realm.c_str()))
3642 return false;
3644 /*
3645 recbuf = readStanza();
3646 status("stream received: '%s'", recbuf.c_str());
3647 elem = parser.parse(recbuf);
3648 //elem->print();
3649 delete elem;
3650 */
3652 //We are now logged in
3653 status("Connected");
3654 connected = true;
3655 XmppEvent evt(XmppEvent::EVENT_CONNECTED);
3656 dispatchXmppEvent(evt);
3657 //Thread::sleep(1000000);
3659 sock->setReceiveTimeout(1000);
3660 ReceiverThread runner(*this);
3661 Thread thread(runner);
3662 thread.start();
3664 return true;
3665 }
3667 bool XmppClient::connect()
3668 {
3669 if (!createSession())
3670 {
3671 disconnect();
3672 return false;
3673 }
3674 return true;
3675 }
3678 bool XmppClient::connect(DOMString hostArg, int portArg,
3679 DOMString usernameArg,
3680 DOMString passwordArg,
3681 DOMString resourceArg)
3682 {
3683 host = hostArg;
3684 port = portArg;
3685 password = passwordArg;
3686 resource = resourceArg;
3688 //parse this one
3689 setUsername(usernameArg);
3691 bool ret = connect();
3692 return ret;
3693 }
3695 bool XmppClient::disconnect()
3696 {
3697 if (connected)
3698 {
3699 char *fmt =
3700 "<presence from='%s' type='unavailable'/>\n";
3701 write(fmt, jid.c_str());
3702 }
3703 keepGoing = false;
3704 connected = false;
3705 Thread::sleep(3000); //allow receiving thread to quit
3706 sock->disconnect();
3707 roster.clear();
3708 groupChatsClear();
3709 XmppEvent event(XmppEvent::EVENT_DISCONNECTED);
3710 dispatchXmppEvent(event);
3711 return true;
3712 }
3714 //#######################
3715 //# ROSTER
3716 //#######################
3718 bool XmppClient::rosterAdd(const DOMString &rosterGroup,
3719 const DOMString &otherJid,
3720 const DOMString &name)
3721 {
3722 if (!checkConnect())
3723 return false;
3724 char *fmt =
3725 "<iq from='%s' type='set' id='roster_%d'>"
3726 "<query xmlns='jabber:iq:roster'>"
3727 "<item jid='%s' name='%s'><group>%s</group></item>"
3728 "</query></iq>\n";
3729 if (!write(fmt, jid.c_str(), msgId++, otherJid.c_str(),
3730 name.c_str(), rosterGroup.c_str()))
3731 {
3732 return false;
3733 }
3734 return true;
3735 }
3737 bool XmppClient::rosterDelete(const DOMString &otherJid)
3738 {
3739 if (!checkConnect())
3740 return false;
3741 char *fmt =
3742 "<iq from='%s' type='set' id='roster_%d'>"
3743 "<query xmlns='jabber:iq:roster'>"
3744 "<item jid='%s' subscription='remove'><group>%s</group></item>"
3745 "</query></iq>\n";
3746 if (!write(fmt, jid.c_str(), msgId++, otherJid.c_str()))
3747 {
3748 return false;
3749 }
3750 return true;
3751 }
3754 static bool xmppRosterCompare(const XmppUser& p1, const XmppUser& p2)
3755 {
3756 DOMString s1 = p1.group;
3757 DOMString s2 = p2.group;
3758 for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
3759 {
3760 int comp = tolower(s1[len]) - tolower(s2[len]);
3761 if (comp)
3762 return (comp<0);
3763 }
3765 s1 = p1.jid;
3766 s2 = p2.jid;
3767 for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
3768 {
3769 int comp = tolower(s1[len]) - tolower(s2[len]);
3770 if (comp)
3771 return (comp<0);
3772 }
3773 return false;
3774 }
3776 std::vector<XmppUser> XmppClient::getRoster()
3777 {
3778 std::vector<XmppUser> ros = roster;
3779 std::sort(ros.begin(), ros.end(), xmppRosterCompare);
3780 return ros;
3781 }
3783 //#######################
3784 //# CHAT (individual)
3785 //#######################
3787 bool XmppClient::message(const DOMString &user, const DOMString &subj,
3788 const DOMString &msg)
3789 {
3790 if (!checkConnect())
3791 return false;
3793 DOMString xmlSubj = toXml(subj);
3794 DOMString xmlMsg = toXml(msg);
3796 if (xmlSubj.size() > 0)
3797 {
3798 char *fmt =
3799 "<message from='%s' to='%s' type='chat'>"
3800 "<subject>%s</subject><body>%s</body></message>\n";
3801 if (!write(fmt, jid.c_str(), user.c_str(),
3802 xmlSubj.c_str(), xmlMsg.c_str()))
3803 return false;
3804 }
3805 else
3806 {
3807 char *fmt =
3808 "<message from='%s' to='%s'>"
3809 "<body>%s</body></message>\n";
3810 if (!write(fmt, jid.c_str(), user.c_str(), xmlMsg.c_str()))
3811 return false;
3812 }
3813 return true;
3814 }
3818 bool XmppClient::message(const DOMString &user, const DOMString &msg)
3819 {
3820 return message(user, "", msg);
3821 }
3825 bool XmppClient::presence(const DOMString &presence)
3826 {
3827 if (!checkConnect())
3828 return false;
3830 DOMString xmlPres = toXml(presence);
3832 char *fmt =
3833 "<presence from='%s'><show>%s</show></presence>\n";
3834 if (!write(fmt, jid.c_str(), xmlPres.c_str()))
3835 return false;
3836 return true;
3837 }
3839 //#######################
3840 //# GROUP CHAT
3841 //#######################
3843 bool XmppClient::groupChatCreate(const DOMString &groupJid)
3844 {
3845 std::vector<XmppGroupChat *>::iterator iter;
3846 for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
3847 {
3848 if ((*iter)->getGroupJid() == groupJid)
3849 {
3850 error("Group chat '%s' already exists", groupJid.c_str());
3851 return false;
3852 }
3853 }
3854 XmppGroupChat *chat = new XmppGroupChat(groupJid);
3855 groupChats.push_back(chat);
3856 return true;
3857 }
3859 /**
3860 *
3861 */
3862 void XmppClient::groupChatDelete(const DOMString &groupJid)
3863 {
3864 std::vector<XmppGroupChat *>::iterator iter;
3865 for (iter=groupChats.begin() ; iter!=groupChats.end() ; )
3866 {
3867 XmppGroupChat *chat = *iter;
3868 if (chat->getGroupJid() == groupJid)
3869 {
3870 iter = groupChats.erase(iter);
3871 delete chat;
3872 }
3873 else
3874 iter++;
3875 }
3876 }
3878 /**
3879 *
3880 */
3881 bool XmppClient::groupChatExists(const DOMString &groupJid)
3882 {
3883 std::vector<XmppGroupChat *>::iterator iter;
3884 for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
3885 if ((*iter)->getGroupJid() == groupJid)
3886 return true;
3887 return false;
3888 }
3890 /**
3891 *
3892 */
3893 void XmppClient::groupChatsClear()
3894 {
3895 std::vector<XmppGroupChat *>::iterator iter;
3896 for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
3897 delete (*iter);
3898 groupChats.clear();
3899 }
3902 /**
3903 *
3904 */
3905 void XmppClient::groupChatUserAdd(const DOMString &groupJid,
3906 const DOMString &nick)
3907 {
3908 std::vector<XmppGroupChat *>::iterator iter;
3909 for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
3910 {
3911 if ((*iter)->getGroupJid() == groupJid)
3912 {
3913 (*iter)->userAdd("", nick);
3914 }
3915 }
3916 }
3918 /**
3919 *
3920 */
3921 void XmppClient::groupChatUserDelete(const DOMString &groupJid,
3922 const DOMString &nick)
3923 {
3924 std::vector<XmppGroupChat *>::iterator iter;
3925 for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
3926 {
3927 if ((*iter)->getGroupJid() == groupJid)
3928 {
3929 (*iter)->userDelete("", nick);
3930 }
3931 }
3932 }
3934 static bool xmppUserCompare(const XmppUser& p1, const XmppUser& p2)
3935 {
3936 DOMString s1 = p1.nick;
3937 DOMString s2 = p2.nick;
3938 int comp = 0;
3939 for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
3940 {
3941 comp = tolower(s1[len]) - tolower(s2[len]);
3942 if (comp)
3943 break;
3944 }
3945 return (comp<0);
3946 }
3949 std::vector<XmppUser> XmppClient::groupChatGetUserList(
3950 const DOMString &groupJid)
3951 {
3952 if (!checkConnect())
3953 {
3954 std::vector<XmppUser> dummy;
3955 return dummy;
3956 }
3958 std::vector<XmppGroupChat *>::iterator iter;
3959 for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
3960 {
3961 if ((*iter)->getGroupJid() == groupJid )
3962 {
3963 std::vector<XmppUser> uList = (*iter)->getUserList();
3964 std::sort(uList.begin(), uList.end(), xmppUserCompare);
3965 return uList;
3966 }
3967 }
3968 std::vector<XmppUser> dummy;
3969 return dummy;
3970 }
3972 bool XmppClient::groupChatJoin(const DOMString &groupJid,
3973 const DOMString &nick,
3974 const DOMString &pass)
3975 {
3976 if (!checkConnect())
3977 return false;
3979 DOMString user = nick;
3980 if (user.size()<1)
3981 user = username;
3983 char *fmt =
3984 "<presence to='%s/%s'>"
3985 "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
3986 if (!write(fmt, groupJid.c_str(), user.c_str()))
3987 return false;
3988 return true;
3989 }
3992 bool XmppClient::groupChatLeave(const DOMString &groupJid,
3993 const DOMString &nick)
3994 {
3995 if (!checkConnect())
3996 return false;
3998 DOMString user = nick;
3999 if (user.size()<1)
4000 user = username;
4002 char *fmt =
4003 "<presence to='%s/%s' type='unavailable'>"
4004 "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
4005 if (!write(fmt, groupJid.c_str(), user.c_str()))
4006 return false;
4007 return true;
4008 }
4011 bool XmppClient::groupChatMessage(const DOMString &groupJid,
4012 const DOMString &msg)
4013 {
4014 if (!checkConnect())
4015 {
4016 return false;
4017 }
4019 DOMString xmlMsg = toXml(msg);
4021 char *fmt =
4022 "<message from='%s' to='%s' type='groupchat'>"
4023 "<body>%s</body></message>\n";
4024 if (!write(fmt, jid.c_str(), groupJid.c_str(), xmlMsg.c_str()))
4025 return false;
4026 return true;
4027 }
4029 bool XmppClient::groupChatPrivateMessage(const DOMString &groupJid,
4030 const DOMString &toNick,
4031 const DOMString &msg)
4032 {
4033 if (!checkConnect())
4034 return false;
4036 DOMString xmlMsg = toXml(msg);
4038 char *fmt =
4039 "<message from='%s' to='%s/%s' type='chat'>"
4040 "<body>%s</body></message>\n";
4041 if (!write(fmt, jid.c_str(), groupJid.c_str(),
4042 toNick.c_str(), xmlMsg.c_str()))
4043 return false;
4044 return true;
4045 }
4047 bool XmppClient::groupChatPresence(const DOMString &groupJid,
4048 const DOMString &myNick,
4049 const DOMString &presence)
4050 {
4051 if (!checkConnect())
4052 return false;
4054 DOMString user = myNick;
4055 if (user.size()<1)
4056 user = username;
4058 DOMString xmlPresence = toXml(presence);
4060 char *fmt =
4061 "<presence from='%s' to='%s/%s' type='unavailable'>"
4062 "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
4063 if (!write(fmt, jid.c_str(), groupJid.c_str(), user.c_str(), xmlPresence.c_str()))
4064 return true;
4065 return true;
4066 }
4070 //#######################
4071 //# S T R E A M S
4072 //#######################
4075 /**
4076 *
4077 */
4078 int XmppClient::outputStreamOpen(const DOMString &destId,
4079 const DOMString &streamIdArg)
4080 {
4081 int i;
4082 for (i=0; i<outputStreamCount ; i++)
4083 if (outputStreams[i]->getState() == STREAM_AVAILABLE)
4084 break;
4085 if (i>=outputStreamCount)
4086 {
4087 error("No available output streams");
4088 return -1;
4089 }
4090 int streamNr = i;
4091 XmppStream *outs = outputStreams[streamNr];
4093 outs->setState(STREAM_OPENING);
4095 char buf[32];
4096 snprintf(buf, 31, "inband%d", getMsgId());
4097 DOMString iqId = buf;
4099 DOMString streamId = streamIdArg;
4100 if (streamId.size()<1)
4101 {
4102 snprintf(buf, 31, "stream%d", getMsgId());
4103 DOMString streamId = buf;
4104 }
4105 outs->setIqId(iqId);
4106 outs->setStreamId(streamId);
4107 outs->setPeerId(destId);
4109 char *fmt =
4110 "<iq type='set' from='%s' to='%s' id='%s'>"
4111 "<open sid='%s' block-size='4096'"
4112 " xmlns='http://jabber.org/protocol/ibb'/></iq>\n";
4113 if (!write(fmt, jid.c_str(),
4114 destId.c_str(), iqId.c_str(),
4115 streamId.c_str()))
4116 {
4117 outs->reset();
4118 return -1;
4119 }
4121 int state = outs->getState();
4122 for (int tim=0 ; tim<20 ; tim++)
4123 {
4124 if (state == STREAM_OPEN)
4125 break;
4126 else if (state == STREAM_ERROR)
4127 {
4128 printf("ERROR\n");
4129 outs->reset();
4130 return -1;
4131 }
4132 Thread::sleep(1000);
4133 state = outs->getState();
4134 }
4135 if (state != STREAM_OPEN)
4136 {
4137 printf("TIMEOUT ERROR\n");
4138 outs->reset();
4139 return -1;
4140 }
4142 return streamNr;
4143 }
4145 /**
4146 *
4147 */
4148 int XmppClient::outputStreamWrite(int streamNr,
4149 const unsigned char *buf, unsigned long len)
4150 {
4151 XmppStream *outs = outputStreams[streamNr];
4153 unsigned long outLen = 0;
4154 unsigned char *p = (unsigned char *)buf;
4156 while (outLen < len)
4157 {
4158 unsigned long chunksize = 1024;
4159 if (chunksize + outLen > len)
4160 chunksize = len - outLen;
4162 Base64Encoder encoder;
4163 encoder.append(p, chunksize);
4164 DOMString b64data = encoder.finish();
4165 p += chunksize;
4166 outLen += chunksize;
4168 char *fmt =
4169 "<message from='%s' to='%s' id='msg%d'>"
4170 "<data xmlns='http://jabber.org/protocol/ibb' sid='%s' seq='%d'>"
4171 "%s"
4172 "</data>"
4173 "<amp xmlns='http://jabber.org/protocol/amp'>"
4174 "<rule condition='deliver-at' value='stored' action='error'/>"
4175 "<rule condition='match-resource' value='exact' action='error'/>"
4176 "</amp>"
4177 "</message>\n";
4178 if (!write(fmt, jid.c_str(),
4179 outs->getPeerId().c_str(),
4180 getMsgId(),
4181 outs->getStreamId().c_str(),
4182 outs->getSeqNr(),
4183 b64data.c_str()))
4184 {
4185 outs->reset();
4186 return -1;
4187 }
4188 pause(5000);
4189 }
4190 return outLen;
4191 }
4193 /**
4194 *
4195 */
4196 int XmppClient::outputStreamClose(int streamNr)
4197 {
4198 XmppStream *outs = outputStreams[streamNr];
4200 char buf[32];
4201 snprintf(buf, 31, "inband%d", getMsgId());
4202 DOMString iqId = buf;
4203 outs->setIqId(iqId);
4205 outs->setState(STREAM_CLOSING);
4206 char *fmt =
4207 "<iq type='set' from='%s' to='%s' id='%s'>"
4208 "<close sid='%s' xmlns='http://jabber.org/protocol/ibb'/></iq>\n";
4209 if (!write(fmt, jid.c_str(),
4210 outs->getPeerId().c_str(),
4211 iqId.c_str(),
4212 outs->getStreamId().c_str()))
4213 return false;
4215 int state = outs->getState();
4216 for (int tim=0 ; tim<20 ; tim++)
4217 {
4218 if (state == STREAM_CLOSED)
4219 break;
4220 else if (state == STREAM_ERROR)
4221 {
4222 printf("ERROR\n");
4223 outs->reset();
4224 return -1;
4225 }
4226 Thread::sleep(1000);
4227 state = outs->getState();
4228 }
4229 if (state != STREAM_CLOSED)
4230 {
4231 printf("TIMEOUT ERROR\n");
4232 outs->reset();
4233 return -1;
4234 }
4236 outs->reset();
4237 return 1;
4238 }
4241 /**
4242 *
4243 */
4244 int XmppClient::inputStreamOpen(const DOMString &fromJid, const DOMString &streamId,
4245 const DOMString &iqId)
4246 {
4247 int i;
4248 for (i=0 ; i<inputStreamCount ; i++)
4249 {
4250 if (inputStreams[i]->getState() == STREAM_AVAILABLE)
4251 break;
4252 }
4253 if (i>=inputStreamCount)
4254 {
4255 error("No available input streams");
4256 return -1;
4257 }
4258 int streamNr = i;
4259 XmppStream *ins = inputStreams[streamNr];
4260 ins->reset();
4261 ins->setPeerId(fromJid);
4262 ins->setState(STREAM_CLOSED);
4263 ins->setStreamId(streamId);
4265 int state = ins->getState();
4266 for (int tim=0 ; tim<20 ; tim++)
4267 {
4268 if (state == STREAM_OPENING)
4269 break;
4270 else if (state == STREAM_ERROR)
4271 {
4272 printf("ERROR\n");
4273 ins->reset();
4274 return -1;
4275 }
4276 Thread::sleep(1000);
4277 state = ins->getState();
4278 }
4279 if (state != STREAM_OPENING)
4280 {
4281 printf("TIMEOUT ERROR\n");
4282 ins->reset();
4283 return -1;
4284 }
4285 char *fmt =
4286 "<iq type='result' from='%s' to='%s' id='%s'/>\n";
4287 if (!write(fmt, jid.c_str(), fromJid.c_str(), ins->getIqId().c_str()))
4288 {
4289 return -1;
4290 }
4292 ins->setState(STREAM_OPEN);
4293 return streamNr;
4294 }
4296 /**
4297 *
4298 */
4299 int XmppClient::inputStreamAvailable(int streamNr)
4300 {
4301 XmppStream *ins = inputStreams[streamNr];
4302 return ins->available();
4303 }
4305 /**
4306 *
4307 */
4308 std::vector<unsigned char> XmppClient::inputStreamRead(int streamNr)
4309 {
4310 XmppStream *ins = inputStreams[streamNr];
4311 return ins->read();
4312 }
4314 /**
4315 *
4316 */
4317 bool XmppClient::inputStreamClosing(int streamNr)
4318 {
4319 XmppStream *ins = inputStreams[streamNr];
4320 if (ins->getState() == STREAM_CLOSING)
4321 return true;
4322 return false;
4323 }
4326 /**
4327 *
4328 */
4329 int XmppClient::inputStreamClose(int streamNr)
4330 {
4331 int ret=1;
4332 XmppStream *ins = inputStreams[streamNr];
4333 if (ins->getState() == STREAM_CLOSING)
4334 {
4335 char *fmt =
4336 "<iq type='result' from='%s' to='%s' id='%s'/>\n";
4337 if (!write(fmt, jid.c_str(), ins->getPeerId().c_str(),
4338 ins->getIqId().c_str()))
4339 {
4340 ret = -1;
4341 }
4342 }
4343 ins->reset();
4344 return ret;
4345 }
4350 //#######################
4351 //# FILE TRANSFERS
4352 //#######################
4355 /**
4356 *
4357 */
4358 bool XmppClient::fileSend(const DOMString &destJidArg,
4359 const DOMString &offeredNameArg,
4360 const DOMString &fileNameArg,
4361 const DOMString &descriptionArg)
4362 {
4363 DOMString destJid = destJidArg;
4364 DOMString offeredName = offeredNameArg;
4365 DOMString fileName = fileNameArg;
4366 DOMString description = descriptionArg;
4368 int i;
4369 for (i=0; i<fileSendCount ; i++)
4370 if (fileSends[i]->getState() == STREAM_AVAILABLE)
4371 break;
4372 if (i>=fileSendCount)
4373 {
4374 error("No available file send streams");
4375 return false;
4376 }
4377 int fileSendNr = i;
4378 XmppStream *outf = fileSends[fileSendNr];
4380 outf->setState(STREAM_OPENING);
4382 struct stat finfo;
4383 if (stat(fileName.c_str(), &finfo)<0)
4384 {
4385 error("Cannot stat file '%s' for sending", fileName.c_str());
4386 return false;
4387 }
4388 long fileLen = finfo.st_size;
4389 if (!fileLen > 1000000)
4390 {
4391 error("'%s' too large", fileName.c_str());
4392 return false;
4393 }
4394 if (!S_ISREG(finfo.st_mode))
4395 {
4396 error("'%s' is not a regular file", fileName.c_str());
4397 return false;
4398 }
4399 FILE *f = fopen(fileName.c_str(), "rb");
4400 if (!f)
4401 {
4402 error("cannot open '%s' for sending", fileName.c_str());
4403 return false;
4404 }
4405 unsigned char *sendBuf = (unsigned char *)malloc(fileLen+1);
4406 if (!sendBuf)
4407 {
4408 error("cannot cannot allocate send buffer for %s", fileName.c_str());
4409 return false;
4410 }
4411 for (long i=0 ; i<fileLen && !feof(f); i++)
4412 {
4413 sendBuf[i] = fgetc(f);
4414 }
4415 fclose(f);
4417 //## get the last path segment from the whole path
4418 if (offeredName.size()<1)
4419 {
4420 int slashPos = -1;
4421 for (unsigned int i=0 ; i<fileName.size() ; i++)
4422 {
4423 int ch = fileName[i];
4424 if (ch == '/' || ch == '\\')
4425 slashPos = i;
4426 }
4427 if (slashPos>=0 && slashPos<=(int)(fileName.size()-1))
4428 {
4429 offeredName = fileName.substr(slashPos+1,
4430 fileName.size()-slashPos-1);
4431 printf("offeredName:%s\n", offeredName.c_str());
4432 }
4433 }
4435 char buf[32];
4436 snprintf(buf, 31, "file%d", getMsgId());
4437 DOMString iqId = buf;
4438 outf->setIqId(iqId);
4440 snprintf(buf, 31, "stream%d", getMsgId());
4441 DOMString streamId = buf;
4442 //outf->setStreamId(streamId);
4444 DOMString hash = Md5::hashHex(sendBuf, fileLen);
4445 printf("Hash:%s\n", hash.c_str());
4447 outf->setPeerId(destJid);
4449 char dtgBuf[81];
4450 struct tm *timeVal = gmtime(&(finfo.st_mtime));
4451 strftime(dtgBuf, 80, "%Y-%m-%dT%H:%M:%Sz", timeVal);
4453 char *fmt =
4454 "<iq type='set' id='%s' to='%s'>"
4455 "<si xmlns='http://jabber.org/protocol/si' id='%s'"
4456 " mime-type='text/plain'"
4457 " profile='http://jabber.org/protocol/si/profile/file-transfer'>"
4458 "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'"
4459 " name='%s' size='%d' hash='%s' date='%s'><desc>%s</desc></file>"
4460 "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
4461 "<x xmlns='jabber:x:data' type='form'>"
4462 "<field var='stream-method' type='list-single'>"
4463 //"<option><value>http://jabber.org/protocol/bytestreams</value></option>"
4464 "<option><value>http://jabber.org/protocol/ibb</value></option>"
4465 "</field></x></feature></si></iq>\n";
4466 if (!write(fmt, iqId.c_str(), destJid.c_str(),
4467 streamId.c_str(), offeredName.c_str(), fileLen,
4468 hash.c_str(), dtgBuf, description.c_str()))
4469 {
4470 free(sendBuf);
4471 return false;
4472 }
4474 int state = outf->getState();
4475 for (int tim=0 ; tim<20 ; tim++)
4476 {
4477 printf("##### waiting for open\n");
4478 if (state == STREAM_OPEN)
4479 {
4480 outf->reset();
4481 break;
4482 }
4483 else if (state == STREAM_ERROR)
4484 {
4485 printf("ERROR\n");
4486 outf->reset();
4487 return false;
4488 }
4489 Thread::sleep(1000);
4490 state = outf->getState();
4491 }
4492 if (state != STREAM_OPEN)
4493 {
4494 printf("TIMEOUT ERROR\n");
4495 outf->reset();
4496 return false;
4497 }
4499 //free up this reqource
4500 outf->reset();
4502 int streamNr = outputStreamOpen(destJid, streamId);
4503 if (streamNr<0)
4504 {
4505 error("cannot open output stream %s", streamId.c_str());
4506 outf->reset();
4507 return false;
4508 }
4510 int ret = outputStreamWrite(streamNr, sendBuf, fileLen);
4512 if (ret<0)
4513 {
4514 }
4516 outputStreamClose(streamNr);
4518 free(sendBuf);
4519 return true;
4520 }
4523 class FileSendThread : public Thread
4524 {
4525 public:
4527 FileSendThread(XmppClient &par,
4528 const DOMString &destJidArg,
4529 const DOMString &offeredNameArg,
4530 const DOMString &fileNameArg,
4531 const DOMString &descriptionArg) : client(par)
4532 {
4533 destJid = destJidArg;
4534 offeredName = offeredNameArg;
4535 fileName = fileNameArg;
4536 description = descriptionArg;
4537 }
4539 virtual ~FileSendThread() {}
4541 void run()
4542 {
4543 client.fileSend(destJid, offeredName,
4544 fileName, description);
4545 }
4547 private:
4549 XmppClient &client;
4550 DOMString destJid;
4551 DOMString offeredName;
4552 DOMString fileName;
4553 DOMString description;
4554 };
4556 /**
4557 *
4558 */
4559 bool XmppClient::fileSendBackground(const DOMString &destJid,
4560 const DOMString &offeredName,
4561 const DOMString &fileName,
4562 const DOMString &description)
4563 {
4564 FileSendThread thread(*this, destJid, offeredName,
4565 fileName, description);
4566 thread.start();
4567 return true;
4568 }
4571 /**
4572 *
4573 */
4574 bool XmppClient::fileReceive(const DOMString &fromJid,
4575 const DOMString &iqId,
4576 const DOMString &streamId,
4577 const DOMString &fileName,
4578 long fileSize,
4579 const DOMString &fileHash)
4580 {
4581 char *fmt =
4582 "<iq type='result' to='%s' id='%s'>"
4583 "<si xmlns='http://jabber.org/protocol/si'>"
4584 "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'/>"
4585 "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
4586 "<x xmlns='jabber:x:data' type='submit'>"
4587 "<field var='stream-method'>"
4588 "<value>http://jabber.org/protocol/ibb</value>"
4589 "</field></x></feature></si></iq>\n";
4590 if (!write(fmt, fromJid.c_str(), iqId.c_str()))
4591 {
4592 return false;
4593 }
4595 int streamNr = inputStreamOpen(fromJid, streamId, iqId);
4596 if (streamNr < 0)
4597 {
4598 return false;
4599 }
4602 Md5 md5;
4603 FILE *f = fopen(fileName.c_str(), "wb");
4604 if (!f)
4605 {
4606 return false;
4607 }
4609 while (true)
4610 {
4611 if (inputStreamAvailable(streamNr)<1)
4612 {
4613 if (inputStreamClosing(streamNr))
4614 break;
4615 pause(100);
4616 continue;
4617 }
4618 std::vector<unsigned char> ret = inputStreamRead(streamNr);
4619 std::vector<unsigned char>::iterator iter;
4620 for (iter=ret.begin() ; iter!=ret.end() ; iter++)
4621 {
4622 unsigned char ch = *iter;
4623 md5.append(&ch, 1);
4624 fwrite(&ch, 1, 1, f);
4625 }
4626 }
4628 inputStreamClose(streamNr);
4629 fclose(f);
4631 DOMString hash = md5.finishHex();
4632 printf("received file hash:%s\n", hash.c_str());
4634 return true;
4635 }
4639 class FileReceiveThread : public Thread
4640 {
4641 public:
4643 FileReceiveThread(XmppClient &par,
4644 const DOMString &fromJidArg,
4645 const DOMString &iqIdArg,
4646 const DOMString &streamIdArg,
4647 const DOMString &fileNameArg,
4648 long fileSizeArg,
4649 const DOMString &fileHashArg) : client(par)
4650 {
4651 fromJid = fromJidArg;
4652 iqId = iqIdArg;
4653 streamId = streamIdArg;
4654 fileName = fileNameArg;
4655 fileSize = fileSizeArg;
4656 fileHash = fileHashArg;
4657 }
4659 virtual ~FileReceiveThread() {}
4661 void run()
4662 {
4663 client.fileReceive(fromJid, iqId, streamId,
4664 fileName, fileSize, fileHash);
4665 }
4667 private:
4669 XmppClient &client;
4670 DOMString fromJid;
4671 DOMString iqId;
4672 DOMString streamId;
4673 DOMString fileName;
4674 long fileSize;
4675 DOMString fileHash;
4676 };
4678 /**
4679 *
4680 */
4681 bool XmppClient::fileReceiveBackground(const DOMString &fromJid,
4682 const DOMString &iqId,
4683 const DOMString &streamId,
4684 const DOMString &fileName,
4685 long fileSize,
4686 const DOMString &fileHash)
4687 {
4688 FileReceiveThread thread(*this, fromJid, iqId, streamId,
4689 fileName, fileSize, fileHash);
4690 thread.start();
4691 return true;
4692 }
4696 //########################################################################
4697 //# X M P P G R O U P C H A T
4698 //########################################################################
4700 /**
4701 *
4702 */
4703 XmppGroupChat::XmppGroupChat(const DOMString &groupJidArg)
4704 {
4705 groupJid = groupJidArg;
4706 }
4708 /**
4709 *
4710 */
4711 XmppGroupChat::XmppGroupChat(const XmppGroupChat &other)
4712 {
4713 groupJid = other.groupJid;
4714 userList = other.userList;
4715 }
4717 /**
4718 *
4719 */
4720 XmppGroupChat::~XmppGroupChat()
4721 {
4722 }
4725 /**
4726 *
4727 */
4728 DOMString XmppGroupChat::getGroupJid()
4729 {
4730 return groupJid;
4731 }
4734 void XmppGroupChat::userAdd(const DOMString &jid, const DOMString &nick)
4735 {
4736 std::vector<XmppUser>::iterator iter;
4737 for (iter= userList.begin() ; iter!=userList.end() ; iter++)
4738 {
4739 if (iter->nick == nick)
4740 return;
4741 }
4742 XmppUser user(jid, nick);
4743 userList.push_back(user);
4744 }
4746 void XmppGroupChat::userDelete(const DOMString &jid, const DOMString &nick)
4747 {
4748 std::vector<XmppUser>::iterator iter;
4749 for (iter= userList.begin() ; iter!=userList.end() ; )
4750 {
4751 if (iter->nick == nick)
4752 iter = userList.erase(iter);
4753 else
4754 iter++;
4755 }
4756 }
4758 std::vector<XmppUser> XmppGroupChat::getUserList() const
4759 {
4760 return userList;
4761 }
4768 } //namespace Pedro
4769 //########################################################################
4770 //# E N D O F F I L E
4771 //########################################################################