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