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