summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 6784119)
raw | patch | inline | side by side (parent: 6784119)
author | ishmal <ishmal@users.sourceforge.net> | |
Mon, 15 May 2006 15:06:21 +0000 (15:06 +0000) | ||
committer | ishmal <ishmal@users.sourceforge.net> | |
Mon, 15 May 2006 15:06:21 +0000 (15:06 +0000) |
27 files changed:
diff --git a/src/pedro/Makefile.mingw b/src/pedro/Makefile.mingw
--- /dev/null
+++ b/src/pedro/Makefile.mingw
@@ -0,0 +1,183 @@
+###########################################################################
+#
+#
+# Makefile for the Pedro mini-XMPP client
+#
+# Authors:
+# Bob Jamison
+#
+# Copyright (C) 2005-2006 Bob Jamison
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+#
+##########################################################################
+# FILE SEPARATORS
+# $(S) will be set to one of these
+##########################################################################
+BSLASH := \\#
+FSLASH := /
+
+##########################################################################
+# SENSE ARCHITECTURE
+##########################################################################
+ifdef ComSpec
+ARCH=win32
+else
+ARCH=xlib
+endif
+
+##########################################################################
+# WIN32 SETTINGS
+##########################################################################
+ifeq ($(ARCH),win32)
+
+####### Where is your GTK directory?
+GTK=c:/gtk28
+
+####### Same thing, DOS style
+GTKDOS=c:\gtk28
+
+#SSL=openssl-0.9.8a
+SSL=$(GTK)
+
+CFLAGS = -g -Wall -DHAVE_SSL
+INC = -I. -I$(GTK)/include -I$(SSL)/include
+LIBS = -mwindows -L$(GTK)/lib -L$(SSL) -lssl -lcrypto -lgdi32 -lws2_32
+RM = del
+CP = copy
+S = $(BSLASH)
+
+
+all: test.exe pedro.exe
+
+GTKINC = -DGLIBMM_DLL \
+-I$(GTK)/include/glibmm-2.4 -I$(GTK)/lib/glibmm-2.4/include \
+-I$(GTK)/include/gtkmm-2.4 -I$(GTK)/lib/gtkmm-2.4/include \
+-I$(GTK)/include/gdkmm-2.4 -I$(GTK)/lib/gdkmm-2.4/include \
+-I$(GTK)/include/pangomm-1.4 \
+-I$(GTK)/include/atkmm-1.6 -I$(GTK)/include/cairo \
+-I$(GTK)/include/sigc++-2.0 -I$(GTK)/lib/sigc++-2.0/include \
+-I$(GTK)/include/gtk-2.0 -I$(GTK)/lib/gtk-2.0/include \
+-I$(GTK)/include/atk-1.0 -I$(GTK)/include/pango-1.0 \
+-I$(GTK)/include/glib-2.0 -I$(GTK)/lib/glib-2.0/include
+
+
+####### Our Gtk libs
+GTKLIBS = -L$(GTK)/lib -lloudmouth-1 \
+-lgtkmm-2.4 -lgdkmm-2.4 -lglibmm-2.4 \
+-latkmm-1.6 -lpangomm-1.4 -lsigc-2.0 \
+-lgtk-win32-2.0 -lgdk-win32-2.0 -latk-1.0 \
+-lgdk_pixbuf-2.0 -lm -lpangoft2-1.0 -lpangowin32-1.0 -lpango-1.0 \
+-lgobject-2.0 -lgmodule-2.0 -lgthread-2.0 -lglib-2.0
+
+endif
+
+
+##########################################################################
+# XLIB SETTINGS
+##########################################################################
+ifeq ($(ARCH),xlib)
+
+CFLAGS = -g -Wall -DHAVE_SSL -DHAVE_PTHREAD_H
+XINC = -I/usr/X11R6/include
+XLIB = -L/usr/X11R6/lib -lXrender -lX11
+INC = -I. -I.. $(XINC)
+LIBS = $(XLIB) -lpthread -lssl
+RM = rm -rf
+CP = cp
+S = $(FSLASH)
+all: test pedro
+
+GTKINC += `pkg-config gtkmm-2.4 --cflags`
+GTKLIBS += `pkg-config gtkmm-2.4 --libs`
+
+endif
+
+
+
+OBJ = \
+pedrodom.o \
+pedroxmpp.o
+
+GUIOBJ = \
+pedrogui.o \
+pedromain.o
+
+TESTOBJ = \
+work/test.o \
+work/filesend.o \
+work/filerec.o \
+work/groupchat.o
+
+test.exe: libpedro.a work/test.o
+ $(CXX) -o $@ work/test.o libpedro.a $(LIBS)
+test: libpedro.a work/test.o
+ $(CXX) -o $@ work/test.o libpedro.a $(LIBS)
+
+filesend.exe: libpedro.a work/filesend.o
+ $(CXX) -o $@ work/filesend.o libpedro.a $(LIBS)
+filesend: libpedro.a work/filesend.o
+ $(CXX) -o $@ work/filesend.o libpedro.a $(LIBS)
+
+filerec.exe: libpedro.a work/filerec.o
+ $(CXX) -o $@ work/filerec.o libpedro.a $(LIBS)
+filerec: libpedro.a work/filerec.o
+ $(CXX) -o $@ work/filerec.o libpedro.a $(LIBS)
+
+groupchat.exe: libpedro.a work/groupchat.o
+ $(CXX) -o $@ work/groupchat.o libpedro.a $(LIBS)
+groupchat: libpedro.a work/groupchat.o
+ $(CXX) -o $@ work/groupchat.o libpedro.a $(LIBS)
+
+pedro.exe: libpedro.a pedromain.o pedrogui.o
+ $(CXX) -o $@ pedromain.o pedrogui.o libpedro.a $(GTKLIBS) $(LIBS)
+pedro: libpedro.a pedromain.o pedrogui.o
+ $(CXX) -o $@ pedromain.o pedrogui.o libpedro.a $(GTKLIBS) $(LIBS)
+
+
+libpedro.a: $(OBJ)
+ ar crv libpedro.a $(OBJ)
+
+pedromain.o: pedromain.cpp
+ $(CXX) $(CFLAGS) $(INC) $(GTKINC) -c -o $@ $<
+
+pedrogui.o: pedrogui.cpp pedrogui.h
+ $(CXX) $(CFLAGS) $(INC) $(GTKINC) -c -o $@ $<
+
+.cpp.o:
+ $(CXX) $(CFLAGS) $(INC) -c -o $@ $<
+
+clean:
+ $(foreach a, $(OBJ), $(shell $(RM) $(subst /,$(S), $(a))))
+ $(foreach a, $(GUIOBJ), $(shell $(RM) $(subst /,$(S), $(a))))
+ $(foreach a, $(TESTOBJ), $(shell $(RM) $(subst /,$(S), $(a))))
+ -$(RM) *.a
+ -$(RM) test
+ -$(RM) test.exe
+ -$(RM) filesend
+ -$(RM) filesend.exe
+ -$(RM) filerec
+ -$(RM) filerec.exe
+ -$(RM) groupchat
+ -$(RM) groupchat.exe
+ -$(RM) pedro
+ -$(RM) pedro.exe
+ -$(RM) core.*
+
+###########################################################################
+# E N D O F F I L E
+###########################################################################
+
diff --git a/src/pedro/certs/client.pem b/src/pedro/certs/client.pem
--- /dev/null
@@ -0,0 +1,32 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,6D3B09E4CA5421FF
+
+SaDJA2MhJ12ZmDxfGkSLhQgjYPEQYqVfs5b4DZTz+9pJqzuNxHrZZU43oArbWBdB
+3DKc1THejbyHF2lY7xgPLk/5iax5r+CXesDKZroSliHyERBIOCUgDN6ecwvVGtYv
+C8IhlwGPEXyxr59lyV37RjkSUVXYBqiRbLlNIcQtp5T6GkFe+yftOnv6/UADCLTS
+Pu8xwkda1rf7dgPwYIKuk2SOTTe1VMDtWacRUGu8NteTJ4aiVaeeo9wdsKId5U2b
+Z7NTJjOjvdXOLRonfkGvDXmrmN4eICks0bV0ZBtkULAfGjKNGs6riY+XNGKNRmjI
+idRRB0za+EGorpiJ/vbe7n7uaFXIJlfqCwhTi4Up3mS8sR4tLHfmdjp85GV9P9B3
+xX3CHIeG5/EYDt0Qn1gRL5ODL/0O7nFGJslhcQUS6bMmcg9nSzhClTE2gREz0j9g
+pwzvRpEkIl3Tw4niZLIX8fW2cEIyKTBMCCG2MDwHHgXRL3SUXkOGeitFefkcXN/z
+/UWRS8XQcX7/lGWCiuEpgn+esoirjf8lFNVsx6OT0UXj3oBxGrz1iB/vpu/PMBVQ
+JsbEPSh/ElHSDUItw2ytjJmkolRtM01b7cFj16ZxbHjinXWTIGZFWUYIlaeA2zHK
+D/NRMFJwjrQYhjRgPqltvbw7M01Co7SNFBwSotARr36FBjsxbOH3F1jY6w+kXvJU
+X5m83C9UONM2K7kkKYXbE2yW+kzJF2LFX0Uu4yDluxNG767/WwqiQSI63aIzNAPp
+rSsaIMBSbVZia8q49gcvGyuvqBZpwm/PcZwr/PHJjvGs8hdU1ACmyQ==
+-----END RSA PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIICFTCCAX4CAgECMA0GCSqGSIb3DQEBBAUAMFcxCzAJBgNVBAYTAlVTMRMwEQYD
+VQQKEwpSVEZNLCBJbmMuMRkwFwYDVQQLExBXaWRnZXRzIERpdmlzaW9uMRgwFgYD
+VQQDEw9UZXN0IENBMjAwMTA1MTcwHhcNMDEwNTE3MTYxMTM2WhcNMDQwMzA2MTYx
+MTM2WjBOMQswCQYDVQQGEwJVUzETMBEGA1UEChMKUlRGTSwgSW5jLjEZMBcGA1UE
+CxMQV2lkZ2V0cyBEaXZpc2lvbjEPMA0GA1UEAxMGY2xpZW50MIGfMA0GCSqGSIb3
+DQEBAQUAA4GNADCBiQKBgQCHNWSoNh6msUwYGGd7TYQDsdSG0ao6QXaYjk+78ZyM
+QeZUBu2dZFjG4wnzkKwrD4rp/J5PLR9AdxR72lb9AavEOKL2UDHJGsscZkGVw/bz
+ZbxrKF2rvdpZSvKP1OhV1MOds/WTpRm1gcmVSoV5vLOMqVjzjHoxQ/+1zpjzMxWL
+0wIDAQABMA0GCSqGSIb3DQEBBAUAA4GBACTJhRR5tv8A7dc5+zmKR1Q/i8qE3Mrn
+mp/MOXHfX+ifJ/w+twoc/yd4En+7pr+hGsiTofct1JOZDW9Akq/ZGu1+NpVRT7Cw
+53EdMwpi7ArwZAsLIUBsKA7QmLTbdwjU5S7WlZ24eygZHyqZrK4Few+JuzlFkkoI
+FIDCfinyz24m
+-----END CERTIFICATE-----
diff --git a/src/pedro/certs/dh1024.pem b/src/pedro/certs/dh1024.pem
--- /dev/null
@@ -0,0 +1,5 @@
+-----BEGIN DH PARAMETERS-----
+MIGHAoGBANmAnfkETuKHOCWaE+W+F3kM/e7z5A8hZb7OqwGMQrUOaBEAr4BWeZBn
+G/87hhwZgNP69/KUchm714qd/PpOspCaUJ20x6PcmKujpAgca/f19HGMBjRawQMk
+R9oaBwazuQT0l0rTTKmvpMEcrQQIcVWii3CZI56I56oqF8biGPD7AgEC
+-----END DH PARAMETERS-----
diff --git a/src/pedro/certs/root.pem b/src/pedro/certs/root.pem
--- /dev/null
+++ b/src/pedro/certs/root.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICIjCCAYugAwIBAgIBADANBgkqhkiG9w0BAQQFADBXMQswCQYDVQQGEwJVUzET
+MBEGA1UEChMKUlRGTSwgSW5jLjEZMBcGA1UECxMQV2lkZ2V0cyBEaXZpc2lvbjEY
+MBYGA1UEAxMPVGVzdCBDQTIwMDEwNTE3MB4XDTAxMDUxNzE2MDExNFoXDTA2MTIy
+NTE2MDExNFowVzELMAkGA1UEBhMCVVMxEzARBgNVBAoTClJURk0sIEluYy4xGTAX
+BgNVBAsTEFdpZGdldHMgRGl2aXNpb24xGDAWBgNVBAMTD1Rlc3QgQ0EyMDAxMDUx
+NzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAmkX40warmH0+lnwD9YjsJhRz
+ZX6qXadFry0y2trZ6gMs8Mv33IKPwOu8TE7V+3PESEtjI2wr8juV9OkbIPOm+td5
+M8+6vXyIW+JBo3ch99i0QMTf5/jTgsW+3IjV8yEdiGcZFp2NWKLRvZPq2VRbuF7R
+1pvgcaRuBJ0wGOohwnsCAwEAATANBgkqhkiG9w0BAQQFAAOBgQCUB8zMKIlX5io8
+TalbzH9Qke7BcvFAL+wp/5w1ToVsWkNrINSWKv6bl/jcqOD3aPhK7qhaeOU8ZWKL
+PoPPCnRl9Wo+1JtsOO3qIgJP79Bl9ooLGahixF2v/gea5qNISjQvwYllLSa//APP
+6kXHngO0RIRbiTBYHSkAzm6hDdsvVA==
+-----END CERTIFICATE-----
diff --git a/src/pedro/certs/server.pem b/src/pedro/certs/server.pem
--- /dev/null
@@ -0,0 +1,32 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,5772A2A7BE34B611
+
+1yJ+xAn4MudcIfXXy7ElYngJ9EohIh8yvcyVLmE4kVd0xeaL/Bqhvk25BjYCK5d9
+k1K8cjgnKEBjbC++0xtJxFSbUhwoKTLwn+sBoJDcFzMKkmJXXDbSTOaNr1sVwiAR
+SnB4lhUcHguYoV5zlRJn53ft7t1mjB6RwGH+d1Zx6t95OqM1lnKqwekwmotVAWHj
+ncu3N8qhmoPMppmzEv0fOo2/pK2WohcJykSeN5zBrZCUxoO0NBNEZkFUcVjR+KsA
+1ZeI1mU60szqg+AoU/XtFcow8RtG1QZKQbbXzyfbwaG+6LqkHaWYKHQEI1546yWK
+us1HJ734uUkZoyyyazG6PiGCYV2u/aY0i3qdmyDqTvmVIvve7E4glBrtDS9h7D40
+nPShIvOatoPzIK4Y0QSvrI3G1vTsIZT3IOZto4AWuOkLNfYS2ce7prOreF0KjhV0
+3tggw9pHdDmTjHTiIkXqheZxZ7TVu+pddZW+CuB62I8lCBGPW7os1f21e3eOD/oY
+YPCI44aJvgP+zUORuZBWqaSJ0AAIuVW9S83Yzkz/tlSFHViOebyd8Cug4TlxK1VI
+q6hbSafh4C8ma7YzlvqjMzqFifcIolcbx+1A6ot0UiayJTUra4d6Uc4Rbc9RIiG0
+jfDWC6aii9YkAgRl9WqSd31yASge/HDqVXFwR48qdlYQ57rcHviqxyrwRDnfw/lX
+Mf6LPiDKEco4MKej7SR2kK2c2AgxUzpGZeAY6ePyhxbdhA0eY21nDeFd/RbwSc5s
+eTiCCMr41OB4hfBFXKDKqsM3K7klhoz6D5WsgE6u3lDoTdz76xOSTg==
+-----END RSA PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIICGDCCAYECAgEBMA0GCSqGSIb3DQEBBAUAMFcxCzAJBgNVBAYTAlVTMRMwEQYD
+VQQKEwpSVEZNLCBJbmMuMRkwFwYDVQQLExBXaWRnZXRzIERpdmlzaW9uMRgwFgYD
+VQQDEw9UZXN0IENBMjAwMTA1MTcwHhcNMDEwNTE3MTYxMDU5WhcNMDQwMzA2MTYx
+MDU5WjBRMQswCQYDVQQGEwJVUzETMBEGA1UEChMKUlRGTSwgSW5jLjEZMBcGA1UE
+CxMQV2lkZ2V0cyBEaXZpc2lvbjESMBAGA1UEAxMJbG9jYWxob3N0MIGfMA0GCSqG
+SIb3DQEBAQUAA4GNADCBiQKBgQCiWhMjNOPlPLNW4DJFBiL2fFEIkHuRor0pKw25
+J0ZYHW93lHQ4yxA6afQr99ayRjMY0D26pH41f0qjDgO4OXskBsaYOFzapSZtQMbT
+97OCZ7aHtK8z0ZGNW/cslu+1oOLomgRxJomIFgW1RyUUkQP1n0hemtUdCLOLlO7Q
+CPqZLQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAIumUwl1OoWuyN2xfoBHYAs+lRLY
+KmFLoI5+iMcGxWIsksmA+b0FLRAN43wmhPnums8eXgYbDCrKLv2xWcvKDP3mps7m
+AMivwtu/eFpYz6J8Mo1fsV4Ys08A/uPXkT23jyKo2hMu8mywkqXCXYF2e+7pEeBr
+dsbmkWK5NgoMl8eM
+-----END CERTIFICATE-----
diff --git a/src/pedro/icon/Thumbs.db b/src/pedro/icon/Thumbs.db
new file mode 100644 (file)
index 0000000..15c6329
Binary files /dev/null and b/src/pedro/icon/Thumbs.db differ
index 0000000..15c6329
Binary files /dev/null and b/src/pedro/icon/Thumbs.db differ
diff --git a/src/pedro/icon/available.png b/src/pedro/icon/available.png
new file mode 100644 (file)
index 0000000..e88ebd4
Binary files /dev/null and b/src/pedro/icon/available.png differ
index 0000000..e88ebd4
Binary files /dev/null and b/src/pedro/icon/available.png differ
diff --git a/src/pedro/icon/away.png b/src/pedro/icon/away.png
new file mode 100644 (file)
index 0000000..9bb8998
Binary files /dev/null and b/src/pedro/icon/away.png differ
index 0000000..9bb8998
Binary files /dev/null and b/src/pedro/icon/away.png differ
diff --git a/src/pedro/icon/chat.png b/src/pedro/icon/chat.png
new file mode 100644 (file)
index 0000000..84ac594
Binary files /dev/null and b/src/pedro/icon/chat.png differ
index 0000000..84ac594
Binary files /dev/null and b/src/pedro/icon/chat.png differ
diff --git a/src/pedro/icon/dnd.png b/src/pedro/icon/dnd.png
new file mode 100644 (file)
index 0000000..25c7a6e
Binary files /dev/null and b/src/pedro/icon/dnd.png differ
index 0000000..25c7a6e
Binary files /dev/null and b/src/pedro/icon/dnd.png differ
diff --git a/src/pedro/icon/error.png b/src/pedro/icon/error.png
new file mode 100644 (file)
index 0000000..35febd2
Binary files /dev/null and b/src/pedro/icon/error.png differ
index 0000000..35febd2
Binary files /dev/null and b/src/pedro/icon/error.png differ
diff --git a/src/pedro/icon/offline.png b/src/pedro/icon/offline.png
new file mode 100644 (file)
index 0000000..06284fa
Binary files /dev/null and b/src/pedro/icon/offline.png differ
index 0000000..06284fa
Binary files /dev/null and b/src/pedro/icon/offline.png differ
diff --git a/src/pedro/icon/xa.png b/src/pedro/icon/xa.png
new file mode 100644 (file)
index 0000000..0911e8a
Binary files /dev/null and b/src/pedro/icon/xa.png differ
index 0000000..0911e8a
Binary files /dev/null and b/src/pedro/icon/xa.png differ
diff --git a/src/pedro/mingwenv.bat b/src/pedro/mingwenv.bat
--- /dev/null
+++ b/src/pedro/mingwenv.bat
@@ -0,0 +1,2 @@
+set PATH=c:\mingw\bin;%PATH%\r
+set RM=del\r
diff --git a/src/pedro/pedro.bat b/src/pedro/pedro.bat
--- /dev/null
+++ b/src/pedro/pedro.bat
@@ -0,0 +1,2 @@
+set path=c:\inkscape;%path%\r
+pedro.exe\r
diff --git a/src/pedro/pedrodom.cpp b/src/pedro/pedrodom.cpp
--- /dev/null
+++ b/src/pedro/pedrodom.cpp
@@ -0,0 +1,782 @@
+/*
+ * Implementation of the Pedro mini-DOM parser and tree
+ *
+ * Authors:
+ * Bob Jamison
+ *
+ * Copyright (C) 2005 Bob Jamison
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <malloc.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+#include "pedrodom.h"
+
+namespace Pedro
+{
+
+
+
+//########################################################################
+//# E L E M E N T
+//########################################################################
+
+Element *Element::clone()
+{
+ Element *elem = new Element(name, value);
+ elem->parent = parent;
+ elem->attributes = attributes;
+ elem->namespaces = namespaces;
+
+ std::vector<Element *>::iterator iter;
+ for (iter = children.begin(); iter != children.end() ; iter++)
+ {
+ elem->addChild((*iter)->clone());
+ }
+ return elem;
+}
+
+
+void Element::findElementsRecursive(std::vector<Element *>&res, const DOMString &name)
+{
+ if (getName() == name)
+ {
+ res.push_back(this);
+ }
+ for (unsigned int i=0; i<children.size() ; i++)
+ children[i]->findElementsRecursive(res, name);
+}
+
+std::vector<Element *> Element::findElements(const DOMString &name)
+{
+ std::vector<Element *> res;
+ findElementsRecursive(res, name);
+ return res;
+}
+
+DOMString Element::getAttribute(const DOMString &name)
+{
+ for (unsigned int i=0 ; i<attributes.size() ; i++)
+ if (attributes[i].getName() ==name)
+ return attributes[i].getValue();
+ return "";
+}
+
+DOMString Element::getTagAttribute(const DOMString &tagName, const DOMString &attrName)
+{
+ std::vector<Element *>elems = findElements(tagName);
+ if (elems.size() <1)
+ return "";
+ DOMString res = elems[0]->getAttribute(attrName);
+ return res;
+}
+
+DOMString Element::getTagValue(const DOMString &tagName)
+{
+ std::vector<Element *>elems = findElements(tagName);
+ if (elems.size() <1)
+ return "";
+ DOMString res = elems[0]->getValue();
+ return res;
+}
+
+void Element::addChild(Element *child)
+{
+ if (!child)
+ return;
+ child->parent = this;
+ children.push_back(child);
+}
+
+
+void Element::addAttribute(const DOMString &name, const DOMString &value)
+{
+ Attribute attr(name, value);
+ attributes.push_back(attr);
+}
+
+void Element::addNamespace(const DOMString &prefix, const DOMString &namespaceURI)
+{
+ Namespace ns(prefix, namespaceURI);
+ namespaces.push_back(ns);
+}
+
+void Element::writeIndentedRecursive(FILE *f, int indent)
+{
+ int i;
+ if (!f)
+ return;
+ //Opening tag, and attributes
+ for (i=0;i<indent;i++)
+ fputc(' ',f);
+ fprintf(f,"<%s",name.c_str());
+ for (unsigned int i=0 ; i<attributes.size() ; i++)
+ {
+ fprintf(f," %s=\"%s\"",
+ attributes[i].getName().c_str(),
+ attributes[i].getValue().c_str());
+ }
+ for (unsigned int i=0 ; i<namespaces.size() ; i++)
+ {
+ fprintf(f," xmlns:%s=\"%s\"",
+ namespaces[i].getPrefix().c_str(),
+ namespaces[i].getNamespaceURI().c_str());
+ }
+ fprintf(f,">\n");
+
+ //Between the tags
+ if (value.size() > 0)
+ {
+ for (int i=0;i<indent;i++)
+ fputc(' ', f);
+ fprintf(f," %s\n", value.c_str());
+ }
+
+ for (unsigned int i=0 ; i<children.size() ; i++)
+ children[i]->writeIndentedRecursive(f, indent+2);
+
+ //Closing tag
+ for (int i=0; i<indent; i++)
+ fputc(' ',f);
+ fprintf(f,"</%s>\n", name.c_str());
+}
+
+void Element::writeIndented(FILE *f)
+{
+ writeIndentedRecursive(f, 0);
+}
+
+void Element::print()
+{
+ writeIndented(stdout);
+}
+
+
+//########################################################################
+//# P A R S E R
+//########################################################################
+
+
+
+typedef struct
+ {
+ char *escaped;
+ char value;
+ } EntityEntry;
+
+static EntityEntry entities[] =
+{
+ { "&" , '&' },
+ { "<" , '<' },
+ { ">" , '>' },
+ { "'", '\'' },
+ { """, '"' },
+ { NULL , '\0' }
+};
+
+
+
+void Parser::getLineAndColumn(long pos, long *lineNr, long *colNr)
+{
+ long line = 1;
+ long col = 1;
+ for (long i=0 ; i<pos ; i++)
+ {
+ XMLCh ch = parsebuf[i];
+ if (ch == '\n' || ch == '\r')
+ {
+ col = 0;
+ line ++;
+ }
+ else
+ col++;
+ }
+ *lineNr = line;
+ *colNr = col;
+
+}
+
+
+void Parser::error(char *fmt, ...)
+{
+ long lineNr;
+ long colNr;
+ getLineAndColumn(currentPosition, &lineNr, &colNr);
+ va_list args;
+ fprintf(stderr, "xml error at line %ld, column %ld:", lineNr, colNr);
+ va_start(args,fmt);
+ vfprintf(stderr,fmt,args);
+ va_end(args) ;
+ fprintf(stderr, "\n");
+}
+
+
+
+int Parser::peek(long pos)
+{
+ if (pos >= parselen)
+ return -1;
+ currentPosition = pos;
+ int ch = parsebuf[pos];
+ //printf("ch:%c\n", ch);
+ return ch;
+}
+
+
+
+DOMString Parser::encode(const DOMString &str)
+{
+ DOMString ret;
+ for (unsigned int i=0 ; i<str.size() ; i++)
+ {
+ XMLCh ch = (XMLCh)str[i];
+ if (ch == '&')
+ ret.append("&");
+ else if (ch == '<')
+ ret.append("<");
+ else if (ch == '>')
+ ret.append(">");
+ else if (ch == '\'')
+ ret.append("'");
+ else if (ch == '"')
+ ret.append(""");
+ else
+ ret.push_back(ch);
+
+ }
+ return ret;
+}
+
+
+int Parser::match(long p0, const char *text)
+{
+ int p = p0;
+ while (*text)
+ {
+ if (peek(p) != *text)
+ return p0;
+ p++; text++;
+ }
+ return p;
+}
+
+
+
+int Parser::skipwhite(long p)
+{
+
+ while (p<parselen)
+ {
+ int p2 = match(p, "<!--");
+ if (p2 > p)
+ {
+ p = p2;
+ while (p<parselen)
+ {
+ p2 = match(p, "-->");
+ if (p2 > p)
+ {
+ p = p2;
+ break;
+ }
+ p++;
+ }
+ }
+ XMLCh b = peek(p);
+ if (!isspace(b))
+ break;
+ p++;
+ }
+ return p;
+}
+
+/* modify this to allow all chars for an element or attribute name*/
+int Parser::getWord(int p0, DOMString &buf)
+{
+ int p = p0;
+ while (p<parselen)
+ {
+ XMLCh b = peek(p);
+ if (b<=' ' || b=='/' || b=='>' || b=='=')
+ break;
+ buf.push_back(b);
+ p++;
+ }
+ return p;
+}
+
+int Parser::getQuoted(int p0, DOMString &buf, int do_i_parse)
+{
+
+ int p = p0;
+ if (peek(p) != '"' && peek(p) != '\'')
+ return p0;
+ p++;
+
+ while ( p<parselen )
+ {
+ XMLCh b = peek(p);
+ if (b=='"' || b=='\'')
+ break;
+ if (b=='&' && do_i_parse)
+ {
+ bool found = false;
+ for (EntityEntry *ee = entities ; ee->value ; ee++)
+ {
+ int p2 = match(p, ee->escaped);
+ if (p2>p)
+ {
+ buf.push_back(ee->value);
+ p = p2;
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ error("unterminated entity");
+ return false;
+ }
+ }
+ else
+ {
+ buf.push_back(b);
+ p++;
+ }
+ }
+ return p;
+}
+
+int Parser::parseVersion(int p0)
+{
+ //printf("### parseVersion: %d\n", p0);
+
+ int p = p0;
+
+ p = skipwhite(p0);
+
+ if (peek(p) != '<')
+ return p0;
+
+ p++;
+ if (p>=parselen || peek(p)!='?')
+ return p0;
+
+ p++;
+
+ DOMString buf;
+
+ while (p<parselen)
+ {
+ XMLCh ch = peek(p);
+ if (ch=='?')
+ {
+ p++;
+ break;
+ }
+ buf.push_back(ch);
+ p++;
+ }
+
+ if (peek(p) != '>')
+ return p0;
+ p++;
+
+ //printf("Got version:%s\n",buf.c_str());
+ return p;
+}
+
+int Parser::parseDoctype(int p0)
+{
+ //printf("### parseDoctype: %d\n", p0);
+
+ int p = p0;
+ p = skipwhite(p);
+
+ if (p>=parselen || peek(p)!='<')
+ return p0;
+
+ p++;
+
+ if (peek(p)!='!' || peek(p+1)=='-')
+ return p0;
+ p++;
+
+ DOMString buf;
+ while (p<parselen)
+ {
+ XMLCh ch = peek(p);
+ if (ch=='>')
+ {
+ p++;
+ break;
+ }
+ buf.push_back(ch);
+ p++;
+ }
+
+ //printf("Got doctype:%s\n",buf.c_str());
+ return p;
+}
+
+int Parser::parseElement(int p0, Element *par,int depth)
+{
+
+ int p = p0;
+
+ int p2 = p;
+
+ p = skipwhite(p);
+
+ //## Get open tag
+ XMLCh ch = peek(p);
+ if (ch!='<')
+ return p0;
+
+ p++;
+
+ DOMString openTagName;
+ p = skipwhite(p);
+ p = getWord(p, openTagName);
+ //printf("####tag :%s\n", openTagName.c_str());
+ p = skipwhite(p);
+
+ //Add element to tree
+ Element *n = new Element(openTagName);
+ n->parent = par;
+ par->addChild(n);
+
+ // Get attributes
+ if (peek(p) != '>')
+ {
+ while (p<parselen)
+ {
+ p = skipwhite(p);
+ ch = peek(p);
+ //printf("ch:%c\n",ch);
+ if (ch=='>')
+ break;
+ else if (ch=='/' && p<parselen+1)
+ {
+ p++;
+ p = skipwhite(p);
+ ch = peek(p);
+ if (ch=='>')
+ {
+ p++;
+ //printf("quick close\n");
+ return p;
+ }
+ }
+ DOMString attrName;
+ p2 = getWord(p, attrName);
+ if (p2==p)
+ break;
+ //printf("name:%s",buf);
+ p=p2;
+ p = skipwhite(p);
+ ch = peek(p);
+ //printf("ch:%c\n",ch);
+ if (ch!='=')
+ break;
+ p++;
+ p = skipwhite(p);
+ // ch = parsebuf[p];
+ // printf("ch:%c\n",ch);
+ DOMString attrVal;
+ p2 = getQuoted(p, attrVal, true);
+ p=p2+1;
+ //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str());
+ char *namestr = (char *)attrName.c_str();
+ if (strncmp(namestr, "xmlns:", 6)==0)
+ n->addNamespace(attrName, attrVal);
+ else
+ n->addAttribute(attrName, attrVal);
+ }
+ }
+
+ bool cdata = false;
+
+ p++;
+ // ### Get intervening data ### */
+ DOMString data;
+ while (p<parselen)
+ {
+ //# COMMENT
+ p2 = match(p, "<!--");
+ if (!cdata && p2>p)
+ {
+ p = p2;
+ while (p<parselen)
+ {
+ p2 = match(p, "-->");
+ if (p2 > p)
+ {
+ p = p2;
+ break;
+ }
+ p++;
+ }
+ }
+
+ ch = peek(p);
+ //# END TAG
+ if (ch=='<' && !cdata && peek(p+1)=='/')
+ {
+ break;
+ }
+ //# CDATA
+ p2 = match(p, "<![CDATA[");
+ if (p2 > p)
+ {
+ cdata = true;
+ p = p2;
+ continue;
+ }
+
+ //# CHILD ELEMENT
+ if (ch == '<')
+ {
+ p2 = parseElement(p, n, depth+1);
+ if (p2 == p)
+ {
+ /*
+ printf("problem on element:%s. p2:%d p:%d\n",
+ openTagName.c_str(), p2, p);
+ */
+ return p0;
+ }
+ p = p2;
+ continue;
+ }
+ //# ENTITY
+ if (ch=='&' && !cdata)
+ {
+ bool found = false;
+ for (EntityEntry *ee = entities ; ee->value ; ee++)
+ {
+ int p2 = match(p, ee->escaped);
+ if (p2>p)
+ {
+ data.push_back(ee->value);
+ p = p2;
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ error("unterminated entity");
+ return -1;
+ }
+ continue;
+ }
+
+ //# NONE OF THE ABOVE
+ data.push_back(ch);
+ p++;
+ }/*while*/
+
+
+ n->value = data;
+ //printf("%d : data:%s\n",p,data.c_str());
+
+ //## Get close tag
+ p = skipwhite(p);
+ ch = peek(p);
+ if (ch != '<')
+ {
+ error("no < for end tag\n");
+ return p0;
+ }
+ p++;
+ ch = peek(p);
+ if (ch != '/')
+ {
+ error("no / on end tag");
+ return p0;
+ }
+ p++;
+ ch = peek(p);
+ p = skipwhite(p);
+ DOMString closeTagName;
+ p = getWord(p, closeTagName);
+ if (openTagName != closeTagName)
+ {
+ error("Mismatched closing tag. Expected </%S>. Got '%S'.",
+ openTagName.c_str(), closeTagName.c_str());
+ return p0;
+ }
+ p = skipwhite(p);
+ if (peek(p) != '>')
+ {
+ error("no > on end tag for '%s'", closeTagName.c_str());
+ return p0;
+ }
+ p++;
+ // printf("close element:%s\n",closeTagName.c_str());
+ p = skipwhite(p);
+ return p;
+}
+
+
+
+
+Element *Parser::parse(XMLCh *buf,int pos,int len)
+{
+ parselen = len;
+ parsebuf = buf;
+ Element *rootNode = new Element("root");
+ pos = parseVersion(pos);
+ pos = parseDoctype(pos);
+ pos = parseElement(pos, rootNode, 0);
+ return rootNode;
+}
+
+
+Element *Parser::parse(const char *buf, int pos, int len)
+{
+
+ XMLCh *charbuf = (XMLCh *)malloc((len+1) * sizeof(XMLCh));
+ long i = 0;
+ while (i< len)
+ {
+ charbuf[i] = (XMLCh)buf[i];
+ i++;
+ }
+ charbuf[i] = '\0';
+ Element *n = parse(charbuf, 0, len);
+ free(charbuf);
+ return n;
+}
+
+Element *Parser::parse(const DOMString &buf)
+{
+ long len = buf.size();
+ XMLCh *charbuf = (XMLCh *)malloc((len+1) * sizeof(XMLCh));
+ long i = 0;
+ while (i< len)
+ {
+ charbuf[i] = (XMLCh)buf[i];
+ i++;
+ }
+ charbuf[i] = '\0';
+ Element *n = parse(charbuf, 0, len);
+ free(charbuf);
+ return n;
+}
+
+Element *Parser::parseFile(const char *fileName)
+{
+
+ //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
+ if (!fileName)
+ return NULL;
+
+ FILE *f = fopen(fileName, "rb");
+ if (!f)
+ return NULL;
+
+ struct stat statBuf;
+ if (fstat(fileno(f),&statBuf)<0)
+ {
+ fclose(f);
+ return NULL;
+ }
+ long filelen = statBuf.st_size;
+
+ //printf("length:%d\n",filelen);
+ XMLCh *charbuf = (XMLCh *)malloc((filelen+1) * sizeof(XMLCh));
+ for (XMLCh *p=charbuf ; !feof(f) ; p++)
+ {
+ *p = (XMLCh)fgetc(f);
+ }
+ fclose(f);
+ charbuf[filelen] = '\0';
+
+
+ /*
+ printf("nrbytes:%d\n",wc_count);
+ printf("buf:%ls\n======\n",charbuf);
+ */
+ Element *n = parse(charbuf, 0, filelen);
+ free(charbuf);
+ return n;
+}
+
+
+
+
+
+
+
+}//namespace Pedro
+
+#if 0
+//########################################################################
+//# T E S T
+//########################################################################
+
+bool doTest(char *fileName)
+{
+ Pedro::Parser parser;
+
+ Pedro::Element *elem = parser.parseFile(fileName);
+
+ if (!elem)
+ {
+ printf("Parsing failed\n");
+ return false;
+ }
+
+ elem->print();
+
+ delete elem;
+
+ return true;
+}
+
+
+
+int main(int argc, char **argv)
+{
+ if (argc != 2)
+ {
+ printf("usage: %s <xmlfile>\n", argv[0]);
+ return 1;
+ }
+
+ if (!doTest(argv[1]))
+ return 1;
+
+ return 0;
+}
+
+#endif
+
+//########################################################################
+//# E N D O F F I L E
+//########################################################################
+
+
diff --git a/src/pedro/pedrodom.h b/src/pedro/pedrodom.h
--- /dev/null
+++ b/src/pedro/pedrodom.h
@@ -0,0 +1,343 @@
+#ifndef __PEDRODOM_H__
+#define __PEDRODOM_H__
+/*
+ * API for the Pedro mini-DOM parser and tree
+ *
+ * Authors:
+ * Bob Jamison
+ *
+ * Copyright (C) 2005-2006 Bob Jamison
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string>
+#include <vector>
+
+
+namespace Pedro
+{
+
+typedef std::string DOMString;
+typedef unsigned int XMLCh;
+
+
+class Namespace
+{
+public:
+ Namespace()
+ {}
+
+ Namespace(const DOMString &prefixArg, const DOMString &namespaceURIArg)
+ {
+ prefix = prefixArg;
+ namespaceURI = namespaceURIArg;
+ }
+
+ Namespace(const Namespace &other)
+ {
+ assign(other);
+ }
+
+ Namespace &operator=(const Namespace &other)
+ {
+ assign(other);
+ return *this;
+ }
+
+ virtual ~Namespace()
+ {}
+
+ virtual DOMString getPrefix()
+ { return prefix; }
+
+ virtual DOMString getNamespaceURI()
+ { return namespaceURI; }
+
+protected:
+
+ void assign(const Namespace &other)
+ {
+ prefix = other.prefix;
+ namespaceURI = other.namespaceURI;
+ }
+
+ DOMString prefix;
+ DOMString namespaceURI;
+
+};
+
+class Attribute
+{
+public:
+ Attribute()
+ {}
+
+ Attribute(const DOMString &nameArg, const DOMString &valueArg)
+ {
+ name = nameArg;
+ value = valueArg;
+ }
+
+ Attribute(const Attribute &other)
+ {
+ assign(other);
+ }
+
+ Attribute &operator=(const Attribute &other)
+ {
+ assign(other);
+ return *this;
+ }
+
+ virtual ~Attribute()
+ {}
+
+ virtual DOMString getName()
+ { return name; }
+
+ virtual DOMString getValue()
+ { return value; }
+
+protected:
+
+ void assign(const Attribute &other)
+ {
+ name = other.name;
+ value = other.value;
+ }
+
+ DOMString name;
+ DOMString value;
+
+};
+
+
+class Element
+{
+friend class Parser;
+
+public:
+ Element()
+ {
+ parent = NULL;
+ }
+
+ Element(const DOMString &nameArg)
+ {
+ parent = NULL;
+ name = nameArg;
+ }
+
+ Element(const DOMString &nameArg, const DOMString &valueArg)
+ {
+ parent = NULL;
+ name = nameArg;
+ value = valueArg;
+ }
+
+ Element(const Element &other)
+ {
+ assign(other);
+ }
+
+ Element &operator=(const Element &other)
+ {
+ assign(other);
+ return *this;
+ }
+
+ virtual Element *clone();
+
+ virtual ~Element()
+ {
+ for (unsigned int i=0 ; i<children.size() ; i++)
+ delete children[i];
+ }
+
+ virtual DOMString getName()
+ { return name; }
+
+ virtual DOMString getValue()
+ { return value; }
+
+ Element *getParent()
+ { return parent; }
+
+ std::vector<Element *> getChildren()
+ { return children; }
+
+ std::vector<Element *> findElements(const DOMString &name);
+
+ DOMString getAttribute(const DOMString &name);
+
+ DOMString getTagAttribute(const DOMString &tagName, const DOMString &attrName);
+
+ DOMString getTagValue(const DOMString &tagName);
+
+ void addChild(Element *child);
+
+ void addAttribute(const DOMString &name, const DOMString &value);
+
+ void addNamespace(const DOMString &prefix, const DOMString &namespaceURI);
+
+
+ /**
+ * Prettyprint an XML tree to an output stream. Elements are indented
+ * according to element hierarchy.
+ * @param f a stream to receive the output
+ * @param elem the element to output
+ */
+ void writeIndented(FILE *f);
+
+ /**
+ * Prettyprint an XML tree to standard output. This is the equivalent of
+ * writeIndented(stdout).
+ * @param elem the element to output
+ */
+ void print();
+
+protected:
+
+ void assign(const Element &other)
+ {
+ parent = other.parent;
+ children = other.children;
+ attributes = other.attributes;
+ namespaces = other.namespaces;
+ name = other.name;
+ value = other.value;
+ }
+
+ void findElementsRecursive(std::vector<Element *>&res, const DOMString &name);
+
+ void writeIndentedRecursive(FILE *f, int indent);
+
+ Element *parent;
+
+ std::vector<Element *>children;
+
+ std::vector<Attribute> attributes;
+ std::vector<Namespace> namespaces;
+
+ DOMString name;
+ DOMString value;
+
+};
+
+
+
+
+
+class Parser
+{
+public:
+ /**
+ * Constructor
+ */
+ Parser()
+ { init(); }
+
+ virtual ~Parser()
+ {}
+
+ /**
+ * Parse XML in a char buffer.
+ * @param buf a character buffer to parse
+ * @param pos position to start parsing
+ * @param len number of chars, from pos, to parse.
+ * @return a pointer to the root of the XML document;
+ */
+ Element *parse(const char *buf,int pos,int len);
+
+ /**
+ * Parse XML in a char buffer.
+ * @param buf a character buffer to parse
+ * @param pos position to start parsing
+ * @param len number of chars, from pos, to parse.
+ * @return a pointer to the root of the XML document;
+ */
+ Element *parse(const DOMString &buf);
+
+ /**
+ * Parse a named XML file. The file is loaded like a data file;
+ * the original format is not preserved.
+ * @param fileName the name of the file to read
+ * @return a pointer to the root of the XML document;
+ */
+ Element *parseFile(const char *fileName);
+
+ /**
+ * Utility method to preprocess a string for XML
+ * output, escaping its entities.
+ * @param str the string to encode
+ */
+ static DOMString encode(const DOMString &str);
+
+
+private:
+
+ void init()
+ {
+ keepGoing = true;
+ currentNode = NULL;
+ parselen = 0;
+ parsebuf = NULL;
+ currentPosition = 0;
+ }
+
+ void getLineAndColumn(long pos, long *lineNr, long *colNr);
+
+ void error(char *fmt, ...);
+
+ int peek(long pos);
+
+ int match(long pos, const char *text);
+
+ int skipwhite(long p);
+
+ int getWord(int p0, DOMString &buf);
+
+ int getQuoted(int p0, DOMString &buf, int do_i_parse);
+
+ int parseVersion(int p0);
+
+ int parseDoctype(int p0);
+
+ int parseElement(int p0, Element *par,int depth);
+
+ Element *parse(XMLCh *buf,int pos,int len);
+
+ bool keepGoing;
+ Element *currentNode;
+ long parselen;
+ XMLCh *parsebuf;
+ DOMString cdatabuf;
+ long currentPosition;
+ int colNr;
+
+};
+
+
+
+}//namespace Pedro
+
+
+#endif /* __PEDRODOM_H__ */
+
+//########################################################################
+//# E N D O F F I L E
+//########################################################################
+
diff --git a/src/pedro/pedrogui.cpp b/src/pedro/pedrogui.cpp
--- /dev/null
+++ b/src/pedro/pedrogui.cpp
@@ -0,0 +1,2175 @@
+/*
+ * Simple demo GUI for the Pedro mini-XMPP client.
+ *
+ * Authors:
+ * Bob Jamison
+ *
+ * Copyright (C) 2005 Bob Jamison
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "pedrogui.h"
+
+#include <stdarg.h>
+
+namespace Pedro
+{
+
+
+
+//#########################################################################
+//# I C O N S
+//#########################################################################
+
+static const guint8 icon_available[] =
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (672) */
+ "\0\0\2\270"
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (48) */
+ "\0\0\0""0"
+ /* width (12) */
+ "\0\0\0\14"
+ /* height (14) */
+ "\0\0\0\16"
+ /* pixel_data: */
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0""333\377333"
+ "\377333\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0""333\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377333\377\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0""333\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\37733"
+ "3\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0""333\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377333\377\377\377\377\0\377\377\377\0\377\377"
+ "\377\0""333\377\377\377\0\377\377\377\0\377\0\0\0\377\377\377\0\377\0"
+ "\0\0\377\377\377\0\377\377\377\0\377333\377\377\377\377\0\377\377\377"
+ "\0\377\377\377\0""333\377\377\377\0\377\377\377\0\377\0\0\0\377\377\377"
+ "\0\377\0\0\0\377\377\377\0\377\377\377\0\377333\377\377\377\377\0\377"
+ "\377\377\0\377\377\377\0""333\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\0\0\0\377\377\377\0\377\377\377\0\377\377\377\0\377333\377\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0""333\377\377\377"
+ "\0\377\377\377\0\377\0\0\0\377\377\377\0\377\377\377\0\377333\377\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0""3"
+ "33\377\377\377\0\377\377\377\0\377\0\0\0\377\377\377\0\377\377\377\0"
+ "\377333\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0""333\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377333\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\377\377\377\0LLL\377\0\0\0\377\0\0\0\377"
+ "\0\0\0\377LLL\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0LLL\377\0\0\0\377\0\0\0\377"
+ "\0\0\0\377LLL\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\0\0\0\377"
+ "\0\0\0\377\0\0\0\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0"};
+
+
+static const guint8 icon_away[] =
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (672) */
+ "\0\0\2\270"
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (48) */
+ "\0\0\0""0"
+ /* width (12) */
+ "\0\0\0\14"
+ /* height (14) */
+ "\0\0\0\16"
+ /* pixel_data: */
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0""333\377333\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0""333\377\377\377\0\377\377\377\0\377333\377\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0""333\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377333\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\0\0\377\377\0\0\377\377\0\0\377\377\0\0\377\377\0"
+ "\0\377\377\0\0\377\377\0\0\377\377\0\0\377\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\0\0\377\377\377\377\377\377\377\377\377\377\377"
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
+ "\377\377\377\377\377\377\0\0\377\377\377\377\0\377\0\0\377\377\377\377"
+ "\377\0\0\0\377\377\377\377\377\377\377\377\377\0\0\0\377\377\377\377"
+ "\377\0\0\0\377\0\0\0\377\377\377\377\377\377\377\377\377\377\0\0\377"
+ "\377\0\0\377\377\377\377\377\0\0\0\377\0\0\0\377\377\377\377\377\0\0"
+ "\0\377\0\0\0\377\377\377\377\377\377\377\377\377\0\0\0\377\377\377\377"
+ "\377\377\0\0\377\377\0\0\377\377\377\377\377\0\0\0\377\377\377\377\377"
+ "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\377\377"
+ "\377\377\377\0\0\377\377\0\0\377\377\377\377\377\0\0\0\377\377\377\377"
+ "\377\377\377\377\377\0\0\0\377\0\0\0\377\377\377\377\377\377\377\377"
+ "\377\0\0\0\377\377\377\377\377\377\0\0\377\377\377\377\0\377\0\0\377"
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0"
+ "\377\377\377\377\0\377\377\377\0\377\377\377\0\377\0\0\377\377\0\0\377"
+ "\377\0\0\377\377\0\0\377\377\0\0\377\377\0\0\377\377\0\0\377\377\0\0"
+ "\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0LLL\377333\377\0\0\0\377\0\0\0\377LLL\377\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0""333\377333\377333\377\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0"};
+
+
+static const guint8 icon_chat[] =
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (672) */
+ "\0\0\2\270"
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (48) */
+ "\0\0\0""0"
+ /* width (12) */
+ "\0\0\0\14"
+ /* height (14) */
+ "\0\0\0\16"
+ /* pixel_data: */
+ "\377\377\377\0\377\377\377\0\377\377\377\0""333\377333\377333\377\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0""333\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377333\377\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\377\377\377\0""333\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377333\377\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0""333\377\377\377\0\377\377"
+ "\377\0\377fff\377\377\377\0\377fff\377\377\377\0\377\377\377\0\37733"
+ "3\377\377\377\377\0\377\377\377\0\377\377\377\0""333\377\377\377\0\377"
+ "fff\377\377\377\0\377fff\377\377\377\0\377fff\377\377\377\0\377333\377"
+ "\377\377\377\0\377\377\377\0\377\377\377\0""333\377\377\377\0\377fff"
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\0\0\0\377\0\0\0\377\0"
+ "\0\0\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0""33"
+ "3\377\377\377\0\377fff\377\377\377\0\377\0\0\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\0\0\0\377\377\377\377\0\377\377\377\0\377\377"
+ "\377\0""333\377\377\377\0\377fff\377\0\0\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\0\0\0\377\377\377\377"
+ "\0\377\377\377\0\377\377\377\0""333\377\0\0\0\377\377\377\0\377\377\377"
+ "\0\377\0\0\0\377\377\377\0\377\0\0\0\377\377\377\0\377\377\377\0\377"
+ "\0\0\0\377\377\377\377\0\377\377\377\0""333\377\0\0\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\0\0\0\377\377\377\377\0\377\377\377\0LLL\377\0\0\0\377"
+ "\377\377\0\377\377\377\0\377\0\0\0\377\377\377\0\377\0\0\0\377\377\377"
+ "\0\377\377\377\0\377\0\0\0\377\377\377\377\0\377\377\377\0LLL\377333"
+ "\377\0\0\0\377\377\377\0\377\377\377\0\377\0\0\0\377\377\377\0\377\377"
+ "\377\0\377\0\0\0\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0""333\377333\377\0\0\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\0\0\0\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\0\0\0\377"
+ "\0\0\0\377\0\0\0\377\377\377\377\0\377\377\377\0\377\377\377\0"};
+
+
+static const guint8 icon_dnd[] =
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (672) */
+ "\0\0\2\270"
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (48) */
+ "\0\0\0""0"
+ /* width (12) */
+ "\0\0\0\14"
+ /* height (14) */
+ "\0\0\0\16"
+ /* pixel_data: */
+ "\377\377\377\0\377\377\377\0\377\377\377\0""333\377333\377333\377\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0""333\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377333\377\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\377\377\377\0""333\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377333\377\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0""333\377\377\377\0\377\377"
+ "\377\0\377fff\377\377\377\0\377fff\377\377\377\0\377\377\377\0\37733"
+ "3\377\377\377\377\0\377\377\377\0\377\377\377\0""333\377\377\377\0\377"
+ "fff\377\377\377\0\377fff\377\377\377\0\377fff\377\377\377\0\377333\377"
+ "\377\377\377\0\377\377\377\0\377\377\377\0""333\377\377\377\0\377fff"
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\177\0\0\377\177\0\0\377"
+ "\177\0\0\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
+ "333\377\377\377\0\377fff\377\377\377\0\377\177\0\0\377\377\0\0\377\377"
+ "\0\0\377\377\0\0\377\177\0\0\377\377\377\377\0\377\377\377\0\377\377"
+ "\377\0""333\377\377\377\0\377fff\377\177\0\0\377\377\377\377\377fff\377"
+ "\377\0\0\377fff\377\377\377\377\377\177\0\0\377\377\377\377\0\377\377"
+ "\377\0\377\377\377\0""333\377\177\0\0\377\377\0\0\377fff\377\377\377"
+ "\377\377fff\377\377\377\377\377fff\377\377\0\0\377\177\0\0\377\377\377"
+ "\377\0\377\377\377\0""333\377\177\0\0\377\377\0\0\377\377\0\0\377fff"
+ "\377\377\377\377\377fff\377\377\0\0\377\377\0\0\377\177\0\0\377\377\377"
+ "\377\0\377\377\377\0LLL\377\177\0\0\377\377\0\0\377fff\377\377\377\377"
+ "\377fff\377\377\377\377\377fff\377\377\0\0\377\177\0\0\377\377\377\377"
+ "\0\377\377\377\0LLL\377333\377\177\0\0\377\377\377\377\377fff\377\377"
+ "\0\0\377fff\377\377\377\377\377\177\0\0\377\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0""333\377333\377\177\0\0\377\377\0\0\377"
+ "\377\0\0\377\377\0\0\377\177\0\0\377\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\177\0\0\377\177\0\0\377\177\0\0\377\377\377\377\0\377\377"
+ "\377\0\377\377\377\0"};
+
+
+static const guint8 icon_error[] =
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (672) */
+ "\0\0\2\270"
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (48) */
+ "\0\0\0""0"
+ /* width (12) */
+ "\0\0\0\14"
+ /* height (14) */
+ "\0\0\0\16"
+ /* pixel_data: */
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0""333\377333\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0""333\377"
+ "\377\377\377\0\0\0\0\377\350\350\350\377333\377\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0""333\377"
+ "\350\350\350\377fff\377\0\0\0\377\350\350\350\377\350\350\350\377333"
+ "\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0""333\377"
+ "\350\350\350\377\350\350\350\377\350\350\350\377\0\0\0\377\350\350\350"
+ "\377\350\350\350\377\350\350\350\377333\377\377\377\377\0\377\377\377"
+ "\0\377\377\377\0""333\377\350\350\350\377\350\350\350\377\0\0\0\377\0"
+ "\0\0\377fff\377\350\350\350\377\350\350\350\377333\377\377\377\377\0"
+ "\377\377\377\0\377\377\377\0""333\377\350\350\350\377\350\350\350\377"
+ "\0\0\0\377\350\350\350\377\0\0\0\377\0\0\0\377\0\0\0\377333\377\377\377"
+ "\377\0\377\377\377\0\377\377\377\0""333\377\350\350\350\377\0\0\0\377"
+ "\350\350\350\377\0\0\0\377\350\350\350\377\350\350\350\377\350\350\350"
+ "\377333\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0""3"
+ "33\377\350\350\350\377\0\0\0\377\0\0\0\377\0\0\0\377\350\350\350\377"
+ "fff\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0""333\377\0\0\0\377\350\350\350\377\350\350\350\377\350\350\350"
+ "\377\0\0\0\377333\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0""333\377\350\350\350\377\350\350\350"
+ "\377\350\350\350\377333\377\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0LLL\377\0\0\0"
+ "\377\0\0\0\377\0\0\0\377LLL\377\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0LLL\377\0\0"
+ "\0\377\0\0\0\377\0\0\0\377LLL\377\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\0\0\0\377\0\0\0\377\0\0\0\377\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"};
+
+
+static const guint8 icon_offline[] =
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (672) */
+ "\0\0\2\270"
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (48) */
+ "\0\0\0""0"
+ /* width (12) */
+ "\0\0\0\14"
+ /* height (14) */
+ "\0\0\0\16"
+ /* pixel_data: */
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0""333\377333"
+ "\377333\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0""333\377\377\377"
+ "\377\377\377\377\377\377\377\377\377\377333\377\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0""333\377"
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
+ "\377\377\377333\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0""333\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377333\377\377\377"
+ "\377\0\377\377\377\0\377\377\377\0""333\377\377\377\377\377\377\377\377"
+ "\377\0\0\0\377\377\377\377\377\0\0\0\377\377\377\377\377\377\377\377"
+ "\377333\377\377\377\377\0\377\377\377\0\377\377\377\0""333\377\377\377"
+ "\377\377\377\377\377\377\0\0\0\377\377\377\377\377\0\0\0\377\377\377"
+ "\377\377\377\377\377\377333\377\377\377\377\0\377\377\377\0\377\377\377"
+ "\0""333\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\377"
+ "\377\377\377\377\377\377\377\377\377\377\377\377333\377\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\377\377\377\0""333\377\377\377\377\377\377"
+ "\377\377\377\0\0\0\377\377\377\377\377\377\377\377\377333\377\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0""333\377"
+ "\377\377\377\377\377\377\377\377\0\0\0\377\377\377\377\377\377\377\377"
+ "\377333\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0""333\377\377\377\377\377\377\377\377\377\377"
+ "\377\377\377333\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0LLL\377\0\0\0\377\0\0"
+ "\0\377\0\0\0\377LLL\377\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0LLL\377\0\0\0\377"
+ "\0\0\0\377\0\0\0\377LLL\377\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\0\0\0\377\0\0\0\377\0\0\0\377\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"};
+
+
+static const guint8 icon_xa[] =
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (672) */
+ "\0\0\2\270"
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (48) */
+ "\0\0\0""0"
+ /* width (12) */
+ "\0\0\0\14"
+ /* height (14) */
+ "\0\0\0\16"
+ /* pixel_data: */
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\0\0\377333\377333\377"
+ "333\377\377\0\0\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\0\0\377333\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377333\377\377\0\0\377\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\0\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\0\0\377"
+ "\177\0\0\377\177\0\0\377\377\377\377\0\377\377\377\0""333\377\377\0\0"
+ "\377\377\377\0\377fff\377\377\377\0\377\177\0\0\377\177\0\0\377\377\0"
+ "\0\377\377\377\377\377\177\0\0\377\377\377\377\0\377\377\377\0""333\377"
+ "\377\0\0\377\177\0\0\377\177\0\0\377\177\0\0\377\377\377\377\377\377"
+ "\377\377\377\377\377\377\377\0\0\0\377\0\0\0\377\177\0\0\377\177\0\0"
+ "\377\177\0\0\377\377\0\0\377\377\377\377\377\377\377\377\377\377\377"
+ "\377\377\262\262\262\377\377\377\377\377\0\0\0\377\0\0\0\377\377\377"
+ "\377\377\177\0\0\377\177\0\0\377\377\377\377\377\262\262\262\377\0\0"
+ "\0\377\262\262\262\377\0\0\0\377\377\377\377\377\0\0\0\377\377\377\377"
+ "\377\262\262\262\377\0\0\0\377\177\0\0\377\177\0\0\377\377\377\377\377"
+ "\0\0\0\377\377\377\377\377\0\0\0\377\0\0\0\377\377\377\377\377\0\0\0"
+ "\377\0\0\0\377\377\377\377\377\0\0\0\377\177\0\0\377\177\0\0\377\377"
+ "\377\377\377\0\0\0\377\377\377\377\377\0\0\0\377\377\377\377\377\0\0"
+ "\0\377\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\177"
+ "\0\0\377\377\377\377\0\177\0\0\377\262\262\262\377\0\0\0\377\262\262"
+ "\262\377\377\377\377\377\377\377\377\377\377\377\377\377\177\0\0\377"
+ "\177\0\0\377\177\0\0\377\377\377\377\0\377\377\377\0\177\0\0\377\377"
+ "\377\377\377\377\377\377\377\177\0\0\377\177\0\0\377\177\0\0\377\177"
+ "\0\0\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\177\0\0\377\177\0\0\377\177\0\0\377333\377\0\0\0\377\0\0\0"
+ "\377LLL\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0""333\377333\377"
+ "333\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0"};
+
+
+//#########################################################################
+//# R O S T E R
+//#########################################################################
+
+
+void Roster::doubleClickCallback(const Gtk::TreeModel::Path &path,
+ Gtk::TreeViewColumn *col)
+{
+ Glib::RefPtr<Gtk::TreeModel> model = rosterView.get_model();
+ Glib::RefPtr<Gtk::TreeSelection> sel = rosterView.get_selection();
+ Gtk::TreeModel::iterator iter = sel->get_selected();
+ DOMString nick = iter->get_value(rosterColumns.userColumn);
+ //printf("Double clicked:%s\n", nick.c_str());
+ if (parent)
+ parent->doChat(nick);
+
+}
+
+void Roster::chatCallback()
+{
+ Glib::RefPtr<Gtk::TreeModel> model = rosterView.get_model();
+ Glib::RefPtr<Gtk::TreeSelection> sel = rosterView.get_selection();
+ Gtk::TreeModel::iterator iter = sel->get_selected();
+ DOMString nick = iter->get_value(rosterColumns.userColumn);
+ //printf("Chat with:%s\n", nick.c_str());
+ if (parent)
+ parent->doChat(nick);
+}
+
+void Roster::sendFileCallback()
+{
+ Glib::RefPtr<Gtk::TreeModel> model = rosterView.get_model();
+ Glib::RefPtr<Gtk::TreeSelection> sel = rosterView.get_selection();
+ Gtk::TreeModel::iterator iter = sel->get_selected();
+ DOMString nick = iter->get_value(rosterColumns.userColumn);
+ //printf("Send file to:%s\n", nick.c_str());
+ if (parent)
+ parent->doSendFile(nick);
+}
+
+bool Roster::buttonPressCallback(GdkEventButton* event)
+{
+ if( (event->type == GDK_BUTTON_PRESS) && (event->button == 3) )
+ {
+ Gtk::Widget *wid = uiManager->get_widget("/PopupMenu");
+ Gtk::Menu *popupMenu = dynamic_cast<Gtk::Menu*>(wid);
+ popupMenu->popup(event->button, event->time);
+ return true;
+ }
+ else
+ return false;
+}
+
+bool Roster::doSetup()
+{
+ set_size_request(200,200);
+
+ pixbuf_available = Gdk::Pixbuf::create_from_inline(
+ sizeof(icon_available), icon_available, false);
+ pixbuf_away = Gdk::Pixbuf::create_from_inline(
+ sizeof(icon_away), icon_away, false);
+ pixbuf_chat = Gdk::Pixbuf::create_from_inline(
+ sizeof(icon_chat), icon_chat, false);
+ pixbuf_dnd = Gdk::Pixbuf::create_from_inline(
+ sizeof(icon_dnd), icon_dnd, false);
+ pixbuf_error = Gdk::Pixbuf::create_from_inline(
+ sizeof(icon_error), icon_error, false);
+ pixbuf_offline = Gdk::Pixbuf::create_from_inline(
+ sizeof(icon_offline), icon_offline, false);
+ pixbuf_xa = Gdk::Pixbuf::create_from_inline(
+ sizeof(icon_xa), icon_xa, false);
+
+ rosterView.setParent(this);
+ treeStore = Gtk::TreeStore::create(rosterColumns);
+ rosterView.set_model(treeStore);
+
+ Gtk::CellRendererText *rend0 = new Gtk::CellRendererText();
+ //rend0->property_background() = "gray";
+ //rend0->property_foreground() = "black";
+ rosterView.append_column("Group", *rend0);
+ Gtk::TreeViewColumn *col0 = rosterView.get_column(0);
+ col0->add_attribute(*rend0, "text", 0);
+
+ Gtk::CellRendererPixbuf *rend1 = new Gtk::CellRendererPixbuf();
+ rosterView.append_column("Status", *rend1);
+ Gtk::TreeViewColumn *col1 = rosterView.get_column(1);
+ col1->add_attribute(*rend1, "pixbuf", 1);
+
+ Gtk::CellRendererText *rend2 = new Gtk::CellRendererText();
+ rosterView.append_column("Item", *rend2);
+ Gtk::TreeViewColumn *col2 = rosterView.get_column(2);
+ col2->add_attribute(*rend2, "text", 2);
+
+ Gtk::CellRendererText *rend3 = new Gtk::CellRendererText();
+ rosterView.append_column("Name", *rend3);
+ Gtk::TreeViewColumn *col3 = rosterView.get_column(3);
+ col3->add_attribute(*rend3, "text", 3);
+
+ Gtk::CellRendererText *rend4 = new Gtk::CellRendererText();
+ rosterView.append_column("Subscription", *rend4);
+ Gtk::TreeViewColumn *col4 = rosterView.get_column(4);
+ col4->add_attribute(*rend4, "text", 4);
+
+ rosterView.signal_row_activated().connect(
+ sigc::mem_fun(*this, &Roster::doubleClickCallback) );
+
+ add(rosterView);
+ set_policy(Gtk::POLICY_ALWAYS, Gtk::POLICY_ALWAYS);
+
+ //##### POPUP MENU
+ Glib::RefPtr<Gtk::ActionGroup> actionGroup = Gtk::ActionGroup::create();
+
+ actionGroup->add( Gtk::Action::create("UserMenu", "_User Menu") );
+
+ actionGroup->add( Gtk::Action::create("Chat",
+ Gtk::Stock::CONNECT, "Chat"),
+ sigc::mem_fun(*this, &Roster::chatCallback) );
+ actionGroup->add( Gtk::Action::create("SendFile",
+ Gtk::Stock::CONNECT, "Send file"),
+ sigc::mem_fun(*this, &Roster::sendFileCallback) );
+
+
+ uiManager = Gtk::UIManager::create();
+
+ uiManager->insert_action_group(actionGroup, 0);
+
+ Glib::ustring ui_info =
+ "<ui>"
+ " <popup name='PopupMenu'>"
+ " <menuitem action='Chat'/>"
+ " <menuitem action='SendFile'/>"
+ " </popup>"
+ "</ui>";
+
+ uiManager->add_ui_from_string(ui_info);
+
+
+ show_all_children();
+
+ return true;
+}
+
+
+/**
+ * Clear the roster
+ */
+void Roster::clear()
+{
+ treeStore->clear();
+}
+
+/**
+ * Regenerate the roster
+ */
+void Roster::refresh()
+{
+ if (!parent)
+ return;
+ treeStore->clear();
+ std::vector<XmppUser> items = parent->client.getRoster();
+
+ //## Add in tree fashion
+ DOMString lastGroup = "";
+ Gtk::TreeModel::Row row = *(treeStore->append());
+ row[rosterColumns.groupColumn] = "";
+ for (unsigned int i=0 ; i<items.size() ; i++)
+ {
+ XmppUser user = items[i];
+ if (user.group != lastGroup)
+ {
+ if (lastGroup.size()>0)
+ row = *(treeStore->append());
+ row[rosterColumns.groupColumn] = user.group;
+ lastGroup = user.group;
+ }
+ Glib::RefPtr<Gdk::Pixbuf> pb = pixbuf_offline;
+ if (user.show == "available")
+ pb = pixbuf_available;
+ else if (user.show == "away")
+ pb = pixbuf_away;
+ else if (user.show == "chat")
+ pb = pixbuf_chat;
+ else if (user.show == "dnd")
+ pb = pixbuf_dnd;
+ else if (user.show == "xa")
+ pb = pixbuf_xa;
+ else
+ {
+ //printf("Unknown show for %s:'%s'\n", user.c_str(), show.c_str());
+ }
+ Gtk::TreeModel::Row childRow = *(treeStore->append(row.children()));
+ childRow[rosterColumns.statusColumn] = pb;
+ childRow[rosterColumns.userColumn] = user.jid;
+ childRow[rosterColumns.nameColumn] = user.nick;
+ childRow[rosterColumns.subColumn] = user.subscription;
+ }
+ rosterView.expand_all();
+}
+
+//#########################################################################
+//# M E S S A G E L I S T
+//#########################################################################
+
+bool MessageList::doSetup()
+{
+ set_size_request(400,200);
+
+ messageListBuffer = Gtk::TextBuffer::create();
+ messageList.set_buffer(messageListBuffer);
+ messageList.set_editable(false);
+ messageList.set_wrap_mode(Gtk::WRAP_WORD_CHAR);
+
+ Glib::RefPtr<Gtk::TextBuffer::TagTable> table =
+ messageListBuffer->get_tag_table();
+ Glib::RefPtr<Gtk::TextBuffer::Tag> color0 =
+ Gtk::TextBuffer::Tag::create("color0");
+ color0->property_foreground() = "DarkGreen";
+ color0->property_weight() = Pango::WEIGHT_BOLD;
+ table->add(color0);
+ Glib::RefPtr<Gtk::TextBuffer::Tag> color1 =
+ Gtk::TextBuffer::Tag::create("color1");
+ color1->property_foreground() = "chocolate4";
+ color1->property_weight() = Pango::WEIGHT_BOLD;
+ table->add(color1);
+ Glib::RefPtr<Gtk::TextBuffer::Tag> color2 =
+ Gtk::TextBuffer::Tag::create("color2");
+ color2->property_foreground() = "red4";
+ color2->property_weight() = Pango::WEIGHT_BOLD;
+ table->add(color2);
+ Glib::RefPtr<Gtk::TextBuffer::Tag> color3 =
+ Gtk::TextBuffer::Tag::create("color3");
+ color3->property_foreground() = "MidnightBlue";
+ color3->property_weight() = Pango::WEIGHT_BOLD;
+ table->add(color3);
+ Glib::RefPtr<Gtk::TextBuffer::Tag> color4 =
+ Gtk::TextBuffer::Tag::create("color4");
+ color4->property_foreground() = "turquoise4";
+ color4->property_weight() = Pango::WEIGHT_BOLD;
+ table->add(color4);
+ Glib::RefPtr<Gtk::TextBuffer::Tag> color5 =
+ Gtk::TextBuffer::Tag::create("color5");
+ color5->property_foreground() = "OliveDrab";
+ color5->property_weight() = Pango::WEIGHT_BOLD;
+ table->add(color5);
+ Glib::RefPtr<Gtk::TextBuffer::Tag> color6 =
+ Gtk::TextBuffer::Tag::create("color6");
+ color6->property_foreground() = "purple4";
+ color6->property_weight() = Pango::WEIGHT_BOLD;
+ table->add(color6);
+ Glib::RefPtr<Gtk::TextBuffer::Tag> color7 =
+ Gtk::TextBuffer::Tag::create("color7");
+ color7->property_foreground() = "VioletRed4";
+ color7->property_weight() = Pango::WEIGHT_BOLD;
+ table->add(color7);
+
+ add(messageList);
+ set_policy(Gtk::POLICY_ALWAYS, Gtk::POLICY_ALWAYS);
+
+
+ show_all_children();
+
+ return true;
+}
+
+/**
+ * Clear all messages from the list
+ */
+void MessageList::clear()
+{
+ messageListBuffer->erase(messageListBuffer->begin(),
+ messageListBuffer->end());
+}
+
+
+/**
+ * Post a message to the list
+ */
+void MessageList::postMessage(const DOMString &from, const DOMString &msg)
+{
+ DOMString out = "<";
+ out.append(from);
+ out.append("> ");
+
+ int val = 0;
+ for (unsigned int i=0 ; i<from.size() ; i++)
+ val += from[i];
+ val = val % 8;
+
+ char buf[16];
+ sprintf(buf, "color%d", val);
+ DOMString tagName = buf;
+
+ messageListBuffer->insert_with_tag(
+ messageListBuffer->end(), out, tagName);
+ messageListBuffer->insert(messageListBuffer->end(), msg);
+ messageListBuffer->insert(messageListBuffer->end(), "\n");
+ //Gtk::Adjustment *adj = get_vadjustment();
+ //adj->set_value(adj->get_upper()-adj->get_page_size());
+ Glib::RefPtr<Gtk::TextBuffer::Mark> mark =
+ messageListBuffer->create_mark("temp", messageListBuffer->end());
+ messageList.scroll_mark_onscreen(mark);
+ messageListBuffer->delete_mark(mark);
+}
+
+
+
+//#########################################################################
+//# U S E R L I S T
+//#########################################################################
+void UserList::doubleClickCallback(const Gtk::TreeModel::Path &path,
+ Gtk::TreeViewColumn *col)
+{
+ Glib::RefPtr<Gtk::TreeModel> model = userList.get_model();
+ Glib::RefPtr<Gtk::TreeSelection> sel = userList.get_selection();
+ Gtk::TreeModel::iterator iter = sel->get_selected();
+ DOMString nick = iter->get_value(userListColumns.userColumn);
+ //printf("Double clicked:%s\n", nick.c_str());
+ if (parent)
+ parent->doChat(nick);
+
+}
+
+void UserList::chatCallback()
+{
+ Glib::RefPtr<Gtk::TreeModel> model = userList.get_model();
+ Glib::RefPtr<Gtk::TreeSelection> sel = userList.get_selection();
+ Gtk::TreeModel::iterator iter = sel->get_selected();
+ DOMString nick = iter->get_value(userListColumns.userColumn);
+ //printf("Chat with:%s\n", nick.c_str());
+ if (parent)
+ parent->doChat(nick);
+}
+
+void UserList::sendFileCallback()
+{
+ Glib::RefPtr<Gtk::TreeModel> model = userList.get_model();
+ Glib::RefPtr<Gtk::TreeSelection> sel = userList.get_selection();
+ Gtk::TreeModel::iterator iter = sel->get_selected();
+ DOMString nick = iter->get_value(userListColumns.userColumn);
+ //printf("Send file to:%s\n", nick.c_str());
+ if (parent)
+ parent->doSendFile(nick);
+}
+
+bool UserList::buttonPressCallback(GdkEventButton* event)
+{
+ if( (event->type == GDK_BUTTON_PRESS) && (event->button == 3) )
+ {
+ Gtk::Widget *wid = uiManager->get_widget("/PopupMenu");
+ Gtk::Menu *popupMenu = dynamic_cast<Gtk::Menu*>(wid);
+ popupMenu->popup(event->button, event->time);
+ return true;
+ }
+ else
+ return false;
+}
+
+bool UserList::doSetup()
+{
+ set_size_request(200,200);
+
+ setParent(NULL);
+
+ pixbuf_available = Gdk::Pixbuf::create_from_inline(
+ sizeof(icon_available), icon_available, false);
+ pixbuf_away = Gdk::Pixbuf::create_from_inline(
+ sizeof(icon_away), icon_away, false);
+ pixbuf_chat = Gdk::Pixbuf::create_from_inline(
+ sizeof(icon_chat), icon_chat, false);
+ pixbuf_dnd = Gdk::Pixbuf::create_from_inline(
+ sizeof(icon_dnd), icon_dnd, false);
+ pixbuf_error = Gdk::Pixbuf::create_from_inline(
+ sizeof(icon_error), icon_error, false);
+ pixbuf_offline = Gdk::Pixbuf::create_from_inline(
+ sizeof(icon_offline), icon_offline, false);
+ pixbuf_xa = Gdk::Pixbuf::create_from_inline(
+ sizeof(icon_xa), icon_xa, false);
+
+ userList.setParent(this);
+ userListStore = Gtk::ListStore::create(userListColumns);
+ userList.set_model(userListStore);
+
+ Gtk::CellRendererPixbuf *rend0 = new Gtk::CellRendererPixbuf();
+ userList.append_column("Status", *rend0);
+ Gtk::TreeViewColumn *col0 = userList.get_column(0);
+ col0->add_attribute(*rend0, "pixbuf", 0);
+
+ Gtk::CellRendererText *rend1 = new Gtk::CellRendererText();
+ //rend1->property_background() = "gray";
+ //rend1->property_foreground() = "black";
+ userList.append_column("User", *rend1);
+ Gtk::TreeViewColumn *col1 = userList.get_column(1);
+ col1->add_attribute(*rend1, "text", 1);
+
+ userList.set_headers_visible(false);
+
+ userList.signal_row_activated().connect(
+ sigc::mem_fun(*this, &UserList::doubleClickCallback) );
+
+ add(userList);
+ set_policy(Gtk::POLICY_ALWAYS, Gtk::POLICY_ALWAYS);
+
+ //##### POPUP MENU
+ Glib::RefPtr<Gtk::ActionGroup> actionGroup = Gtk::ActionGroup::create();
+
+ actionGroup->add( Gtk::Action::create("UserMenu", "_User Menu") );
+
+ actionGroup->add( Gtk::Action::create("Chat",
+ Gtk::Stock::CONNECT, "Chat"),
+ sigc::mem_fun(*this, &UserList::chatCallback) );
+ actionGroup->add( Gtk::Action::create("SendFile",
+ Gtk::Stock::CONNECT, "Send file"),
+ sigc::mem_fun(*this, &UserList::sendFileCallback) );
+
+
+ uiManager = Gtk::UIManager::create();
+
+ uiManager->insert_action_group(actionGroup, 0);
+
+ Glib::ustring ui_info =
+ "<ui>"
+ " <popup name='PopupMenu'>"
+ " <menuitem action='Chat'/>"
+ " <menuitem action='SendFile'/>"
+ " </popup>"
+ "</ui>";
+
+ uiManager->add_ui_from_string(ui_info);
+
+ show_all_children();
+
+ return true;
+}
+
+/**
+ * Clear all messages from the list
+ */
+void UserList::clear()
+{
+ userListStore->clear();
+}
+
+
+/**
+ * Add a user to the list
+ */
+void UserList::addUser(const DOMString &user, const DOMString &show)
+{
+ Glib::RefPtr<Gdk::Pixbuf> pb = pixbuf_offline;
+ if (show == "available")
+ pb = pixbuf_available;
+ else if (show == "away")
+ pb = pixbuf_away;
+ else if (show == "chat")
+ pb = pixbuf_chat;
+ else if (show == "dnd")
+ pb = pixbuf_dnd;
+ else if (show == "xa")
+ pb = pixbuf_xa;
+ else
+ {
+ //printf("Unknown show for %s:'%s'\n", user.c_str(), show.c_str());
+ }
+ Gtk::TreeModel::Row row = *(userListStore->append());
+ row[userListColumns.userColumn] = user;
+ row[userListColumns.statusColumn] = pb;
+}
+
+
+
+
+//#########################################################################
+//# C H A T W I N D O W
+//#########################################################################
+ChatWindow::ChatWindow(PedroGui &par, const DOMString jidArg)
+ : parent(par)
+{
+ jid = jidArg;
+ doSetup();
+}
+
+ChatWindow::~ChatWindow()
+{
+}
+
+void ChatWindow::leaveCallback()
+{
+ hide();
+ parent.chatDelete(jid);
+}
+
+
+void ChatWindow::hideCallback()
+{
+ hide();
+ parent.chatDelete(jid);
+}
+
+
+void ChatWindow::textEnterCallback()
+{
+ DOMString str = inputTxt.get_text();
+ if (str.size() > 0)
+ parent.client.message(jid, str);
+ inputTxt.set_text("");
+ messageList.postMessage(parent.client.getJid(), str);
+}
+
+bool ChatWindow::doSetup()
+{
+ DOMString title = "Private Chat - ";
+ title.append(jid);
+ set_title(title);
+
+ set_size_request(500,300);
+
+ signal_hide().connect(
+ sigc::mem_fun(*this, &ChatWindow::hideCallback) );
+
+ Glib::RefPtr<Gtk::ActionGroup> actionGroup = Gtk::ActionGroup::create();
+ actionGroup->add( Gtk::Action::create("MenuFile", "_File") );
+ actionGroup->add( Gtk::Action::create("Leave", Gtk::Stock::CANCEL),
+ sigc::mem_fun(*this, &ChatWindow::leaveCallback) );
+
+
+ Glib::RefPtr<Gtk::UIManager> uiManager = Gtk::UIManager::create();
+
+ uiManager->insert_action_group(actionGroup, 0);
+ add_accel_group(uiManager->get_accel_group());
+
+ Glib::ustring ui_info =
+ "<ui>"
+ " <menubar name='MenuBar'>"
+ " <menu action='MenuFile'>"
+ " <menuitem action='Leave'/>"
+ " </menu>"
+ " </menubar>"
+ "</ui>";
+
+ add(vbox);
+
+ uiManager->add_ui_from_string(ui_info);
+ Gtk::Widget* pMenuBar = uiManager->get_widget("/MenuBar");
+ vbox.pack_start(*pMenuBar, Gtk::PACK_SHRINK);
+
+ vbox.pack_end(vPaned);
+
+ vPaned.add1(messageList);
+ vPaned.add2(inputTxt);
+
+ inputTxt.signal_activate().connect(
+ sigc::mem_fun(*this, &ChatWindow::textEnterCallback) );
+
+ show_all_children();
+
+ return true;
+}
+
+bool ChatWindow::postMessage(const DOMString &data)
+{
+ messageList.postMessage(jid, data);
+ return true;
+}
+
+//#########################################################################
+//# G R O U P C H A T W I N D O W
+//#########################################################################
+
+GroupChatWindow::GroupChatWindow(PedroGui &par,
+ const DOMString &groupJidArg,
+ const DOMString &nickArg)
+ : parent(par)
+{
+ groupJid = groupJidArg;
+ nick = nickArg;
+ doSetup();
+}
+
+GroupChatWindow::~GroupChatWindow()
+{
+}
+
+
+void GroupChatWindow::leaveCallback()
+{
+ parent.client.groupChatLeave(groupJid, nick);
+ hide();
+ parent.groupChatDelete(groupJid, nick);
+}
+
+void GroupChatWindow::hideCallback()
+{
+ parent.client.groupChatLeave(groupJid, nick);
+ hide();
+ parent.groupChatDelete(groupJid, nick);
+}
+
+void GroupChatWindow::textEnterCallback()
+{
+ DOMString str = inputTxt.get_text();
+ if (str.size() > 0)
+ parent.client.groupChatMessage(groupJid, str);
+ inputTxt.set_text("");
+}
+
+bool GroupChatWindow::doSetup()
+{
+ DOMString title = "Group Chat - ";
+ title.append(groupJid);
+ set_title(title);
+
+ userList.setParent(this);
+
+ set_size_request(500,300);
+
+ signal_hide().connect(
+ sigc::mem_fun(*this, &GroupChatWindow::hideCallback) );
+
+ Glib::RefPtr<Gtk::ActionGroup> actionGroup = Gtk::ActionGroup::create();
+ actionGroup->add( Gtk::Action::create("MenuFile", "_File") );
+ actionGroup->add( Gtk::Action::create("Leave", Gtk::Stock::CANCEL),
+ sigc::mem_fun(*this, &GroupChatWindow::leaveCallback) );
+
+
+ Glib::RefPtr<Gtk::UIManager> uiManager = Gtk::UIManager::create();
+
+ uiManager->insert_action_group(actionGroup, 0);
+ add_accel_group(uiManager->get_accel_group());
+
+ Glib::ustring ui_info =
+ "<ui>"
+ " <menubar name='MenuBar'>"
+ " <menu action='MenuFile'>"
+ " <menuitem action='Leave'/>"
+ " </menu>"
+ " </menubar>"
+ "</ui>";
+
+ add(vbox);
+ uiManager->add_ui_from_string(ui_info);
+ Gtk::Widget* pMenuBar = uiManager->get_widget("/MenuBar");
+ vbox.pack_start(*pMenuBar, Gtk::PACK_SHRINK);
+
+ vbox.pack_end(vPaned);
+
+ vPaned.add1(hPaned);
+ vPaned.add2(inputTxt);
+ inputTxt.signal_activate().connect(
+ sigc::mem_fun(*this, &GroupChatWindow::textEnterCallback) );
+
+
+ hPaned.add1(messageList);
+ hPaned.add2(userList);
+
+
+ show_all_children();
+
+
+ return true;
+}
+
+bool GroupChatWindow::receiveMessage(const DOMString &from,
+ const DOMString &data)
+{
+ messageList.postMessage(from, data);
+ return true;
+}
+
+bool GroupChatWindow::receivePresence(const DOMString &fromNick,
+ bool presence,
+ const DOMString &show,
+ const DOMString &status)
+{
+
+ DOMString presStr = "";
+ presStr.append(fromNick);
+ if (!presence)
+ presStr.append(" left the group");
+ else
+ {
+ if (show.size()<1)
+ presStr.append(" joined the group");
+ else
+ {
+ presStr.append(" : ");
+ presStr.append(show);
+ }
+ }
+ messageList.postMessage("*", presStr);
+ userList.clear();
+ std::vector<XmppUser> memberList =
+ parent.client.groupChatGetUserList(groupJid);
+ for (unsigned int i=0 ; i<memberList.size() ; i++)
+ {
+ XmppUser user = memberList[i];
+ userList.addUser(user.nick, user.show);
+ }
+ return true;
+}
+
+
+void GroupChatWindow::doChat(const DOMString &nick)
+{
+ printf("##Chat with %s\n", nick.c_str());
+ DOMString fullJid = groupJid;
+ fullJid.append("/");
+ fullJid.append(nick);
+ parent.doChat(fullJid);
+}
+
+void GroupChatWindow::doSendFile(const DOMString &nick)
+{
+ printf("##Send file to %s\n", nick.c_str());
+ DOMString fullJid = groupJid;
+ fullJid.append("/");
+ fullJid.append(nick);
+ parent.doSendFile(fullJid);
+
+}
+
+
+
+//#########################################################################
+//# C O N N E C T D I A L O G
+//#########################################################################
+
+
+void ConnectDialog::okCallback()
+{
+ response(Gtk::RESPONSE_OK);
+ hide();
+}
+
+void ConnectDialog::cancelCallback()
+{
+ response(Gtk::RESPONSE_CANCEL);
+ hide();
+}
+
+
+bool ConnectDialog::doSetup()
+{
+ set_title("Connect");
+ set_size_request(300,200);
+
+ Glib::RefPtr<Gtk::ActionGroup> actionGroup = Gtk::ActionGroup::create();
+ actionGroup->add( Gtk::Action::create("MenuFile", "_File") );
+ actionGroup->add( Gtk::Action::create("Connect", Gtk::Stock::CONNECT, "Connect"),
+ sigc::mem_fun(*this, &ConnectDialog::okCallback) );
+ actionGroup->add( Gtk::Action::create("Cancel", Gtk::Stock::CANCEL, "Cancel"),
+ sigc::mem_fun(*this, &ConnectDialog::cancelCallback) );
+
+
+ Glib::RefPtr<Gtk::UIManager> uiManager = Gtk::UIManager::create();
+
+ uiManager->insert_action_group(actionGroup, 0);
+ add_accel_group(uiManager->get_accel_group());
+
+ Glib::ustring ui_info =
+ "<ui>"
+ " <menubar name='MenuBar'>"
+ " <menu action='MenuFile'>"
+ " <menuitem action='Connect'/>"
+ " <separator/>"
+ " <menuitem action='Cancel'/>"
+ " </menu>"
+ " </menubar>"
+ "</ui>";
+
+ uiManager->add_ui_from_string(ui_info);
+ Gtk::Widget* pMenuBar = uiManager->get_widget("/MenuBar");
+ get_vbox()->pack_start(*pMenuBar, Gtk::PACK_SHRINK);
+
+ table.resize(5, 2);
+ get_vbox()->pack_start(table);
+
+ parent.client.setHost("gristle.org");
+ parent.client.setPort(5223);
+ parent.client.setUsername("");
+ parent.client.setPassword("");
+ parent.client.setResource("pedroXmpp");
+
+ hostLabel.set_text("Host");
+ table.attach(hostLabel, 0, 1, 0, 1);
+ hostField.set_text(parent.client.getHost());
+ table.attach(hostField, 1, 2, 0, 1);
+
+ portLabel.set_text("Port");
+ table.attach(portLabel, 0, 1, 1, 2);
+ portSpinner.set_digits(0);
+ portSpinner.set_range(1, 65000);
+ portSpinner.set_value(parent.client.getPort());
+ table.attach(portSpinner, 1, 2, 1, 2);
+
+ userLabel.set_text("Username");
+ table.attach(userLabel, 0, 1, 2, 3);
+ userField.set_text(parent.client.getUsername());
+ table.attach(userField, 1, 2, 2, 3);
+
+ passLabel.set_text("Password");
+ table.attach(passLabel, 0, 1, 3, 4);
+ passField.set_visibility(false);
+ passField.set_text(parent.client.getPassword());
+ passField.signal_activate().connect(
+ sigc::mem_fun(*this, &ConnectDialog::okCallback) );
+ table.attach(passField, 1, 2, 3, 4);
+
+ resourceLabel.set_text("Resource");
+ table.attach(resourceLabel, 0, 1, 4, 5);
+ resourceField.set_text(parent.client.getResource());
+ table.attach(resourceField, 1, 2, 4, 5);
+
+ add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
+
+ show_all_children();
+
+ return true;
+}
+
+//#########################################################################
+//# C H A T D I A L O G
+//#########################################################################
+
+
+void ChatDialog::okCallback()
+{
+ response(Gtk::RESPONSE_OK);
+ hide();
+}
+
+void ChatDialog::cancelCallback()
+{
+ response(Gtk::RESPONSE_CANCEL);
+ hide();
+}
+
+
+bool ChatDialog::doSetup()
+{
+ set_title("Chat with User");
+ set_size_request(300,200);
+
+ Glib::RefPtr<Gtk::ActionGroup> actionGroup = Gtk::ActionGroup::create();
+ actionGroup->add( Gtk::Action::create("MenuFile", "_File") );
+ actionGroup->add( Gtk::Action::create("Chat", Gtk::Stock::CONNECT, "Chat"),
+ sigc::mem_fun(*this, &ChatDialog::okCallback) );
+ actionGroup->add( Gtk::Action::create("Cancel", Gtk::Stock::CANCEL, "Cancel"),
+ sigc::mem_fun(*this, &ChatDialog::cancelCallback) );
+
+
+ Glib::RefPtr<Gtk::UIManager> uiManager = Gtk::UIManager::create();
+
+ uiManager->insert_action_group(actionGroup, 0);
+ add_accel_group(uiManager->get_accel_group());
+
+ Glib::ustring ui_info =
+ "<ui>"
+ " <menubar name='MenuBar'>"
+ " <menu action='MenuFile'>"
+ " <menuitem action='Chat'/>"
+ " <separator/>"
+ " <menuitem action='Cancel'/>"
+ " </menu>"
+ " </menubar>"
+ "</ui>";
+
+ uiManager->add_ui_from_string(ui_info);
+ Gtk::Widget* pMenuBar = uiManager->get_widget("/MenuBar");
+ get_vbox()->pack_start(*pMenuBar, Gtk::PACK_SHRINK);
+
+ table.resize(2, 2);
+ get_vbox()->pack_start(table);
+
+ userLabel.set_text("User");
+ table.attach(userLabel, 0, 1, 0, 1);
+ //userField.set_text("");
+ table.attach(userField, 1, 2, 0, 1);
+
+ //userField.set_text("");
+ table.attach(textField, 0, 2, 1, 2);
+
+ add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
+
+ show_all_children();
+
+ return true;
+}
+
+//#########################################################################
+//# G R O U P C H A T D I A L O G
+//#########################################################################
+
+
+void GroupChatDialog::okCallback()
+{
+ response(Gtk::RESPONSE_OK);
+ hide();
+}
+
+void GroupChatDialog::cancelCallback()
+{
+ response(Gtk::RESPONSE_CANCEL);
+ hide();
+}
+
+
+bool GroupChatDialog::doSetup()
+{
+ set_title("Join Group Chat");
+ set_size_request(300,200);
+
+ Glib::RefPtr<Gtk::ActionGroup> actionGroup = Gtk::ActionGroup::create();
+ actionGroup->add( Gtk::Action::create("MenuFile", "_File") );
+ actionGroup->add( Gtk::Action::create("Join", Gtk::Stock::CONNECT, "Join Group"),
+ sigc::mem_fun(*this, &GroupChatDialog::okCallback) );
+ actionGroup->add( Gtk::Action::create("Cancel", Gtk::Stock::CANCEL, "Cancel"),
+ sigc::mem_fun(*this, &GroupChatDialog::cancelCallback) );
+
+
+ Glib::RefPtr<Gtk::UIManager> uiManager = Gtk::UIManager::create();
+
+ uiManager->insert_action_group(actionGroup, 0);
+ add_accel_group(uiManager->get_accel_group());
+
+ Glib::ustring ui_info =
+ "<ui>"
+ " <menubar name='MenuBar'>"
+ " <menu action='MenuFile'>"
+ " <menuitem action='Join'/>"
+ " <separator/>"
+ " <menuitem action='Cancel'/>"
+ " </menu>"
+ " </menubar>"
+ "</ui>";
+
+ uiManager->add_ui_from_string(ui_info);
+ Gtk::Widget* pMenuBar = uiManager->get_widget("/MenuBar");
+ get_vbox()->pack_start(*pMenuBar, Gtk::PACK_SHRINK);
+
+ table.resize(4, 2);
+ get_vbox()->pack_start(table);
+
+ groupLabel.set_text("Group");
+ table.attach(groupLabel, 0, 1, 0, 1);
+ groupField.set_text("inkscape");
+ table.attach(groupField, 1, 2, 0, 1);
+
+ hostLabel.set_text("Host");
+ table.attach(hostLabel, 0, 1, 1, 2);
+ hostField.set_text("conference.gristle.org");
+ table.attach(hostField, 1, 2, 1, 2);
+
+ nickLabel.set_text("Alt Name");
+ table.attach(nickLabel, 0, 1, 2, 3);
+ table.attach(nickField, 1, 2, 2, 3);
+
+ passLabel.set_text("Password");
+ table.attach(passLabel, 0, 1, 3, 4);
+ passField.set_visibility(false);
+ //passField.set_text("");
+ table.attach(passField, 1, 2, 3, 4);
+
+
+ add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
+
+ show_all_children();
+
+ return true;
+}
+
+//#########################################################################
+//# F I L E S E N D D I A L O G
+//#########################################################################
+
+
+void FileSendDialog::okCallback()
+{
+ response(Gtk::RESPONSE_OK);
+ hide();
+}
+
+void FileSendDialog::cancelCallback()
+{
+ response(Gtk::RESPONSE_CANCEL);
+ hide();
+}
+
+
+void FileSendDialog::buttonCallback()
+{
+ Gtk::FileChooserDialog dlg("Select a file to send",
+ Gtk::FILE_CHOOSER_ACTION_OPEN);
+ dlg.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ dlg.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
+ int ret = dlg.run();
+ if (ret == Gtk::RESPONSE_OK || ret == Gtk::RESPONSE_ACCEPT)
+ {
+ fileName = dlg.get_filename();
+ fileNameField.set_text(fileName);
+ }
+}
+
+bool FileSendDialog::doSetup()
+{
+ set_title("Send file to user");
+ set_size_request(400,150);
+
+ Glib::RefPtr<Gtk::ActionGroup> actionGroup = Gtk::ActionGroup::create();
+ actionGroup->add( Gtk::Action::create("MenuFile", "_File") );
+ actionGroup->add( Gtk::Action::create("Send", Gtk::Stock::NETWORK, "Send File"),
+ sigc::mem_fun(*this, &FileSendDialog::okCallback) );
+ actionGroup->add( Gtk::Action::create("Cancel", Gtk::Stock::CANCEL, "Cancel"),
+ sigc::mem_fun(*this, &FileSendDialog::cancelCallback) );
+
+
+ Glib::RefPtr<Gtk::UIManager> uiManager = Gtk::UIManager::create();
+
+ uiManager->insert_action_group(actionGroup, 0);
+ add_accel_group(uiManager->get_accel_group());
+
+ Glib::ustring ui_info =
+ "<ui>"
+ " <menubar name='MenuBar'>"
+ " <menu action='MenuFile'>"
+ " <menuitem action='Send'/>"
+ " <separator/>"
+ " <menuitem action='Cancel'/>"
+ " </menu>"
+ " </menubar>"
+ "</ui>";
+
+ uiManager->add_ui_from_string(ui_info);
+ Gtk::Widget* pMenuBar = uiManager->get_widget("/MenuBar");
+ get_vbox()->pack_start(*pMenuBar, Gtk::PACK_SHRINK);
+
+ table.resize(2, 2);
+ get_vbox()->pack_start(table);
+
+ jidLabel.set_text("User ID");
+ table.attach(jidLabel, 0, 1, 0, 1);
+ jidField.set_text("inkscape");
+ table.attach(jidField, 1, 2, 0, 1);
+
+ selectFileButton.set_label("Select");
+ selectFileButton.signal_clicked().connect(
+ sigc::mem_fun(*this, &FileSendDialog::buttonCallback) );
+ table.attach(selectFileButton, 0, 1, 1, 2);
+
+ fileName = "";
+ fileNameField.set_text("No file selected");
+ fileNameField.set_editable(false);
+ table.attach(fileNameField, 1, 2, 1, 2);
+
+ add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
+
+ show_all_children();
+
+ return true;
+}
+
+
+//#########################################################################
+//# F I L E R E C E I V E D I A L O G
+//#########################################################################
+
+
+void FileReceiveDialog::okCallback()
+{
+ response(Gtk::RESPONSE_OK);
+ hide();
+}
+
+void FileReceiveDialog::cancelCallback()
+{
+ response(Gtk::RESPONSE_CANCEL);
+ hide();
+}
+
+void FileReceiveDialog::buttonCallback()
+{
+ Gtk::FileChooserDialog dlg("Select a file to save",
+ Gtk::FILE_CHOOSER_ACTION_SAVE);
+ dlg.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ dlg.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
+ int ret = dlg.run();
+ if (ret == Gtk::RESPONSE_OK || ret == Gtk::RESPONSE_ACCEPT)
+ {
+ fileName = dlg.get_filename();
+ fileNameField.set_text(fileName);
+ }
+}
+
+
+bool FileReceiveDialog::doSetup()
+{
+ set_title("File being sent by user");
+ set_size_request(450,250);
+
+ Glib::RefPtr<Gtk::ActionGroup> actionGroup = Gtk::ActionGroup::create();
+ actionGroup->add( Gtk::Action::create("MenuFile", "_File") );
+ actionGroup->add( Gtk::Action::create("Send", Gtk::Stock::NETWORK, "Send File"),
+ sigc::mem_fun(*this, &FileReceiveDialog::okCallback) );
+ actionGroup->add( Gtk::Action::create("Cancel", Gtk::Stock::CANCEL, "Cancel"),
+ sigc::mem_fun(*this, &FileReceiveDialog::cancelCallback) );
+
+
+ Glib::RefPtr<Gtk::UIManager> uiManager = Gtk::UIManager::create();
+
+ uiManager->insert_action_group(actionGroup, 0);
+ add_accel_group(uiManager->get_accel_group());
+
+ Glib::ustring ui_info =
+ "<ui>"
+ " <menubar name='MenuBar'>"
+ " <menu action='MenuFile'>"
+ " <menuitem action='Send'/>"
+ " <separator/>"
+ " <menuitem action='Cancel'/>"
+ " </menu>"
+ " </menubar>"
+ "</ui>";
+
+ uiManager->add_ui_from_string(ui_info);
+ Gtk::Widget* pMenuBar = uiManager->get_widget("/MenuBar");
+ get_vbox()->pack_start(*pMenuBar, Gtk::PACK_SHRINK);
+
+ table.resize(6, 2);
+ get_vbox()->pack_start(table);
+
+ jidLabel.set_text("User ID");
+ table.attach(jidLabel, 0, 1, 0, 1);
+ jidField.set_text(jid);
+ jidField.set_editable(false);
+ table.attach(jidField, 1, 2, 0, 1);
+
+ offeredLabel.set_text("File Offered");
+ table.attach(offeredLabel, 0, 1, 1, 2);
+ offeredField.set_text(offeredName);
+ offeredField.set_editable(false);
+ table.attach(offeredField, 1, 2, 1, 2);
+
+ descLabel.set_text("Description");
+ table.attach(descLabel, 0, 1, 2, 3);
+ descField.set_text(desc);
+ descField.set_editable(false);
+ table.attach(descField, 1, 2, 2, 3);
+
+ char buf[32];
+ snprintf(buf, 31, "%ld", fileSize);
+ sizeLabel.set_text("Size");
+ table.attach(sizeLabel, 0, 1, 3, 4);
+ sizeField.set_text(buf);
+ sizeField.set_editable(false);
+ table.attach(sizeField, 1, 2, 3, 4);
+
+ hashLabel.set_text("MD5 Hash");
+ table.attach(hashLabel, 0, 1, 4, 5);
+ hashField.set_text(hash);
+ hashField.set_editable(false);
+ table.attach(hashField, 1, 2, 4, 5);
+
+ selectFileButton.set_label("Select");
+ selectFileButton.signal_clicked().connect(
+ sigc::mem_fun(*this, &FileReceiveDialog::buttonCallback) );
+ table.attach(selectFileButton, 0, 1, 5, 6);
+
+ fileName = "";
+ fileNameField.set_text("No file selected");
+ fileNameField.set_editable(false);
+ table.attach(fileNameField, 1, 2, 5, 6);
+
+
+ add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
+
+ show_all_children();
+
+ return true;
+}
+
+
+//#########################################################################
+//# M A I N W I N D O W
+//#########################################################################
+
+PedroGui::PedroGui()
+{
+ doSetup();
+}
+
+PedroGui::~PedroGui()
+{
+ chatDeleteAll();
+ groupChatDeleteAll();
+}
+
+
+void PedroGui::error(const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(writeBuf, writeBufLen, fmt, args);
+ va_end(args) ;
+
+ Gtk::MessageDialog dlg(writeBuf,
+ false,
+ Gtk::MESSAGE_ERROR,
+ Gtk::BUTTONS_OK,
+ true);
+ dlg.run();
+}
+
+void PedroGui::status(const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(writeBuf, writeBufLen, fmt, args);
+ va_end(args) ;
+ messageList.postMessage("STATUS", writeBuf);
+}
+
+//################################
+//# CHAT WINDOW MANAGEMENT
+//################################
+bool PedroGui::chatCreate(const DOMString &userJid)
+{
+ std::vector<ChatWindow *>::iterator iter;
+ for (iter=chats.begin() ; iter != chats.end() ; iter++)
+ {
+ if (userJid == (*iter)->getJid())
+ return false;
+ }
+ ChatWindow *chat = new ChatWindow(*this, userJid);
+ chat->show();
+ chats.push_back(chat);
+ return true;
+}
+
+bool PedroGui::chatDelete(const DOMString &userJid)
+{
+ std::vector<ChatWindow *>::iterator iter;
+ for (iter=chats.begin() ; iter != chats.end() ; )
+ {
+ if (userJid == (*iter)->getJid())
+ {
+ delete(*iter);
+ iter = chats.erase(iter);
+ }
+ else
+ iter++;
+ }
+ return true;
+}
+
+bool PedroGui::chatDeleteAll()
+{
+ std::vector<ChatWindow *>::iterator iter;
+ for (iter=chats.begin() ; iter != chats.end() ; )
+ {
+ delete(*iter);
+ iter = chats.erase(iter);
+ }
+ return true;
+}
+
+bool PedroGui::chatMessage(const DOMString &from, const DOMString &data)
+{
+ std::vector<ChatWindow *>::iterator iter;
+ for (iter=chats.begin() ; iter != chats.end() ; iter++)
+ {
+ if (from == (*iter)->getJid())
+ {
+ (*iter)->postMessage(data);
+ return true;
+ }
+ }
+ ChatWindow *chat = new ChatWindow(*this, from);
+ chat->show();
+ chats.push_back(chat);
+ chat->postMessage(data);
+ return true;
+}
+
+//################################
+//# GROUP CHAT WINDOW MANAGEMENT
+//################################
+bool PedroGui::groupChatCreate(const DOMString &groupJid, const DOMString &nick)
+{
+ std::vector<GroupChatWindow *>::iterator iter;
+ for (iter=groupChats.begin() ; iter != groupChats.end() ; iter++)
+ {
+ if (groupJid == (*iter)->getGroupJid())
+ return false;
+ }
+ GroupChatWindow *chat = new GroupChatWindow(*this, groupJid, nick);
+ chat->show();
+ groupChats.push_back(chat);
+ return true;
+}
+
+
+bool PedroGui::groupChatDelete(const DOMString &groupJid, const DOMString &nick)
+{
+ std::vector<GroupChatWindow *>::iterator iter;
+ for (iter=groupChats.begin() ; iter != groupChats.end() ;)
+ {
+ if (groupJid == (*iter)->getGroupJid() &&
+ nick == (*iter)->getNick())
+ {
+ delete(*iter);
+ iter = groupChats.erase(iter);
+ }
+ else
+ iter++;
+ }
+ return true;
+}
+
+
+bool PedroGui::groupChatDeleteAll()
+{
+ std::vector<GroupChatWindow *>::iterator iter;
+ for (iter=groupChats.begin() ; iter != groupChats.end() ; )
+ {
+ delete(*iter);
+ iter = groupChats.erase(iter);
+ }
+ return true;
+}
+
+
+bool PedroGui::groupChatMessage(const DOMString &groupJid,
+ const DOMString &from, const DOMString &data)
+{
+ std::vector<GroupChatWindow *>::iterator iter;
+ for (iter=groupChats.begin() ; iter != groupChats.end() ; iter++)
+ {
+ if (groupJid == (*iter)->getGroupJid())
+ {
+ (*iter)->receiveMessage(from, data);
+ }
+ }
+ return true;
+}
+
+bool PedroGui::groupChatPresence(const DOMString &groupJid,
+ const DOMString &nick, bool presence,
+ const DOMString &show,
+ const DOMString &status)
+{
+ std::vector<GroupChatWindow *>::iterator iter;
+ for (iter=groupChats.begin() ; iter != groupChats.end() ; iter++)
+ {
+ if (groupJid == (*iter)->getGroupJid())
+ {
+ (*iter)->receivePresence(nick, presence, show, status);
+ }
+ }
+ return true;
+}
+
+//################################
+//# EVENTS
+//################################
+/**
+ *
+ */
+void PedroGui::doEvent(const XmppEvent &event)
+{
+
+ int typ = event.getType();
+ switch (typ)
+ {
+ case XmppEvent::EVENT_STATUS:
+ {
+ //printf("##### STATUS: %s\n", event.getData().c_str());
+ status(event.getData().c_str());
+ break;
+ }
+ case XmppEvent::EVENT_ERROR:
+ {
+ //printf("##### ERROR: %s\n", event.getData().c_str());
+ error(event.getData().c_str());
+ break;
+ }
+ case XmppEvent::EVENT_CONNECTED:
+ {
+ status("##### CONNECTED");
+ actionEnable("Connect", false);
+ actionEnable("Chat", true);
+ actionEnable("GroupChat", true);
+ actionEnable("Disconnect", true);
+ DOMString title = "Pedro - ";
+ title.append(client.getJid());
+ set_title(title);
+ break;
+ }
+ case XmppEvent::EVENT_DISCONNECTED:
+ {
+ status("##### DISCONNECTED");
+ actionEnable("Connect", true);
+ actionEnable("Chat", false);
+ actionEnable("GroupChat", false);
+ actionEnable("Disconnect", false);
+ DOMString title = "Pedro";
+ set_title(title);
+ chatDeleteAll();
+ groupChatDeleteAll();
+ break;
+ }
+ case XmppEvent::EVENT_MESSAGE:
+ {
+ status("##### MESSAGE: %s\n", event.getFrom().c_str());
+ chatMessage(event.getFrom(), event.getData());
+ break;
+ }
+ case XmppEvent::EVENT_PRESENCE:
+ {
+ status("##### PRESENCE: %s\n", event.getFrom().c_str());
+ roster.refresh();
+ break;
+ }
+ case XmppEvent::EVENT_ROSTER:
+ {
+ status("##### ROSTER\n");
+ roster.refresh();
+ break;
+ }
+ case XmppEvent::EVENT_MUC_JOIN:
+ {
+ status("##### GROUP JOINED: %s\n", event.getGroup().c_str());
+ break;
+ }
+ case XmppEvent::EVENT_MUC_MESSAGE:
+ {
+ //printf("##### MUC_MESSAGE: %s\n", event.getGroup().c_str());
+ groupChatMessage(event.getGroup(),
+ event.getFrom(), event.getData());
+ break;
+ }
+ case XmppEvent::EVENT_MUC_PRESENCE:
+ {
+ //printf("##### MUC_USER LIST: %s\n", event.getFrom().c_str());
+ groupChatPresence(event.getGroup(),
+ event.getFrom(),
+ event.getPresence(),
+ event.getShow(),
+ event.getStatus());
+ break;
+ }
+ case XmppEvent::EVENT_MUC_LEAVE:
+ {
+ status("##### GROUP LEFT: %s\n", event.getGroup().c_str());
+ groupChatDelete(event.getGroup(), event.getFrom());
+ break;
+ }
+ case XmppEvent::EVENT_FILE_RECEIVE:
+ {
+ status("##### FILE RECEIVE: %s\n", event.getFileName().c_str());
+ doReceiveFile(event.getFrom(), event.getIqId(), event.getStreamId(),
+ event.getFileName(), event.getFileDesc(),
+ event.getFileSize(), event.getFileHash());
+ break;
+ }
+ default:
+ {
+ printf("unknown event type: %d\n", typ);
+ break;
+ }
+ }
+
+}
+
+/**
+ *
+ */
+bool PedroGui::checkEventQueue()
+{
+ while (client.eventQueueAvailable() > 0)
+ {
+ XmppEvent evt = client.eventQueuePop();
+ doEvent(evt);
+ }
+
+ while( Gtk::Main::events_pending() )
+ Gtk::Main::iteration();
+
+ return true;
+}
+
+
+//##################
+//# COMMANDS
+//##################
+void PedroGui::doChat(const DOMString &jid)
+{
+ if (jid.size()>0)
+ {
+ chatCreate(jid);
+ return;
+ }
+
+ FileSendDialog dlg(*this);
+ int ret = dlg.run();
+ if (ret == Gtk::RESPONSE_OK)
+ {
+ chatCreate(dlg.getJid());
+ }
+
+}
+
+void PedroGui::doSendFile(const DOMString &jid)
+{
+ FileSendDialog dlg(*this);
+ if (jid.size()>0)
+ dlg.setJid(jid);
+ int ret = dlg.run();
+ if (ret == Gtk::RESPONSE_OK)
+ {
+ DOMString fileName = dlg.getFileName();
+ printf("fileName:%s\n", fileName.c_str());
+ DOMString offeredName = "";
+ DOMString desc = "";
+ client.fileSendBackground(jid, offeredName, fileName, desc);
+ }
+
+}
+
+void PedroGui::doReceiveFile(
+ const DOMString &jid,
+ const DOMString &iqId,
+ const DOMString &streamId,
+ const DOMString &offeredName,
+ const DOMString &desc,
+ long size,
+ const DOMString &hash
+ )
+
+{
+ FileReceiveDialog dlg(*this, jid, iqId, streamId,
+ offeredName, desc, size, hash);
+ int ret = dlg.run();
+ if (ret == Gtk::RESPONSE_OK)
+ {
+ DOMString fileName = dlg.getFileName();
+ printf("fileName:%s\n", fileName.c_str());
+ client.fileReceiveBackground(jid, iqId, streamId, fileName, size, hash);
+ }
+
+}
+
+
+//##################
+//# CALLBACKS
+//##################
+void PedroGui::connectCallback()
+{
+ ConnectDialog dialog(*this);
+ int result = dialog.run();
+ dialog.hide();
+ if (result == Gtk::RESPONSE_OK)
+ {
+ client.setHost(dialog.getHost());
+ client.setPort(dialog.getPort());
+ client.setUsername(dialog.getUser());
+ client.setPassword(dialog.getPass());
+ client.setResource(dialog.getResource());
+ client.connect();
+ }
+}
+
+void PedroGui::chatCallback()
+{
+ ChatDialog dialog(*this);
+ int result = dialog.run();
+ dialog.hide();
+ if (result == Gtk::RESPONSE_OK)
+ {
+ client.message(dialog.getUser(), dialog.getText());
+ }
+}
+
+void PedroGui::groupChatCallback()
+{
+ GroupChatDialog dialog(*this);
+ int result = dialog.run();
+ dialog.hide();
+ if (result != Gtk::RESPONSE_OK)
+ return;
+ DOMString groupJid = dialog.getGroup();
+ groupJid.append("@");
+ groupJid.append(dialog.getHost());
+ if (client.groupChatExists(groupJid))
+ {
+ error("Group chat %s already exists", groupJid.c_str());
+ return;
+ }
+ groupChatCreate(groupJid, dialog.getNick());
+ client.groupChatJoin(groupJid, dialog.getNick(), dialog.getPass() );
+}
+
+void PedroGui::disconnectCallback()
+{
+ client.disconnect();
+}
+
+void PedroGui::quitCallback()
+{
+ Gtk::Main::quit();
+}
+
+void PedroGui::fontCallback()
+{
+ Gtk::FontSelectionDialog dlg;
+ int ret = dlg.run();
+ if (ret == Gtk::RESPONSE_OK)
+ {
+ Glib::ustring fontName = dlg.get_font_name();
+ Pango::FontDescription fontDesc(fontName);
+ modify_font(fontDesc);
+ }
+}
+
+void PedroGui::colorCallback()
+{
+ Gtk::ColorSelectionDialog dlg;
+ int ret = dlg.run();
+ if (ret == Gtk::RESPONSE_OK)
+ {
+ Gdk::Color col = dlg.get_colorsel()->get_current_color();
+ modify_bg(Gtk::STATE_NORMAL, col);
+ }
+}
+
+void PedroGui::sendFileCallback()
+{
+ doSendFile("");
+}
+
+void PedroGui::aboutCallback()
+{
+ Gtk::AboutDialog dlg;
+ std::vector<Glib::ustring>authors;
+ authors.push_back("Bob Jamison");
+ dlg.set_authors(authors);
+ DOMString comments = "A simple XMPP gui client ";
+ comments.append("Based on the Pedro XMPP client");
+ dlg.set_comments(comments);
+ dlg.set_version("1.0");
+ dlg.run();
+}
+
+void PedroGui::actionEnable(const DOMString &name, bool val)
+{
+ DOMString path = "/ui/MenuBar/MenuFile/";
+ path.append(name);
+ Glib::RefPtr<Gtk::Action> action = uiManager->get_action(path);
+ if (!action)
+ {
+ path = "/ui/MenuBar/MenuEdit/";
+ path.append(name);
+ action = uiManager->get_action(path);
+ }
+ if (!action)
+ {
+ path = "/ui/MenuBar/MenuTransfer/";
+ path.append(name);
+ action = uiManager->get_action(path);
+ }
+ if (!action)
+ {
+ path = "/ui/MenuBar/MenuHelp/";
+ path.append(name);
+ action = uiManager->get_action(path);
+ }
+ if (!action)
+ return;
+ action->set_sensitive(val);
+}
+
+
+bool PedroGui::doSetup()
+{
+ set_title("Inkscape XMPP (Inkboard)");
+ set_size_request(500, 300);
+ add(mainBox);
+
+ Glib::RefPtr<Gtk::ActionGroup> actionGroup = Gtk::ActionGroup::create();
+
+ //### FILE MENU
+ actionGroup->add( Gtk::Action::create("MenuFile", "_File") );
+
+ actionGroup->add( Gtk::Action::create("Connect",
+ Gtk::Stock::CONNECT, "Connect"),
+ sigc::mem_fun(*this, &PedroGui::connectCallback) );
+
+ actionGroup->add( Gtk::Action::create("Chat",
+ Gtk::Stock::CONNECT, "Chat"),
+ sigc::mem_fun(*this, &PedroGui::chatCallback) );
+
+ actionGroup->add( Gtk::Action::create("GroupChat",
+ Gtk::Stock::CONNECT, "Group Chat"),
+ sigc::mem_fun(*this, &PedroGui::groupChatCallback) );
+
+ actionGroup->add( Gtk::Action::create("Disconnect",
+ Gtk::Stock::DISCONNECT, "Disconnect"),
+ sigc::mem_fun(*this, &PedroGui::disconnectCallback) );
+
+ actionGroup->add( Gtk::Action::create("Quit", Gtk::Stock::QUIT),
+ sigc::mem_fun(*this, &PedroGui::quitCallback) );
+
+ //### EDIT MENU
+ actionGroup->add( Gtk::Action::create("MenuEdit", "_Edit") );
+ actionGroup->add( Gtk::Action::create("SelectFont",
+ Gtk::Stock::SELECT_FONT, "Select Font"),
+ sigc::mem_fun(*this, &PedroGui::fontCallback) );
+ actionGroup->add( Gtk::Action::create("SelectColor",
+ Gtk::Stock::SELECT_COLOR, "Select Color"),
+ sigc::mem_fun(*this, &PedroGui::colorCallback) );
+
+ //### TRANSFER MENU
+ actionGroup->add( Gtk::Action::create("MenuTransfer", "_Transfer") );
+ actionGroup->add( Gtk::Action::create("SendFile",
+ Gtk::Stock::NETWORK, "Send File"),
+ sigc::mem_fun(*this, &PedroGui::sendFileCallback) );
+
+ //### HELP MENU
+ actionGroup->add( Gtk::Action::create("MenuHelp", "_Help") );
+ actionGroup->add( Gtk::Action::create("About",
+ Gtk::Stock::ABOUT, "About Pedro"),
+ sigc::mem_fun(*this, &PedroGui::aboutCallback) );
+
+ uiManager = Gtk::UIManager::create();
+
+ uiManager->insert_action_group(actionGroup, 0);
+ add_accel_group(uiManager->get_accel_group());
+
+ Glib::ustring ui_info =
+ "<ui>"
+ " <menubar name='MenuBar'>"
+ " <menu action='MenuFile'>"
+ " <menuitem action='Connect'/>"
+ " <separator/>"
+ " <menuitem action='Chat'/>"
+ " <menuitem action='GroupChat'/>"
+ " <separator/>"
+ " <menuitem action='Disconnect'/>"
+ " <menuitem action='Quit'/>"
+ " </menu>"
+ " <menu action='MenuEdit'>"
+ " <menuitem action='SelectFont'/>"
+ " <menuitem action='SelectColor'/>"
+ " </menu>"
+ " <menu action='MenuTransfer'>"
+ " <menuitem action='SendFile'/>"
+ " </menu>"
+ " <menu action='MenuHelp'>"
+ " <menuitem action='About'/>"
+ " </menu>"
+ " </menubar>"
+ "</ui>";
+
+ uiManager->add_ui_from_string(ui_info);
+ Gtk::Widget* pMenuBar = uiManager->get_widget("/MenuBar");
+ mainBox.pack_start(*pMenuBar, Gtk::PACK_SHRINK);
+
+ actionEnable("Connect", true);
+ actionEnable("Chat", false);
+ actionEnable("GroupChat", false);
+ actionEnable("Disconnect", false);
+
+ mainBox.pack_start(vPaned);
+ vPaned.add1(roster);
+ vPaned.add2(messageList);
+ roster.setParent(this);
+
+ show_all_children();
+
+ //# Start a timer to check the queue every nn milliseconds
+ Glib::signal_timeout().connect(
+ sigc::mem_fun(*this, &PedroGui::checkEventQueue), 20 );
+
+ //client.addXmppEventListener(*this);
+ client.eventQueueEnable(true);
+
+ return true;
+}
+
+
+} // namespace Pedro
+
+
+
+//########################################################################
+//# E N D O F F I L E
+//########################################################################
+
diff --git a/src/pedro/pedrogui.h b/src/pedro/pedrogui.h
--- /dev/null
+++ b/src/pedro/pedrogui.h
@@ -0,0 +1,731 @@
+#ifndef __PEDROGUI_H__
+#define __PEDROGUI_H__
+/*
+ * Simple demo GUI for the Pedro mini-XMPP client.
+ *
+ * Authors:
+ * Bob Jamison
+ *
+ * Copyright (C) 2005 Bob Jamison
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+
+#include <gtkmm.h>
+
+#include "pedroxmpp.h"
+
+
+namespace Pedro
+{
+
+
+class PedroGui;
+class GroupChatWindow;
+
+//#########################################################################
+//# R O S T E R
+//#########################################################################
+class Roster : public Gtk::ScrolledWindow
+{
+public:
+
+ Roster()
+ { doSetup(); }
+
+ virtual ~Roster()
+ {}
+
+ /**
+ * Clear all roster items from the list
+ */
+ virtual void clear();
+
+ /**
+ * Regenerate the roster
+ */
+ virtual void refresh();
+
+
+ void setParent(PedroGui *val)
+ { parent = val; }
+
+private:
+
+ class CustomTreeView : public Gtk::TreeView
+ {
+ public:
+ CustomTreeView()
+ { parent = NULL; }
+ virtual ~CustomTreeView()
+ {}
+
+ bool on_button_press_event(GdkEventButton* event)
+ {
+ Gtk::TreeView::on_button_press_event(event);
+ if (parent)
+ parent->buttonPressCallback(event);
+ return true;
+ }
+ void setParent(Roster *val)
+ { parent = val; }
+
+ private:
+ Roster *parent;
+ };
+
+ void doubleClickCallback(const Gtk::TreeModel::Path &path,
+ Gtk::TreeViewColumn *col);
+
+ void sendFileCallback();
+ void chatCallback();
+ bool buttonPressCallback(GdkEventButton* event);
+
+ bool doSetup();
+
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf_available;
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf_away;
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf_chat;
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf_dnd;
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf_error;
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf_offline;
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf_xa;
+
+ class RosterColumns : public Gtk::TreeModel::ColumnRecord
+ {
+ public:
+ RosterColumns()
+ {
+ add(groupColumn);
+ add(statusColumn); add(userColumn);
+ add(nameColumn); add(subColumn);
+ }
+
+ Gtk::TreeModelColumn<Glib::ustring> groupColumn;
+ Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf> > statusColumn;
+ Gtk::TreeModelColumn<Glib::ustring> userColumn;
+ Gtk::TreeModelColumn<Glib::ustring> nameColumn;
+ Gtk::TreeModelColumn<Glib::ustring> subColumn;
+ };
+
+ RosterColumns rosterColumns;
+
+ Glib::RefPtr<Gtk::UIManager> uiManager;
+
+ Glib::RefPtr<Gtk::TreeStore> treeStore;
+ CustomTreeView rosterView;
+
+ PedroGui *parent;
+};
+
+//#########################################################################
+//# M E S S A G E L I S T
+//#########################################################################
+class MessageList : public Gtk::ScrolledWindow
+{
+public:
+
+ MessageList()
+ { doSetup(); }
+
+ virtual ~MessageList()
+ {}
+
+ /**
+ * Clear all messages from the list
+ */
+ virtual void clear();
+
+ /**
+ * Post a message to the list
+ */
+ virtual void postMessage(const DOMString &from, const DOMString &msg);
+
+private:
+
+ bool doSetup();
+
+ Gtk::TextView messageList;
+ Glib::RefPtr<Gtk::TextBuffer> messageListBuffer;
+
+};
+
+//#########################################################################
+//# U S E R L I S T
+//#########################################################################
+class UserList : public Gtk::ScrolledWindow
+{
+public:
+
+ UserList()
+ { doSetup(); }
+
+ virtual ~UserList()
+ {}
+
+ /**
+ * Clear all messages from the list
+ */
+ virtual void clear();
+
+ /**
+ * Post a message to the list
+ */
+ virtual void addUser(const DOMString &user, const DOMString &show);
+
+
+ void setParent(GroupChatWindow *val)
+ { parent = val; }
+
+private:
+
+ class CustomTreeView : public Gtk::TreeView
+ {
+ public:
+ CustomTreeView()
+ { parent = NULL; }
+ virtual ~CustomTreeView()
+ {}
+
+ bool on_button_press_event(GdkEventButton* event)
+ {
+ Gtk::TreeView::on_button_press_event(event);
+ if (parent)
+ parent->buttonPressCallback(event);
+ return true;
+ }
+ void setParent(UserList *val)
+ { parent = val; }
+
+ private:
+ UserList *parent;
+ };
+
+ void doubleClickCallback(const Gtk::TreeModel::Path &path,
+ Gtk::TreeViewColumn *col);
+
+ void sendFileCallback();
+ void chatCallback();
+ bool buttonPressCallback(GdkEventButton* event);
+
+ bool doSetup();
+
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf_available;
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf_away;
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf_chat;
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf_dnd;
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf_error;
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf_offline;
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf_xa;
+
+ class UserListColumns : public Gtk::TreeModel::ColumnRecord
+ {
+ public:
+ UserListColumns()
+ { add(statusColumn); add(userColumn); }
+
+ Gtk::TreeModelColumn<Glib::ustring> userColumn;
+ Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf> > statusColumn;
+ };
+
+ UserListColumns userListColumns;
+
+ Glib::RefPtr<Gtk::UIManager> uiManager;
+
+ Glib::RefPtr<Gtk::ListStore> userListStore;
+ CustomTreeView userList;
+
+ GroupChatWindow *parent;
+};
+
+
+//#########################################################################
+//# C H A T W I N D O W
+//#########################################################################
+class ChatWindow : public Gtk::Window
+{
+public:
+
+ ChatWindow(PedroGui &par, const DOMString jid);
+
+ virtual ~ChatWindow();
+
+ virtual DOMString getJid()
+ { return jid; }
+
+ virtual void setJid(const DOMString &val)
+ { jid = val; }
+
+ virtual bool postMessage(const DOMString &data);
+
+private:
+
+ DOMString jid;
+
+ void leaveCallback();
+ void hideCallback();
+ void textEnterCallback();
+
+ bool doSetup();
+
+ Gtk::VBox vbox;
+ Gtk::VPaned vPaned;
+
+ MessageList messageList;
+
+ Gtk::Entry inputTxt;
+
+ PedroGui &parent;
+};
+
+
+//#########################################################################
+//# G R O U P C H A T W I N D O W
+//#########################################################################
+class GroupChatWindow : public Gtk::Window
+{
+public:
+
+ GroupChatWindow(PedroGui &par, const DOMString &groupJid,
+ const DOMString &nick);
+
+ virtual ~GroupChatWindow();
+
+
+ virtual DOMString getGroupJid()
+ { return groupJid; }
+
+ virtual void setGroupJid(const DOMString &val)
+ { groupJid = val; }
+
+ virtual DOMString getNick()
+ { return nick; }
+
+ virtual void setNick(const DOMString &val)
+ { nick = val; }
+
+ virtual bool receiveMessage(const DOMString &from,
+ const DOMString &data);
+
+ virtual bool receivePresence(const DOMString &nick,
+ bool presence,
+ const DOMString &show,
+ const DOMString &status);
+
+ virtual void doSendFile(const DOMString &nick);
+
+ virtual void doChat(const DOMString &nick);
+
+
+private:
+
+ void textEnterCallback();
+ void leaveCallback();
+ void hideCallback();
+
+ bool doSetup();
+
+ Gtk::VBox vbox;
+ Gtk::VPaned vPaned;
+ Gtk::HPaned hPaned;
+
+ MessageList messageList;
+
+ UserList userList;
+
+ Gtk::Entry inputTxt;
+
+ DOMString groupJid;
+ DOMString nick;
+
+ PedroGui &parent;
+ };
+
+
+//#########################################################################
+//# C O N N E C T D I A L O G
+//#########################################################################
+class ConnectDialog : public Gtk::Dialog
+{
+public:
+
+ ConnectDialog(PedroGui &par) : parent(par)
+ { doSetup(); }
+
+ virtual ~ConnectDialog()
+ {}
+
+ DOMString getHost()
+ { return hostField.get_text(); }
+ int getPort()
+ { return (int)portSpinner.get_value(); }
+ DOMString getUser()
+ { return userField.get_text(); }
+ DOMString getPass()
+ { return passField.get_text(); }
+ DOMString getResource()
+ { return resourceField.get_text(); }
+
+private:
+
+ void okCallback();
+ void cancelCallback();
+
+ bool doSetup();
+
+ Gtk::Table table;
+
+ Gtk::Label hostLabel;
+ Gtk::Entry hostField;
+ Gtk::Label portLabel;
+ Gtk::SpinButton portSpinner;
+ Gtk::Label userLabel;
+ Gtk::Entry userField;
+ Gtk::Label passLabel;
+ Gtk::Entry passField;
+ Gtk::Label resourceLabel;
+ Gtk::Entry resourceField;
+
+ PedroGui &parent;
+};
+
+
+
+//#########################################################################
+//# C H A T D I A L O G
+//#########################################################################
+class ChatDialog : public Gtk::Dialog
+{
+public:
+
+ ChatDialog(PedroGui &par) : parent(par)
+ { doSetup(); }
+
+ virtual ~ChatDialog()
+ {}
+
+ DOMString getUser()
+ { return userField.get_text(); }
+
+ DOMString getText()
+ { return textField.get_text(); }
+
+private:
+
+ void okCallback();
+ void cancelCallback();
+
+ bool doSetup();
+
+ Gtk::Table table;
+
+ Gtk::Label userLabel;
+ Gtk::Entry userField;
+ Gtk::Entry textField;
+
+ PedroGui &parent;
+};
+
+
+
+//#########################################################################
+//# G R O U P C H A T D I A L O G
+//#########################################################################
+
+class GroupChatDialog : public Gtk::Dialog
+{
+public:
+
+ GroupChatDialog(PedroGui &par) : parent(par)
+ { doSetup(); }
+
+ virtual ~GroupChatDialog()
+ {}
+
+ DOMString getGroup()
+ { return groupField.get_text(); }
+ DOMString getHost()
+ { return hostField.get_text(); }
+ DOMString getPass()
+ { return passField.get_text(); }
+ DOMString getNick()
+ { return nickField.get_text(); }
+
+private:
+
+ void okCallback();
+ void cancelCallback();
+
+ bool doSetup();
+
+ Gtk::Table table;
+
+ Gtk::Label groupLabel;
+ Gtk::Entry groupField;
+ Gtk::Label hostLabel;
+ Gtk::Entry hostField;
+ Gtk::Label passLabel;
+ Gtk::Entry passField;
+ Gtk::Label nickLabel;
+ Gtk::Entry nickField;
+
+ PedroGui &parent;
+};
+
+
+//#########################################################################
+//# F I L E S E N D D I A L O G
+//#########################################################################
+
+class FileSendDialog : public Gtk::Dialog
+{
+public:
+
+ FileSendDialog(PedroGui &par) : parent(par)
+ { doSetup(); }
+
+ virtual ~FileSendDialog()
+ {}
+
+ DOMString getFileName()
+ { return fileName; }
+ DOMString getJid()
+ { return jidField.get_text(); }
+ void setJid(const DOMString &val)
+ { return jidField.set_text(val); }
+
+private:
+
+ void okCallback();
+ void cancelCallback();
+ void buttonCallback();
+
+ bool doSetup();
+
+ Gtk::Table table;
+
+ Gtk::Label jidLabel;
+ Gtk::Entry jidField;
+
+ DOMString fileName;
+ Gtk::Entry fileNameField;
+
+ Gtk::Button selectFileButton;
+
+ PedroGui &parent;
+};
+
+//#########################################################################
+//# F I L E R E C E I V E D I A L O G
+//#########################################################################
+
+class FileReceiveDialog : public Gtk::Dialog
+{
+public:
+
+ FileReceiveDialog(PedroGui &par,
+ const DOMString &jidArg,
+ const DOMString &iqIdArg,
+ const DOMString &streamIdArg,
+ const DOMString &offeredNameArg,
+ const DOMString &descArg,
+ long sizeArg,
+ const DOMString &hashArg
+ ) : parent(par)
+ {
+ jid = jidArg;
+ iqId = iqIdArg;
+ streamId = streamIdArg;
+ offeredName = offeredNameArg;
+ desc = descArg;
+ fileSize = sizeArg;
+ hash = hashArg;
+ doSetup();
+ }
+
+ virtual ~FileReceiveDialog()
+ {}
+
+ DOMString getJid()
+ { return jid; }
+ DOMString getIq()
+ { return iqId; }
+ DOMString getStreamId()
+ { return streamId; }
+ DOMString getOfferedName()
+ { return offeredName; }
+ DOMString getFileName()
+ { return fileName; }
+ DOMString getDescription()
+ { return desc; }
+ long getSize()
+ { return fileSize; }
+ DOMString getHash()
+ { return hash; }
+
+private:
+
+ void okCallback();
+ void cancelCallback();
+ void buttonCallback();
+
+ bool doSetup();
+
+ Gtk::Table table;
+
+ Gtk::Label jidLabel;
+ Gtk::Entry jidField;
+ Gtk::Label offeredLabel;
+ Gtk::Entry offeredField;
+ Gtk::Label descLabel;
+ Gtk::Entry descField;
+ Gtk::Label sizeLabel;
+ Gtk::Entry sizeField;
+ Gtk::Label hashLabel;
+ Gtk::Entry hashField;
+
+ Gtk::Entry fileNameField;
+
+ Gtk::Button selectFileButton;
+
+ DOMString jid;
+ DOMString iqId;
+ DOMString streamId;
+ DOMString offeredName;
+ DOMString desc;
+ long fileSize;
+ DOMString hash;
+
+ DOMString fileName;
+
+ PedroGui &parent;
+};
+
+
+
+//#########################################################################
+//# M A I N W I N D O W
+//#########################################################################
+
+class PedroGui : public Gtk::Window
+{
+public:
+
+ PedroGui();
+
+ virtual ~PedroGui();
+
+ virtual void error(const char *fmt, ...);
+
+ virtual void status(const char *fmt, ...);
+
+ //Let everyone share this
+ XmppClient client;
+
+
+ /**
+ *
+ */
+ virtual void doEvent(const XmppEvent &event);
+
+ /**
+ *
+ */
+ bool PedroGui::checkEventQueue();
+
+
+ bool chatCreate(const DOMString &userJid);
+ bool chatDelete(const DOMString &userJid);
+ bool chatDeleteAll();
+ bool chatMessage(const DOMString &jid, const DOMString &data);
+
+ bool groupChatCreate(const DOMString &groupJid,
+ const DOMString &nick);
+ bool groupChatDelete(const DOMString &groupJid,
+ const DOMString &nick);
+ bool groupChatDeleteAll();
+ bool groupChatMessage(const DOMString &groupJid,
+ const DOMString &from, const DOMString &data);
+ bool groupChatPresence(const DOMString &groupJid,
+ const DOMString &nick,
+ bool presence,
+ const DOMString &show,
+ const DOMString &status);
+
+ void doChat(const DOMString &jid);
+ void doSendFile(const DOMString &jid);
+ void doReceiveFile(const DOMString &jid,
+ const DOMString &iqId,
+ const DOMString &streamId,
+ const DOMString &offeredName,
+ const DOMString &desc,
+ long size,
+ const DOMString &hash);
+
+private:
+
+
+ //# File menu
+ void connectCallback();
+ void chatCallback();
+ void groupChatCallback();
+ void disconnectCallback();
+ void quitCallback();
+
+ //# Edit menu
+ void fontCallback();
+ void colorCallback();
+
+ //# Transfer menu
+ void sendFileCallback();
+
+ //# Help menu
+ void aboutCallback();
+
+ bool doSetup();
+
+ Gtk::VBox mainBox;
+
+ Pango::FontDescription fontDesc;
+ Gdk::Color foregroundColor;
+ Gdk::Color backgroundColor;
+
+ Gtk::VPaned vPaned;
+ MessageList messageList;
+ Roster roster;
+
+ Glib::RefPtr<Gtk::UIManager> uiManager;
+ void actionEnable(const DOMString &name, bool val);
+
+ std::vector<ChatWindow *>chats;
+
+ std::vector<GroupChatWindow *>groupChats;
+
+ static const int writeBufLen = 2048;
+
+ char writeBuf[writeBufLen];
+};
+
+
+} //namespace Pedro
+
+#endif /* __PEDROGUI_H__ */
+//#########################################################################
+//# E N D O F F I L E
+//#########################################################################
+
+
diff --git a/src/pedro/pedromain.cpp b/src/pedro/pedromain.cpp
--- /dev/null
+++ b/src/pedro/pedromain.cpp
@@ -0,0 +1,32 @@
+#include <stdio.h>
+
+#include "pedrogui.h"
+
+int main(int argc, char *argv[])
+{
+ Gtk::Main kit(argc, argv);
+
+ Pedro::PedroGui window;
+
+ kit.run(window);
+
+ return 0;
+}
+
+
+
+
+#ifdef __WIN32__
+#include <windows.h>
+
+
+extern "C" int __export WINAPI
+WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
+ char *lpszCmdLine, int nCmdShow)
+{
+ int ret = main (__argc, __argv);
+ return ret;
+}
+
+#endif
+
diff --git a/src/pedro/pedroxmpp.cpp b/src/pedro/pedroxmpp.cpp
--- /dev/null
+++ b/src/pedro/pedroxmpp.cpp
@@ -0,0 +1,4842 @@
+/*
+ * Implementation the Pedro mini-XMPP client
+ *
+ * Authors:
+ * Bob Jamison
+ *
+ * Copyright (C) 2005 Bob Jamison
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <sys/stat.h>
+
+#include "pedroxmpp.h"
+#include "pedrodom.h"
+
+#ifdef __WIN32__
+
+#include <windows.h>
+
+#else /* UNIX */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include <pthread.h>
+
+#endif
+
+#ifdef HAVE_SSL
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#endif
+
+
+namespace Pedro
+{
+
+//########################################################################
+//########################################################################
+//### U T I L I T Y
+//########################################################################
+//########################################################################
+
+
+//########################################################################
+//# B A S E 6 4
+//########################################################################
+
+//#################
+//# ENCODER
+//#################
+
+
+static char *base64encode =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * This class is for Base-64 encoding
+ */
+class Base64Encoder
+{
+
+public:
+
+ Base64Encoder()
+ {
+ reset();
+ }
+
+ virtual ~Base64Encoder()
+ {}
+
+ virtual void reset()
+ {
+ outBuf = 0L;
+ bitCount = 0;
+ buf = "";
+ }
+
+ virtual void append(int ch);
+
+ virtual void append(char *str);
+
+ virtual void append(unsigned char *str, int len);
+
+ virtual void append(const DOMString &str);
+
+ virtual DOMString finish();
+
+ static DOMString encode(const DOMString &str);
+
+
+private:
+
+
+ unsigned long outBuf;
+
+ int bitCount;
+
+ DOMString buf;
+
+};
+
+
+
+/**
+ * Writes the specified byte to the output buffer
+ */
+void Base64Encoder::append(int ch)
+{
+ outBuf <<= 8;
+ outBuf |= (ch & 0xff);
+ bitCount += 8;
+ if (bitCount >= 24)
+ {
+ int indx = (int)((outBuf & 0x00fc0000L) >> 18);
+ int obyte = (int)base64encode[indx & 63];
+ buf.push_back(obyte);
+
+ indx = (int)((outBuf & 0x0003f000L) >> 12);
+ obyte = (int)base64encode[indx & 63];
+ buf.push_back(obyte);
+
+ indx = (int)((outBuf & 0x00000fc0L) >> 6);
+ obyte = (int)base64encode[indx & 63];
+ buf.push_back(obyte);
+
+ indx = (int)((outBuf & 0x0000003fL) );
+ obyte = (int)base64encode[indx & 63];
+ buf.push_back(obyte);
+
+ bitCount = 0;
+ outBuf = 0L;
+ }
+}
+
+/**
+ * Writes the specified string to the output buffer
+ */
+void Base64Encoder::append(char *str)
+{
+ while (*str)
+ append((int)*str++);
+}
+
+/**
+ * Writes the specified string to the output buffer
+ */
+void Base64Encoder::append(unsigned char *str, int len)
+{
+ while (len>0)
+ {
+ append((int)*str++);
+ len--;
+ }
+}
+
+/**
+ * Writes the specified string to the output buffer
+ */
+void Base64Encoder::append(const DOMString &str)
+{
+ append((char *)str.c_str());
+}
+
+/**
+ * Closes this output stream and releases any system resources
+ * associated with this stream.
+ */
+DOMString Base64Encoder::finish()
+{
+ //get any last bytes (1 or 2) out of the buffer
+ if (bitCount == 16)
+ {
+ outBuf <<= 2; //pad to make 18 bits
+
+ int indx = (int)((outBuf & 0x0003f000L) >> 12);
+ int obyte = (int)base64encode[indx & 63];
+ buf.push_back(obyte);
+
+ indx = (int)((outBuf & 0x00000fc0L) >> 6);
+ obyte = (int)base64encode[indx & 63];
+ buf.push_back(obyte);
+
+ indx = (int)((outBuf & 0x0000003fL) );
+ obyte = (int)base64encode[indx & 63];
+ buf.push_back(obyte);
+
+ buf.push_back('=');
+ }
+ else if (bitCount == 8)
+ {
+ outBuf <<= 4; //pad to make 12 bits
+
+ int indx = (int)((outBuf & 0x00000fc0L) >> 6);
+ int obyte = (int)base64encode[indx & 63];
+ buf.push_back(obyte);
+
+ indx = (int)((outBuf & 0x0000003fL) );
+ obyte = (int)base64encode[indx & 63];
+ buf.push_back(obyte);
+
+ buf.push_back('=');
+ buf.push_back('=');
+ }
+
+ DOMString ret = buf;
+ reset();
+ return ret;
+}
+
+
+DOMString Base64Encoder::encode(const DOMString &str)
+{
+ Base64Encoder encoder;
+ encoder.append(str);
+ DOMString ret = encoder.finish();
+ return ret;
+}
+
+
+
+//#################
+//# DECODER
+//#################
+
+static int base64decode[] =
+{
+/*00*/ -1, -1, -1, -1, -1, -1, -1, -1,
+/*08*/ -1, -1, -1, -1, -1, -1, -1, -1,
+/*10*/ -1, -1, -1, -1, -1, -1, -1, -1,
+/*18*/ -1, -1, -1, -1, -1, -1, -1, -1,
+/*20*/ -1, -1, -1, -1, -1, -1, -1, -1,
+/*28*/ -1, -1, -1, 62, -1, -1, -1, 63,
+/*30*/ 52, 53, 54, 55, 56, 57, 58, 59,
+/*38*/ 60, 61, -1, -1, -1, -1, -1, -1,
+/*40*/ -1, 0, 1, 2, 3, 4, 5, 6,
+/*48*/ 7, 8, 9, 10, 11, 12, 13, 14,
+/*50*/ 15, 16, 17, 18, 19, 20, 21, 22,
+/*58*/ 23, 24, 25, -1, -1, -1, -1, -1,
+/*60*/ -1, 26, 27, 28, 29, 30, 31, 32,
+/*68*/ 33, 34, 35, 36, 37, 38, 39, 40,
+/*70*/ 41, 42, 43, 44, 45, 46, 47, 48,
+/*78*/ 49, 50, 51, -1, -1, -1, -1, -1
+};
+
+class Base64Decoder
+{
+public:
+ Base64Decoder()
+ {
+ reset();
+ }
+
+ virtual ~Base64Decoder()
+ {}
+
+ virtual void reset()
+ {
+ inCount = 0;
+ buf.clear();
+ }
+
+
+ virtual void append(int ch);
+
+ virtual void append(char *str);
+
+ virtual void append(const DOMString &str);
+
+ std::vector<unsigned char> finish();
+
+ static std::vector<unsigned char> decode(const DOMString &str);
+
+ static DOMString decodeToString(const DOMString &str);
+
+private:
+
+ int inBytes[4];
+ int inCount;
+ std::vector<unsigned char> buf;
+};
+
+/**
+ * Appends one char to the decoder
+ */
+void Base64Decoder::append(int ch)
+{
+ if (isspace(ch))
+ return;
+ else if (ch == '=') //padding
+ {
+ inBytes[inCount++] = 0;
+ }
+ else
+ {
+ int byteVal = base64decode[ch & 0x7f];
+ //printf("char:%c %d\n", ch, byteVal);
+ if (byteVal < 0)
+ {
+ //Bad lookup value
+ }
+ inBytes[inCount++] = byteVal;
+ }
+
+ if (inCount >=4 )
+ {
+ unsigned char b0 = ((inBytes[0]<<2) & 0xfc) | ((inBytes[1]>>4) & 0x03);
+ unsigned char b1 = ((inBytes[1]<<4) & 0xf0) | ((inBytes[2]>>2) & 0x0f);
+ unsigned char b2 = ((inBytes[2]<<6) & 0xc0) | ((inBytes[3] ) & 0x3f);
+ buf.push_back(b0);
+ buf.push_back(b1);
+ buf.push_back(b2);
+ inCount = 0;
+ }
+
+}
+
+void Base64Decoder::append(char *str)
+{
+ while (*str)
+ append((int)*str++);
+}
+
+void Base64Decoder::append(const DOMString &str)
+{
+ append((char *)str.c_str());
+}
+
+std::vector<unsigned char> Base64Decoder::finish()
+{
+ std::vector<unsigned char> ret = buf;
+ reset();
+ return ret;
+}
+
+std::vector<unsigned char> Base64Decoder::decode(const DOMString &str)
+{
+ Base64Decoder decoder;
+ decoder.append(str);
+ std::vector<unsigned char> ret = decoder.finish();
+ return ret;
+}
+
+DOMString Base64Decoder::decodeToString(const DOMString &str)
+{
+ Base64Decoder decoder;
+ decoder.append(str);
+ std::vector<unsigned char> ret = decoder.finish();
+ DOMString buf;
+ for (unsigned int i=0 ; i<ret.size() ; i++)
+ buf.push_back(ret[i]);
+ return buf;
+}
+
+
+
+//########################################################################
+//# S H A 1
+//########################################################################
+
+class Sha1
+{
+public:
+
+ /**
+ *
+ */
+ Sha1()
+ { init(); }
+
+ /**
+ *
+ */
+ virtual ~Sha1()
+ {}
+
+
+ /**
+ * Static convenience method. This would be the most commonly used
+ * version;
+ * @parm digest points to a bufer of 20 unsigned chars
+ */
+ static void hash(unsigned char *dataIn, int len, unsigned char *digest);
+
+ /**
+ * Static convenience method. This will fill a string with the hex
+ * codex string.
+ */
+ static DOMString hashHex(unsigned char *dataIn, int len);
+
+ /**
+ * Initialize the context (also zeroizes contents)
+ */
+ virtual void init();
+
+ /**
+ *
+ */
+ virtual void append(unsigned char *dataIn, int len);
+
+ /**
+ *
+ * @parm digest points to a bufer of 20 unsigned chars
+ */
+ virtual void finish(unsigned char *digest);
+
+
+private:
+
+ void hashblock();
+
+ unsigned long H[5];
+ unsigned long W[80];
+ unsigned long sizeHi,sizeLo;
+ int lenW;
+
+};
+
+
+
+void Sha1::hash(unsigned char *dataIn, int len, unsigned char *digest)
+{
+ Sha1 sha1;
+ sha1.append(dataIn, len);
+ sha1.finish(digest);
+}
+
+static char *sha1hex = "0123456789abcdef";
+
+DOMString Sha1::hashHex(unsigned char *dataIn, int len)
+{
+ unsigned char hashout[20];
+ hash(dataIn, len, hashout);
+ DOMString ret;
+ for (int i=0 ; i<20 ; i++)
+ {
+ unsigned char ch = hashout[i];
+ ret.push_back(sha1hex[ (ch>>4) & 15 ]);
+ ret.push_back(sha1hex[ ch & 15 ]);
+ }
+ return ret;
+}
+
+
+void Sha1::init()
+{
+
+ lenW = 0;
+ sizeHi = 0;
+ sizeLo = 0;
+
+ // Initialize H with the magic constants (see FIPS180 for constants)
+ H[0] = 0x67452301L;
+ H[1] = 0xefcdab89L;
+ H[2] = 0x98badcfeL;
+ H[3] = 0x10325476L;
+ H[4] = 0xc3d2e1f0L;
+
+ for (int i = 0; i < 80; i++)
+ W[i] = 0;
+}
+
+
+void Sha1::append(unsigned char *dataIn, int len)
+{
+ // Read the data into W and process blocks as they get full
+ for (int i = 0; i < len; i++)
+ {
+ W[lenW / 4] <<= 8;
+ W[lenW / 4] |= (unsigned long)dataIn[i];
+ if ((++lenW) % 64 == 0)
+ {
+ hashblock();
+ lenW = 0;
+ }
+ sizeLo += 8;
+ sizeHi += (sizeLo < 8);
+ }
+}
+
+
+void Sha1::finish(unsigned char hashout[20])
+{
+ unsigned char pad0x80 = 0x80;
+ unsigned char pad0x00 = 0x00;
+ unsigned char padlen[8];
+
+ // Pad with a binary 1 (e.g. 0x80), then zeroes, then length
+ padlen[0] = (unsigned char)((sizeHi >> 24) & 255);
+ padlen[1] = (unsigned char)((sizeHi >> 16) & 255);
+ padlen[2] = (unsigned char)((sizeHi >> 8) & 255);
+ padlen[3] = (unsigned char)((sizeHi >> 0) & 255);
+ padlen[4] = (unsigned char)((sizeLo >> 24) & 255);
+ padlen[5] = (unsigned char)((sizeLo >> 16) & 255);
+ padlen[6] = (unsigned char)((sizeLo >> 8) & 255);
+ padlen[7] = (unsigned char)((sizeLo >> 0) & 255);
+
+ append(&pad0x80, 1);
+
+ while (lenW != 56)
+ append(&pad0x00, 1);
+ append(padlen, 8);
+
+ // Output hash
+ for (int i = 0; i < 20; i++)
+ {
+ hashout[i] = (unsigned char)(H[i / 4] >> 24);
+ H[i / 4] <<= 8;
+ }
+
+ // Re-initialize the context (also zeroizes contents)
+ init();
+}
+
+
+#define SHA_ROTL(X,n) ((((X) << (n)) | ((X) >> (32-(n)))) & 0xffffffffL)
+
+void Sha1::hashblock()
+{
+
+ for (int t = 16; t <= 79; t++)
+ W[t] = SHA_ROTL(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1);
+
+ unsigned long A = H[0];
+ unsigned long B = H[1];
+ unsigned long C = H[2];
+ unsigned long D = H[3];
+ unsigned long E = H[4];
+
+ unsigned long TEMP;
+
+ for (int t = 0; t <= 19; t++)
+ {
+ TEMP = (SHA_ROTL(A,5) + (((C^D)&B)^D) +
+ E + W[t] + 0x5a827999L) & 0xffffffffL;
+ E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
+ }
+ for (int t = 20; t <= 39; t++)
+ {
+ TEMP = (SHA_ROTL(A,5) + (B^C^D) +
+ E + W[t] + 0x6ed9eba1L) & 0xffffffffL;
+ E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
+ }
+ for (int t = 40; t <= 59; t++)
+ {
+ TEMP = (SHA_ROTL(A,5) + ((B&C)|(D&(B|C))) +
+ E + W[t] + 0x8f1bbcdcL) & 0xffffffffL;
+ E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
+ }
+ for (int t = 60; t <= 79; t++)
+ {
+ TEMP = (SHA_ROTL(A,5) + (B^C^D) +
+ E + W[t] + 0xca62c1d6L) & 0xffffffffL;
+ E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
+ }
+
+ H[0] += A;
+ H[1] += B;
+ H[2] += C;
+ H[3] += D;
+ H[4] += E;
+}
+
+
+
+
+
+//########################################################################
+//# M D 5
+//########################################################################
+
+class Md5
+{
+public:
+
+ /**
+ *
+ */
+ Md5()
+ { init(); }
+
+ /**
+ *
+ */
+ virtual ~Md5()
+ {}
+
+ /**
+ * Static convenience method.
+ * @parm digest points to an buffer of 16 unsigned chars
+ */
+ static void hash(unsigned char *dataIn,
+ unsigned long len, unsigned char *digest);
+
+ static DOMString Md5::hashHex(unsigned char *dataIn, unsigned long len);
+
+ /**
+ * Initialize the context (also zeroizes contents)
+ */
+ virtual void init();
+
+ /**
+ *
+ */
+ virtual void append(unsigned char *dataIn, unsigned long len);
+
+ /**
+ *
+ */
+ virtual void append(const DOMString &str);
+
+ /**
+ * Finalize and output the hash.
+ * @parm digest points to an buffer of 16 unsigned chars
+ */
+ virtual void finish(unsigned char *digest);
+
+
+ /**
+ * Same as above , but hex to an output String
+ */
+ virtual DOMString finishHex();
+
+private:
+
+ void transform(unsigned long *buf, unsigned long *in);
+
+ unsigned long buf[4];
+ unsigned long bits[2];
+ unsigned char in[64];
+
+};
+
+
+
+
+void Md5::hash(unsigned char *dataIn, unsigned long len, unsigned char *digest)
+{
+ Md5 md5;
+ md5.append(dataIn, len);
+ md5.finish(digest);
+}
+
+DOMString Md5::hashHex(unsigned char *dataIn, unsigned long len)
+{
+ Md5 md5;
+ md5.append(dataIn, len);
+ DOMString ret = md5.finishHex();
+ return ret;
+}
+
+
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+/*
+static void byteReverse(unsigned char *buf, unsigned long longs)
+{
+ do
+ {
+ unsigned long t = (unsigned long)
+ ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ *(unsigned long *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+*/
+
+static void md5_memcpy(void *dest, void *src, int n)
+{
+ unsigned char *s1 = (unsigned char *)dest;
+ unsigned char *s2 = (unsigned char *)src;
+ while (n--)
+ *s1++ = *s2++;
+}
+
+static void md5_memset(void *dest, char v, int n)
+{
+ unsigned char *s = (unsigned char *)dest;
+ while (n--)
+ *s++ = v;
+}
+
+/**
+ * Initialize MD5 polynomials and storage
+ */
+void Md5::init()
+{
+ buf[0] = 0x67452301;
+ buf[1] = 0xefcdab89;
+ buf[2] = 0x98badcfe;
+ buf[3] = 0x10325476;
+
+ bits[0] = 0;
+ bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void Md5::append(unsigned char *source, unsigned long len)
+{
+
+ // Update bitcount
+ unsigned long t = bits[0];
+ if ((bits[0] = t + ((unsigned long) len << 3)) < t)
+ bits[1]++;// Carry from low to high
+ bits[1] += len >> 29;
+
+ //Bytes already in shsInfo->data
+ t = (t >> 3) & 0x3f;
+
+
+ // Handle any leading odd-sized chunks
+ if (t)
+ {
+ unsigned char *p = (unsigned char *) in + t;
+ t = 64 - t;
+ if (len < t)
+ {
+ md5_memcpy(p, source, len);
+ return;
+ }
+ md5_memcpy(p, source, t);
+ //byteReverse(in, 16);
+ transform(buf, (unsigned long *) in);
+ source += t;
+ len -= t;
+ }
+
+ // Process data in 64-byte chunks
+ while (len >= 64)
+ {
+ md5_memcpy(in, source, 64);
+ //byteReverse(in, 16);
+ transform(buf, (unsigned long *) in);
+ source += 64;
+ len -= 64;
+ }
+
+ // Handle any remaining bytes of data.
+ md5_memcpy(in, source, len);
+}
+
+/*
+ * Update context to reflect the concatenation of another string
+ */
+void Md5::append(const DOMString &str)
+{
+ append((unsigned char *)str.c_str(), str.size());
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void Md5::finish(unsigned char *digest)
+{
+ // Compute number of bytes mod 64
+ unsigned int count = (bits[0] >> 3) & 0x3F;
+
+ // Set the first char of padding to 0x80.
+ // This is safe since there is always at least one byte free
+ unsigned char *p = in + count;
+ *p++ = 0x80;
+
+ // Bytes of padding needed to make 64 bytes
+ count = 64 - 1 - count;
+
+ // Pad out to 56 mod 64
+ if (count < 8)
+ {
+ // Two lots of padding: Pad the first block to 64 bytes
+ md5_memset(p, 0, count);
+ //byteReverse(in, 16);
+ transform(buf, (unsigned long *) in);
+
+ // Now fill the next block with 56 bytes
+ md5_memset(in, 0, 56);
+ }
+ else
+ {
+ // Pad block to 56 bytes
+ md5_memset(p, 0, count - 8);
+ }
+ //byteReverse(in, 14);
+
+ // Append length in bits and transform
+ ((unsigned long *) in)[14] = bits[0];
+ ((unsigned long *) in)[15] = bits[1];
+
+ transform(buf, (unsigned long *) in);
+ //byteReverse((unsigned char *) buf, 4);
+ md5_memcpy(digest, buf, 16);
+ init(); // Security! ;-)
+}
+
+static char *md5hex = "0123456789abcdef";
+
+DOMString Md5::finishHex()
+{
+ unsigned char hashout[16];
+ finish(hashout);
+ DOMString ret;
+ for (int i=0 ; i<16 ; i++)
+ {
+ unsigned char ch = hashout[i];
+ ret.push_back(md5hex[ (ch>>4) & 15 ]);
+ ret.push_back(md5hex[ ch & 15 ]);
+ }
+ return ret;
+}
+
+
+
+//# The four core functions - F1 is optimized somewhat
+
+// #define F1(x, y, z) (x & y | ~x & z)
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+// ## This is the central step in the MD5 algorithm.
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ * @parm buf points to an array of 4 unsigned longs
+ * @parm in points to an array of 16 unsigned longs
+ */
+void Md5::transform(unsigned long *buf, unsigned long *in)
+{
+ unsigned long a = buf[0];
+ unsigned long b = buf[1];
+ unsigned long c = buf[2];
+ unsigned long d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[ 2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[ 7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[ 5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[ 3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[ 1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[ 8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[ 6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[ 4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[ 2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[ 9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+
+
+
+
+
+
+//########################################################################
+//########################################################################
+//### T H R E A D
+//########################################################################
+//########################################################################
+
+
+//########################################################################
+//### T H R E A D
+//########################################################################
+
+/**
+ * This is the interface for a delegate class which can
+ * be run by a Thread.
+ * Thread thread(runnable);
+ * thread.start();
+ */
+class Runnable
+{
+public:
+
+ Runnable()
+ {}
+ virtual ~Runnable()
+ {}
+
+ /**
+ * The method of a delegate class which can
+ * be run by a Thread. Thread is completed when this
+ * method is done.
+ */
+ virtual void run() = 0;
+
+};
+
+
+
+/**
+ * A simple wrapper of native threads in a portable class.
+ * It can be used either to execute its own run() method, or
+ * delegate to a Runnable class's run() method.
+ */
+class Thread
+{
+public:
+
+ /**
+ * Create a thread which will execute its own run() method.
+ */
+ Thread()
+ { runnable = NULL ; started = false; }
+
+ /**
+ * Create a thread which will run a Runnable class's run() method.
+ */
+ Thread(const Runnable &runner)
+ { runnable = (Runnable *)&runner; started = false; }
+
+ /**
+ * This does not kill a spawned thread.
+ */
+ virtual ~Thread()
+ {}
+
+ /**
+ * Static method to pause the current thread for a given
+ * number of milliseconds.
+ */
+ static void sleep(unsigned long millis);
+
+ /**
+ * This method will be executed if the Thread was created with
+ * no delegated Runnable class. The thread is completed when
+ * the method is done.
+ */
+ virtual void run()
+ {}
+
+ /**
+ * Starts the thread.
+ */
+ virtual void start();
+
+ /**
+ * Calls either this class's run() method, or that of a Runnable.
+ * A user would normally not call this directly.
+ */
+ virtual void execute()
+ {
+ started = true;
+ if (runnable)
+ runnable->run();
+ else
+ run();
+ }
+
+private:
+
+ Runnable *runnable;
+
+ bool started;
+
+};
+
+
+
+
+
+#ifdef __WIN32__
+
+
+static DWORD WINAPI WinThreadFunction(LPVOID context)
+{
+ Thread *thread = (Thread *)context;
+ thread->execute();
+ return 0;
+}
+
+
+void Thread::start()
+{
+ DWORD dwThreadId;
+ HANDLE hThread = CreateThread(NULL, 0, WinThreadFunction,
+ (LPVOID)this, 0, &dwThreadId);
+ //Make sure the thread is started before 'this' is deallocated
+ while (!started)
+ sleep(10);
+ CloseHandle(hThread);
+}
+
+void Thread::sleep(unsigned long millis)
+{
+ Sleep(millis);
+}
+
+#else /* UNIX */
+
+
+void *PthreadThreadFunction(void *context)
+{
+ Thread *thread = (Thread *)context;
+ thread->execute();
+ return NULL;
+}
+
+
+void Thread::start()
+{
+ pthread_t thread;
+
+ int ret = pthread_create(&thread, NULL,
+ PthreadThreadFunction, (void *)this);
+ if (ret != 0)
+ printf("Thread::start: thread creation failed: %s\n", strerror(ret));
+
+ //Make sure the thread is started before 'this' is deallocated
+ while (!started)
+ sleep(10);
+
+}
+
+void Thread::sleep(unsigned long millis)
+{
+ timespec requested;
+ requested.tv_sec = millis / 1000;
+ requested.tv_nsec = (millis % 1000 ) * 1000000L;
+ nanosleep(&requested, NULL);
+}
+
+#endif
+
+
+//########################################################################
+//########################################################################
+//### S O C K E T
+//########################################################################
+//########################################################################
+
+
+
+
+
+class TcpSocket
+{
+public:
+
+ TcpSocket();
+
+ TcpSocket(const std::string &hostname, int port);
+
+ TcpSocket(const char *hostname, int port);
+
+ TcpSocket(const TcpSocket &other);
+
+ virtual ~TcpSocket();
+
+ bool isConnected();
+
+ void enableSSL(bool val);
+
+ bool connect(const std::string &hostname, int portno);
+
+ bool connect(const char *hostname, int portno);
+
+ bool startTls();
+
+ bool connect();
+
+ bool disconnect();
+
+ bool setReceiveTimeout(unsigned long millis);
+
+ long available();
+
+ bool write(int ch);
+
+ bool write(char *str);
+
+ bool write(const std::string &str);
+
+ int read();
+
+ std::string readLine();
+
+private:
+ void init();
+
+ std::string hostname;
+ int portno;
+ int sock;
+ bool connected;
+
+ bool sslEnabled;
+
+ unsigned long receiveTimeout;
+
+#ifdef HAVE_SSL
+ SSL_CTX *sslContext;
+ SSL *sslStream;
+#endif
+
+};
+
+
+
+//#########################################################################
+//# U T I L I T Y
+//#########################################################################
+
+static void mybzero(void *s, size_t n)
+{
+ unsigned char *p = (unsigned char *)s;
+ while (n > 0)
+ {
+ *p++ = (unsigned char)0;
+ n--;
+ }
+}
+
+static void mybcopy(void *src, void *dest, size_t n)
+{
+ unsigned char *p = (unsigned char *)dest;
+ unsigned char *q = (unsigned char *)src;
+ while (n > 0)
+ {
+ *p++ = *q++;
+ n--;
+ }
+}
+
+
+
+//#########################################################################
+//# T C P C O N N E C T I O N
+//#########################################################################
+
+TcpSocket::TcpSocket()
+{
+ init();
+}
+
+
+TcpSocket::TcpSocket(const char *hostnameArg, int port)
+{
+ init();
+ hostname = hostnameArg;
+ portno = port;
+}
+
+TcpSocket::TcpSocket(const std::string &hostnameArg, int port)
+{
+ init();
+ hostname = hostnameArg;
+ portno = port;
+}
+
+
+#ifdef HAVE_SSL
+
+static void cryptoLockCallback(int mode, int type, const char *file, int line)
+{
+ //printf("########### LOCK\n");
+ static int modes[CRYPTO_NUM_LOCKS]; /* = {0, 0, ... } */
+ const char *errstr = NULL;
+
+ int rw = mode & (CRYPTO_READ|CRYPTO_WRITE);
+ if (!((rw == CRYPTO_READ) || (rw == CRYPTO_WRITE)))
+ {
+ errstr = "invalid mode";
+ goto err;
+ }
+
+ if (type < 0 || type >= CRYPTO_NUM_LOCKS)
+ {
+ errstr = "type out of bounds";
+ goto err;
+ }
+
+ if (mode & CRYPTO_LOCK)
+ {
+ if (modes[type])
+ {
+ errstr = "already locked";
+ /* must not happen in a single-threaded program
+ * (would deadlock)
+ */
+ goto err;
+ }
+
+ modes[type] = rw;
+ }
+ else if (mode & CRYPTO_UNLOCK)
+ {
+ if (!modes[type])
+ {
+ errstr = "not locked";
+ goto err;
+ }
+
+ if (modes[type] != rw)
+ {
+ errstr = (rw == CRYPTO_READ) ?
+ "CRYPTO_r_unlock on write lock" :
+ "CRYPTO_w_unlock on read lock";
+ }
+
+ modes[type] = 0;
+ }
+ else
+ {
+ errstr = "invalid mode";
+ goto err;
+ }
+
+ err:
+ if (errstr)
+ {
+ /* we cannot use bio_err here */
+ fprintf(stderr, "openssl (lock_dbg_cb): %s (mode=%d, type=%d) at %s:%d\n",
+ errstr, mode, type, file, line);
+ }
+}
+
+static unsigned long cryptoIdCallback()
+{
+#ifdef __WIN32__
+ unsigned long ret = (unsigned long) GetCurrentThreadId();
+#else
+ unsigned long ret = (unsigned long) pthread_self();
+#endif
+ return ret;
+}
+
+#endif
+
+
+TcpSocket::TcpSocket(const TcpSocket &other)
+{
+ init();
+ sock = other.sock;
+ hostname = other.hostname;
+ portno = other.portno;
+}
+
+static bool tcp_socket_inited = false;
+
+void TcpSocket::init()
+{
+ if (!tcp_socket_inited)
+ {
+#ifdef __WIN32__
+ WORD wVersionRequested = MAKEWORD( 2, 2 );
+ WSADATA wsaData;
+ WSAStartup( wVersionRequested, &wsaData );
+#endif
+#ifdef HAVE_SSL
+ sslStream = NULL;
+ sslContext = NULL;
+ CRYPTO_set_locking_callback(cryptoLockCallback);
+ CRYPTO_set_id_callback(cryptoIdCallback);
+ SSL_library_init();
+ SSL_load_error_strings();
+#endif
+ tcp_socket_inited = true;
+ }
+ sock = -1;
+ connected = false;
+ hostname = "";
+ portno = -1;
+ sslEnabled = false;
+ receiveTimeout = 0;
+}
+
+TcpSocket::~TcpSocket()
+{
+ disconnect();
+}
+
+bool TcpSocket::isConnected()
+{
+ if (!connected || sock < 0)
+ return false;
+ return true;
+}
+
+void TcpSocket::enableSSL(bool val)
+{
+ sslEnabled = val;
+}
+
+
+bool TcpSocket::connect(const char *hostnameArg, int portnoArg)
+{
+ hostname = hostnameArg;
+ portno = portnoArg;
+ return connect();
+}
+
+bool TcpSocket::connect(const std::string &hostnameArg, int portnoArg)
+{
+ hostname = hostnameArg;
+ portno = portnoArg;
+ return connect();
+}
+
+
+
+#ifdef HAVE_SSL
+/*
+static int password_cb(char *buf, int bufLen, int rwflag, void *userdata)
+{
+ char *password = "password";
+ if (bufLen < (int)(strlen(password)+1))
+ return 0;
+
+ strcpy(buf,password);
+ int ret = strlen(password);
+ return ret;
+}
+
+static void infoCallback(const SSL *ssl, int where, int ret)
+{
+ switch (where)
+ {
+ case SSL_CB_ALERT:
+ {
+ printf("## %d SSL ALERT: %s\n", where, SSL_alert_desc_string_long(ret));
+ break;
+ }
+ default:
+ {
+ printf("## %d SSL: %s\n", where, SSL_state_string_long(ssl));
+ break;
+ }
+ }
+}
+*/
+#endif
+
+
+bool TcpSocket::startTls()
+{
+#ifdef HAVE_SSL
+ sslStream = NULL;
+ sslContext = NULL;
+
+ //SSL_METHOD *meth = SSLv23_method();
+ //SSL_METHOD *meth = SSLv3_client_method();
+ SSL_METHOD *meth = TLSv1_client_method();
+ sslContext = SSL_CTX_new(meth);
+ //SSL_CTX_set_info_callback(sslContext, infoCallback);
+
+#if 0
+ char *keyFile = "client.pem";
+ char *caList = "root.pem";
+ /* Load our keys and certificates*/
+ if (!(SSL_CTX_use_certificate_chain_file(sslContext, keyFile)))
+ {
+ fprintf(stderr, "Can't read certificate file\n");
+ disconnect();
+ return false;
+ }
+
+ SSL_CTX_set_default_passwd_cb(sslContext, password_cb);
+
+ if (!(SSL_CTX_use_PrivateKey_file(sslContext, keyFile, SSL_FILETYPE_PEM)))
+ {
+ fprintf(stderr, "Can't read key file\n");
+ disconnect();
+ return false;
+ }
+
+ /* Load the CAs we trust*/
+ if (!(SSL_CTX_load_verify_locations(sslContext, caList, 0)))
+ {
+ fprintf(stderr, "Can't read CA list\n");
+ disconnect();
+ return false;
+ }
+#endif
+
+ /* Connect the SSL socket */
+ sslStream = SSL_new(sslContext);
+ SSL_set_fd(sslStream, sock);
+
+ if (SSL_connect(sslStream)<=0)
+ {
+ fprintf(stderr, "SSL connect error\n");
+ disconnect();
+ return false;
+ }
+
+ sslEnabled = true;
+#endif /*HAVE_SSL*/
+ return true;
+}
+
+
+bool TcpSocket::connect()
+{
+ if (hostname.size()<1)
+ {
+ printf("open: null hostname\n");
+ return false;
+ }
+
+ if (portno<1)
+ {
+ printf("open: bad port number\n");
+ return false;
+ }
+
+ sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (sock < 0)
+ {
+ printf("open: error creating socket\n");
+ return false;
+ }
+
+ char *c_hostname = (char *)hostname.c_str();
+ struct hostent *server = gethostbyname(c_hostname);
+ if (!server)
+ {
+ printf("open: could not locate host '%s'\n", c_hostname);
+ return false;
+ }
+
+ struct sockaddr_in serv_addr;
+ mybzero((char *) &serv_addr, sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ mybcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr,
+ server->h_length);
+ serv_addr.sin_port = htons(portno);
+
+ int ret = ::connect(sock, (const sockaddr *)&serv_addr, sizeof(serv_addr));
+ if (ret < 0)
+ {
+ printf("open: could not connect to host '%s'\n", c_hostname);
+ return false;
+ }
+
+ if (sslEnabled)
+ {
+ if (!startTls())
+ return false;
+ }
+ connected = true;
+ return true;
+}
+
+bool TcpSocket::disconnect()
+{
+ bool ret = true;
+ connected = false;
+#ifdef HAVE_SSL
+ if (sslEnabled)
+ {
+ if (sslStream)
+ {
+ int r = SSL_shutdown(sslStream);
+ switch(r)
+ {
+ case 1:
+ break; /* Success */
+ case 0:
+ case -1:
+ default:
+ //printf("Shutdown failed");
+ ret = false;
+ }
+ SSL_free(sslStream);
+ }
+ if (sslContext)
+ SSL_CTX_free(sslContext);
+ }
+ sslStream = NULL;
+ sslContext = NULL;
+#endif /*HAVE_SSL*/
+
+#ifdef __WIN32__
+ closesocket(sock);
+#else
+ ::close(sock);
+#endif
+ sock = -1;
+ sslEnabled = false;
+
+ return ret;
+}
+
+
+
+bool TcpSocket::setReceiveTimeout(unsigned long millis)
+{
+ receiveTimeout = millis;
+ return true;
+}
+
+/**
+ * For normal sockets, return the number of bytes waiting to be received.
+ * For SSL, just return >0 when something is ready to be read.
+ */
+long TcpSocket::available()
+{
+ if (!isConnected())
+ return -1;
+
+ long count = 0;
+#ifdef __WIN32__
+ if (ioctlsocket(sock, FIONREAD, (unsigned long *)&count) != 0)
+ return -1;
+#else
+ if (ioctl(sock, FIONREAD, &count) != 0)
+ return -1;
+#endif
+ if (count<=0 && sslEnabled)
+ {
+#ifdef HAVE_SSL
+ return SSL_pending(sslStream);
+#endif
+ }
+ return count;
+}
+
+
+
+bool TcpSocket::write(int ch)
+{
+ if (!isConnected())
+ {
+ printf("write: socket closed\n");
+ return false;
+ }
+ unsigned char c = (unsigned char)ch;
+
+ if (sslEnabled)
+ {
+#ifdef HAVE_SSL
+ int r = SSL_write(sslStream, &c, 1);
+ if (r<=0)
+ {
+ switch(SSL_get_error(sslStream, r))
+ {
+ default:
+ printf("SSL write problem");
+ return -1;
+ }
+ }
+#endif
+ }
+ else
+ {
+ if (send(sock, (const char *)&c, 1, 0) < 0)
+ //if (send(sock, &c, 1, 0) < 0)
+ {
+ printf("write: could not send data\n");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool TcpSocket::write(char *str)
+{
+ if (!isConnected())
+ {
+ printf("write(str): socket closed\n");
+ return false;
+ }
+ int len = strlen(str);
+
+ if (sslEnabled)
+ {
+#ifdef HAVE_SSL
+ int r = SSL_write(sslStream, (unsigned char *)str, len);
+ if (r<=0)
+ {
+ switch(SSL_get_error(sslStream, r))
+ {
+ default:
+ printf("SSL write problem");
+ return -1;
+ }
+ }
+#endif
+ }
+ else
+ {
+ if (send(sock, str, len, 0) < 0)
+ //if (send(sock, &c, 1, 0) < 0)
+ {
+ printf("write: could not send data\n");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool TcpSocket::write(const std::string &str)
+{
+ return write((char *)str.c_str());
+}
+
+int TcpSocket::read()
+{
+ if (!isConnected())
+ return -1;
+
+ //We'll use this loop for timeouts, so that SSL and plain sockets
+ //will behave the same way
+ if (receiveTimeout > 0)
+ {
+ unsigned long tim = 0;
+ while (true)
+ {
+ int avail = available();
+ if (avail > 0)
+ break;
+ if (tim >= receiveTimeout)
+ return -2;
+ Thread::sleep(20);
+ tim += 20;
+ }
+ }
+
+ //check again
+ if (!isConnected())
+ return -1;
+
+ unsigned char ch;
+ if (sslEnabled)
+ {
+#ifdef HAVE_SSL
+ if (!sslStream)
+ return -1;
+ int r = SSL_read(sslStream, &ch, 1);
+ unsigned long err = SSL_get_error(sslStream, r);
+ switch (err)
+ {
+ case SSL_ERROR_NONE:
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ return -1;
+ case SSL_ERROR_SYSCALL:
+ printf("SSL read problem(syscall) %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ default:
+ printf("SSL read problem %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+#endif
+ }
+ else
+ {
+ if (recv(sock, (char *)&ch, 1, 0) <= 0)
+ {
+ printf("read: could not receive data\n");
+ disconnect();
+ return -1;
+ }
+ }
+ return (int)ch;
+}
+
+std::string TcpSocket::readLine()
+{
+ std::string ret;
+
+ while (isConnected())
+ {
+ int ch = read();
+ if (ch<0)
+ return ret;
+ if (ch=='\r' || ch=='\n')
+ return ret;
+ ret.push_back((char)ch);
+ }
+
+ return ret;
+}
+
+
+//########################################################################
+//########################################################################
+//### X M P P
+//########################################################################
+//########################################################################
+
+
+
+
+//########################################################################
+//# X M P P E V E N T
+//########################################################################
+
+
+XmppEvent::XmppEvent(int type)
+{
+ eventType = type;
+ presence = false;
+ dom = NULL;
+}
+
+XmppEvent::XmppEvent(const XmppEvent &other)
+{
+ assign(other);
+}
+
+XmppEvent &XmppEvent::operator=(const XmppEvent &other)
+{
+ assign(other);
+ return (*this);
+}
+
+XmppEvent::~XmppEvent()
+{
+ if (dom)
+ delete dom;
+}
+
+void XmppEvent::assign(const XmppEvent &other)
+{
+ eventType = other.eventType;
+ presence = other.presence;
+ status = other.status;
+ show = other.show;
+ to = other.to;
+ from = other.from;
+ group = other.group;
+ data = other.data;
+ fileName = other.fileName;
+ fileDesc = other.fileDesc;
+ fileSize = other.fileSize;
+ fileHash = other.fileHash;
+ setDOM(other.dom);
+}
+
+int XmppEvent::getType() const
+{
+ return eventType;
+}
+
+DOMString XmppEvent::getIqId() const
+{
+ return iqId;
+}
+
+void XmppEvent::setIqId(const DOMString &val)
+{
+ iqId = val;
+}
+
+DOMString XmppEvent::getStreamId() const
+{
+ return streamId;
+}
+
+void XmppEvent::setStreamId(const DOMString &val)
+{
+ streamId = val;
+}
+
+bool XmppEvent::getPresence() const
+{
+ return presence;
+}
+
+void XmppEvent::setPresence(bool val)
+{
+ presence = val;
+}
+
+DOMString XmppEvent::getShow() const
+{
+ return show;
+}
+
+void XmppEvent::setShow(const DOMString &val)
+{
+ show = val;
+}
+
+DOMString XmppEvent::getStatus() const
+{
+ return status;
+}
+
+void XmppEvent::setStatus(const DOMString &val)
+{
+ status = val;
+}
+
+DOMString XmppEvent::getTo() const
+{
+ return to;
+}
+
+void XmppEvent::setTo(const DOMString &val)
+{
+ to = val;
+}
+
+DOMString XmppEvent::getFrom() const
+{
+ return from;
+}
+
+void XmppEvent::setFrom(const DOMString &val)
+{
+ from = val;
+}
+
+DOMString XmppEvent::getGroup() const
+{
+ return group;
+}
+
+void XmppEvent::setGroup(const DOMString &val)
+{
+ group = val;
+}
+
+DOMString XmppEvent::getData() const
+{
+ return data;
+}
+
+void XmppEvent::setData(const DOMString &val)
+{
+ data = val;
+}
+
+DOMString XmppEvent::getFileName() const
+{
+ return fileName;
+}
+
+void XmppEvent::setFileName(const DOMString &val)
+{
+ fileName = val;
+}
+
+DOMString XmppEvent::getFileDesc() const
+{
+ return fileDesc;
+}
+
+void XmppEvent::setFileDesc(const DOMString &val)
+{
+ fileDesc = val;
+}
+
+long XmppEvent::getFileSize() const
+{
+ return fileSize;
+}
+
+void XmppEvent::setFileSize(long val)
+{
+ fileSize = val;
+}
+
+DOMString XmppEvent::getFileHash() const
+{
+ return fileHash;
+}
+
+void XmppEvent::setFileHash(const DOMString &val)
+{
+ fileHash = val;
+}
+
+Element *XmppEvent::getDOM() const
+{
+ return dom;
+}
+
+void XmppEvent::setDOM(const Element *val)
+{
+ if (!val)
+ dom = NULL;
+ else
+ dom = ((Element *)val)->clone();
+}
+
+
+std::vector<XmppUser> XmppEvent::getUserList() const
+{
+ return userList;
+}
+
+void XmppEvent::setUserList(const std::vector<XmppUser> &val)
+{
+ userList = val;
+}
+
+//########################################################################
+//# X M P P E V E N T T A R G E T
+//########################################################################
+
+//###########################
+//# CONSTRUCTORS
+//###########################
+
+XmppEventTarget::XmppEventTarget()
+{
+ eventQueueEnabled = false;
+}
+
+
+XmppEventTarget::XmppEventTarget(const XmppEventTarget &other)
+{
+ listeners = other.listeners;
+ eventQueueEnabled = other.eventQueueEnabled;
+}
+
+XmppEventTarget::~XmppEventTarget()
+{
+}
+
+
+//###########################
+//# M E S S A G E S
+//###########################
+
+void XmppEventTarget::error(char *fmt, ...)
+{
+ va_list args;
+ va_start(args,fmt);
+ vsnprintf(targetWriteBuf, targetWriteBufLen, fmt, args);
+ va_end(args) ;
+ printf("Error:%s\n", targetWriteBuf);
+ XmppEvent evt(XmppEvent::EVENT_ERROR);
+ evt.setData(targetWriteBuf);
+ dispatchXmppEvent(evt);
+}
+
+void XmppEventTarget::status(char *fmt, ...)
+{
+ va_list args;
+ va_start(args,fmt);
+ vsnprintf(targetWriteBuf, targetWriteBufLen, fmt, args);
+ va_end(args) ;
+ //printf("Status:%s\n", targetWriteBuf);
+ XmppEvent evt(XmppEvent::EVENT_STATUS);
+ evt.setData(targetWriteBuf);
+ dispatchXmppEvent(evt);
+}
+
+
+
+//###########################
+//# L I S T E N E R S
+//###########################
+
+void XmppEventTarget::dispatchXmppEvent(const XmppEvent &event)
+{
+ std::vector<XmppEventListener *>::iterator iter;
+ for (iter = listeners.begin(); iter != listeners.end() ; iter++)
+ (*iter)->processXmppEvent(event);
+ if (eventQueueEnabled)
+ eventQueue.push_back(event);
+}
+
+void XmppEventTarget::addXmppEventListener(const XmppEventListener &listener)
+{
+ XmppEventListener *lsnr = (XmppEventListener *)&listener;
+ std::vector<XmppEventListener *>::iterator iter;
+ for (iter = listeners.begin(); iter != listeners.end() ; iter++)
+ if (*iter == lsnr)
+ return;
+ listeners.push_back(lsnr);
+}
+
+void XmppEventTarget::removeXmppEventListener(const XmppEventListener &listener)
+{
+ XmppEventListener *lsnr = (XmppEventListener *)&listener;
+ std::vector<XmppEventListener *>::iterator iter;
+ for (iter = listeners.begin(); iter != listeners.end() ; iter++)
+ if (*iter == lsnr)
+ listeners.erase(iter);
+}
+
+void XmppEventTarget::clearXmppEventListeners()
+{
+ listeners.clear();
+}
+
+
+//###########################
+//# E V E N T Q U E U E
+//###########################
+
+void XmppEventTarget::eventQueueEnable(bool val)
+{
+ eventQueueEnabled = val;
+ if (!eventQueueEnabled)
+ eventQueue.clear();
+}
+
+int XmppEventTarget::eventQueueAvailable()
+{
+ return eventQueue.size();
+}
+
+XmppEvent XmppEventTarget::eventQueuePop()
+{
+ if (!eventQueueEnabled || eventQueue.size()<1)
+ {
+ XmppEvent dummy(XmppEvent::EVENT_NONE);
+ return dummy;
+ }
+ XmppEvent event = *(eventQueue.begin());
+ eventQueue.erase(eventQueue.begin());
+ return event;
+}
+
+
+//########################################################################
+//# X M P P S T R E A M
+//########################################################################
+
+/**
+ *
+ */
+class XmppStream
+{
+public:
+
+ /**
+ *
+ */
+ XmppStream();
+
+ /**
+ *
+ */
+ virtual ~XmppStream();
+
+ /**
+ *
+ */
+ virtual void reset();
+
+ /**
+ *
+ */
+ virtual int getState();
+
+ /**
+ *
+ */
+ virtual void setState(int val);
+
+ /**
+ *
+ */
+ virtual DOMString getStreamId();
+
+ /**
+ *
+ */
+ void setStreamId(const DOMString &val);
+
+ /**
+ *
+ */
+ virtual DOMString getIqId();
+
+ /**
+ *
+ */
+ void setIqId(const DOMString &val);
+
+ /**
+ *
+ */
+ virtual int getSeqNr();
+
+ /**
+ *
+ */
+ virtual DOMString getPeerId();
+
+ /**
+ *
+ */
+ virtual void setPeerId(const DOMString &val);
+
+ /**
+ *
+ */
+ int available();
+
+ /**
+ *
+ */
+ void receiveData(std::vector<unsigned char> &newData);
+
+ /**
+ *
+ */
+ std::vector<unsigned char> read();
+
+private:
+
+
+ DOMString streamId;
+
+ DOMString iqId;
+
+ DOMString sourceId;
+
+ int state;
+
+ long seqNr;
+
+ std::vector<unsigned char> data;
+};
+
+
+/**
+ *
+ */
+XmppStream::XmppStream()
+{
+ reset();
+}
+
+/**
+ *
+ */
+XmppStream::~XmppStream()
+{
+ reset();
+}
+
+/**
+ *
+ */
+void XmppStream::reset()
+{
+ state = XmppClient::STREAM_AVAILABLE;
+ seqNr = 0;
+ data.clear();
+}
+
+/**
+ *
+ */
+int XmppStream::getState()
+{
+ return state;
+}
+
+/**
+ *
+ */
+void XmppStream::setState(int val)
+{
+ state = val;
+}
+
+/**
+ *
+ */
+DOMString XmppStream::getStreamId()
+{
+ return streamId;
+}
+
+/**
+ *
+ */
+void XmppStream::setStreamId(const DOMString &val)
+{
+ streamId = val;
+}
+
+/**
+ *
+ */
+DOMString XmppStream::getIqId()
+{
+ return iqId;
+}
+
+
+/**
+ *
+ */
+void XmppStream::setIqId(const DOMString &val)
+{
+ iqId = val;
+}
+
+/**
+ * Source or destination JID
+ */
+void XmppStream::setPeerId(const DOMString &val)
+{
+ sourceId = val;
+}
+
+/**
+ * Source or destination JID
+ */
+DOMString XmppStream::getPeerId()
+{
+ return sourceId;
+}
+
+/**
+ * Stream packet sequence number
+ */
+int XmppStream::getSeqNr()
+{
+ seqNr++;
+ if (seqNr >= 65535)
+ seqNr = 0;
+ return seqNr;
+}
+
+/**
+ *
+ */
+int XmppStream::available()
+{
+ return data.size();
+}
+
+/**
+ *
+ */
+void XmppStream::receiveData(std::vector<unsigned char> &newData)
+{
+ std::vector<unsigned char>::iterator iter;
+ for (iter=newData.begin() ; iter!=newData.end() ; iter++)
+ data.push_back(*iter);
+}
+
+/**
+ *
+ */
+std::vector<unsigned char> XmppStream::read()
+{
+ if (state != XmppClient::STREAM_OPEN)
+ {
+ std::vector<unsigned char>dummy;
+ return dummy;
+ }
+ std::vector<unsigned char> ret = data;
+ data.clear();
+ return ret;
+}
+
+
+
+
+
+
+//########################################################################
+//# X M P P C L I E N T
+//########################################################################
+class ReceiverThread : public Runnable
+{
+public:
+
+ ReceiverThread(XmppClient &par) : client(par) {}
+
+ virtual ~ReceiverThread() {}
+
+ void run()
+ { client.receiveAndProcessLoop(); }
+
+private:
+
+ XmppClient &client;
+};
+
+
+//###########################
+//# CONSTRUCTORS
+//###########################
+
+XmppClient::XmppClient()
+{
+ init();
+}
+
+
+XmppClient::XmppClient(const XmppClient &other) : XmppEventTarget(other)
+{
+ init();
+ assign(other);
+}
+
+void XmppClient::assign(const XmppClient &other)
+{
+ msgId = other.msgId;
+ host = other.host;
+ realm = other.realm;
+ port = other.port;
+ username = other.username;
+ password = other.password;
+ resource = other.resource;
+ connected = other.connected;
+ groupChats = other.groupChats;
+}
+
+
+void XmppClient::init()
+{
+ sock = new TcpSocket();
+ msgId = 0;
+ connected = false;
+
+ for (int i=0 ; i<outputStreamCount ; i++)
+ {
+ outputStreams[i] = new XmppStream();
+ }
+ for (int i=0 ; i<inputStreamCount ; i++)
+ {
+ inputStreams[i] = new XmppStream();
+ }
+ for (int i=0 ; i<fileSendCount ; i++)
+ {
+ fileSends[i] = new XmppStream();
+ }
+}
+
+XmppClient::~XmppClient()
+{
+ disconnect();
+ delete sock;
+ for (int i=0 ; i<outputStreamCount ; i++)
+ {
+ delete outputStreams[i];
+ }
+ for (int i=0 ; i<inputStreamCount ; i++)
+ {
+ delete inputStreams[i];
+ }
+ for (int i=0 ; i<fileSendCount ; i++)
+ {
+ delete fileSends[i];
+ }
+ groupChatsClear();
+}
+
+//##############################################
+//# UTILILY
+//##############################################
+
+/**
+ *
+ */
+bool XmppClient::pause(unsigned long millis)
+{
+ Thread::sleep(millis);
+ return true;
+}
+
+
+static int strIndex(const DOMString &str, char *key)
+{
+ unsigned int p = str.find(key);
+ if (p == DOMString::npos)
+ return -1;
+ return p;
+}
+
+
+DOMString XmppClient::toXml(const DOMString &str)
+{
+ return Parser::encode(str);
+}
+
+static DOMString trim(const DOMString &str)
+{
+ unsigned int i;
+ for (i=0 ; i<str.size() ; i++)
+ if (!isspace(str[i]))
+ break;
+ int start = i;
+ for (i=str.size() ; i>0 ; i--)
+ if (!isspace(str[i-1]))
+ break;
+ int end = i;
+ if (start>=end)
+ return "";
+ return str.substr(start, end);
+}
+
+//##############################################
+//# VARIABLES (ones that need special handling)
+//##############################################
+/**
+ *
+ */
+DOMString XmppClient::getUsername()
+{
+ return username;
+}
+
+/**
+ *
+ */
+void XmppClient::setUsername(const DOMString &val)
+{
+ int p = strIndex(val, "@");
+ if (p > 0)
+ {
+ username = val.substr(0, p);
+ realm = val.substr(p+1, jid.size()-p-1);
+ }
+ else
+ {
+ realm = host;
+ username = val;
+ }
+}
+
+//##############################################
+//# CONNECTION
+//##############################################
+
+//#######################
+//# RECEIVING
+//#######################
+DOMString XmppClient::readStanza()
+{
+ int openCount = 0;
+ bool inTag = false;
+ bool slashSeen = false;
+ bool trivialTag = false;
+ bool querySeen = false;
+ bool inQuote = false;
+ bool textSeen = false;
+ DOMString buf;
+
+ while (true)
+ {
+ int ch = sock->read();
+ //printf("%c", ch); fflush(stdout);
+ if (ch<0)
+ {
+ if (ch == -2) //a simple timeout, not an error
+ {
+ //Since we are timed out, let's assume that we
+ //are between chunks of text. Let's reset all states.
+ //printf("-----#### Timeout\n");
+ continue;
+ }
+ else
+ {
+ keepGoing = false;
+ if (!sock->isConnected())
+ {
+ disconnect();
+ return "";
+ }
+ else
+ {
+ error("socket read error");
+ disconnect();
+ return "";
+ }
+ }
+ }
+ buf.push_back(ch);
+ if (ch == '<')
+ {
+ inTag = true;
+ slashSeen = false;
+ querySeen = false;
+ inQuote = false;
+ textSeen = false;
+ trivialTag = false;
+ }
+ else if (ch == '>')
+ {
+ if (!inTag) //unescaped '>' in pcdata? horror
+ continue;
+ inTag = false;
+ if (!trivialTag && !querySeen)
+ {
+ if (slashSeen)
+ openCount--;
+ else
+ openCount++;
+ }
+ //printf("# openCount:%d t:%d q:%d\n",
+ // openCount, trivialTag, querySeen);
+ //check if we are 'balanced', but not a <?version?> tag
+ if (openCount <= 0 && !querySeen)
+ {
+ break;
+ }
+ //we know that this one will be open-ended
+ if (strIndex(buf, "<stream:stream") >= 0)
+ {
+ buf.append("</stream:stream>");
+ break;
+ }
+ }
+ else if (ch == '/')
+ {
+ if (inTag && !inQuote)
+ {
+ slashSeen = true;
+ if (textSeen) // <tagName/> <--looks like this
+ trivialTag = true;
+ }
+ }
+ else if (ch == '?')
+ {
+ if (inTag && !inQuote)
+ querySeen = true;
+ }
+ else if (ch == '"' || ch == '\'')
+ {
+ if (inTag)
+ inQuote = !inQuote;
+ }
+ else
+ {
+ if (inTag && !inQuote && !isspace(ch))
+ textSeen = true;
+ }
+ }
+ return buf;
+}
+
+
+
+static bool isGroupChat(Element *root)
+{
+ if (!root)
+ return false;
+ std::vector<Element *>elems = root->findElements("x");
+ for (unsigned int i=0 ; i<elems.size() ; i++)
+ {
+ DOMString xmlns = elems[i]->getAttribute("xmlns");
+ //printf("### XMLNS ### %s\n", xmlns.c_str());
+ if (strIndex(xmlns, "http://jabber.org/protocol/muc") >=0 )
+ return true;
+ }
+ return false;
+}
+
+
+
+
+static bool parseJid(const DOMString &fullJid,
+ DOMString &jid, DOMString &resource)
+{
+ int p = strIndex(fullJid, "/");
+ if (p < 0)
+ {
+ jid = fullJid;
+ resource = "";
+ return true;
+ }
+ jid = fullJid.substr(0, p);
+ resource = fullJid.substr(p+1, fullJid.size()-p-1);
+ return true;
+}
+
+
+
+
+bool XmppClient::processMessage(Element *root)
+{
+ DOMString from = root->getTagAttribute("message", "from");
+ DOMString to = root->getTagAttribute("message", "to");
+ DOMString type = root->getTagAttribute("message", "type");
+
+ //####Check for embedded namespaces here
+ //# IN BAND BYTESTREAMS
+ DOMString ibbNamespace = "http://jabber.org/protocol/ibb";
+ if (root->getTagAttribute("data", "xmlns") == ibbNamespace)
+ {
+ DOMString streamId = root->getTagAttribute("data", "sid");
+ if (streamId.size() > 0)
+ {
+ for (int i=0 ; i<inputStreamCount ; i++)
+ {
+ XmppStream *ins = inputStreams[i];
+ //printf("##ins:%s streamid:%s\n",
+ // ins->getStreamId().c_str(), streamId.c_str());
+ if (ins->getStreamId() == streamId)
+ {
+ //# We have a winner
+ if (ins->getState() != STREAM_OPEN)
+ {
+ XmppEvent event(XmppEvent::EVENT_ERROR);
+ event.setFrom(from);
+ event.setData("received unrequested stream data");
+ dispatchXmppEvent(event);
+ return true;
+ }
+ DOMString data = root->getTagValue("data");
+ std::vector<unsigned char>binData =
+ Base64Decoder::decode(data);
+ ins->receiveData(binData);
+ }
+ }
+ }
+ }
+
+
+ //#### NORMAL MESSAGES
+ DOMString subject = root->getTagValue("subject");
+ DOMString body = root->getTagValue("body");
+ DOMString thread = root->getTagValue("thread");
+ //##rfc 3921, para 2.4. ignore if no recognizable info
+ if (subject.size() < 1 && body.size()<1 && thread.size()<1)
+ return true;
+
+ if (type == "groupchat")
+ {
+ DOMString fromGid;
+ DOMString fromNick;
+ parseJid(from, fromGid, fromNick);
+ //printf("fromGid:%s fromNick:%s\n",
+ // fromGid.c_str(), fromNick.c_str());
+ DOMString toGid;
+ DOMString toNick;
+ parseJid(to, toGid, toNick);
+ //printf("toGid:%s toNick:%s\n",
+ // toGid.c_str(), toNick.c_str());
+
+ if (fromNick.size() > 0)//normal group message
+ {
+ XmppEvent event(XmppEvent::EVENT_MUC_MESSAGE);
+ event.setGroup(fromGid);
+ event.setFrom(fromNick);
+ event.setData(body);
+ event.setDOM(root);
+ dispatchXmppEvent(event);
+ }
+ else // from the server itself
+ {
+ //note the space before, so it doesnt match 'unlocked'
+ if (strIndex(body, " locked") >= 0)
+ {
+ printf("LOCKED!! ;)\n");
+ char *fmt =
+ "<iq from='%s' id='create%d' to='%s' type='set'>"
+ "<query xmlns='http://jabber.org/protocol/muc#owner'>"
+ "<x xmlns='jabber:x:data' type='submit'/>"
+ "</query></iq>\n";
+ if (!write(fmt, jid.c_str(), msgId++, fromGid.c_str()))
+ return false;
+ }
+ }
+ }
+ else
+ {
+ XmppEvent event(XmppEvent::EVENT_MESSAGE);
+ event.setFrom(from);
+ event.setData(body);
+ event.setDOM(root);
+ dispatchXmppEvent(event);
+ }
+
+ return true;
+}
+
+
+
+
+bool XmppClient::processPresence(Element *root)
+{
+
+ DOMString fullJid = root->getTagAttribute("presence", "from");
+ DOMString to = root->getTagAttribute("presence", "to");
+ DOMString presenceStr = root->getTagAttribute("presence", "type");
+ bool presence = true;
+ if (presenceStr == "unavailable")
+ presence = false;
+ DOMString status = root->getTagValue("status");
+ DOMString show = root->getTagValue("show");
+
+ if (isGroupChat(root))
+ {
+ DOMString fromGid;
+ DOMString fromNick;
+ parseJid(fullJid, fromGid, fromNick);
+ //printf("fromGid:%s fromNick:%s\n",
+ // fromGid.c_str(), fromNick.c_str());
+ DOMString item_jid = root->getTagAttribute("item", "jid");
+ if (item_jid == jid) //Me
+ {
+ if (presence)
+ {
+ groupChatCreate(fromGid);
+ groupChatUserAdd(fromGid, fromNick, "");
+ groupChatUserShow(fromGid, fromNick, "available");
+
+ XmppEvent event(XmppEvent::EVENT_MUC_JOIN);
+ event.setGroup(fromGid);
+ event.setFrom(fromNick);
+ event.setPresence(presence);
+ event.setShow(show);
+ event.setStatus(status);
+ dispatchXmppEvent(event);
+ }
+ else
+ {
+ groupChatDelete(fromGid);
+ groupChatUserDelete(fromGid, fromNick);
+
+ XmppEvent event(XmppEvent::EVENT_MUC_LEAVE);
+ event.setGroup(fromGid);
+ event.setFrom(fromNick);
+ event.setPresence(presence);
+ event.setShow(show);
+ event.setStatus(status);
+ dispatchXmppEvent(event);
+ }
+ }
+ else // someone else
+ {
+ if (presence)
+ {
+ groupChatUserAdd(fromGid, fromNick, "");
+ }
+ else
+ groupChatUserDelete(fromGid, fromNick);
+ groupChatUserShow(fromGid, fromNick, show);
+ XmppEvent event(XmppEvent::EVENT_MUC_PRESENCE);
+ event.setGroup(fromGid);
+ event.setFrom(fromNick);
+ event.setPresence(presence);
+ event.setShow(show);
+ event.setStatus(status);
+ dispatchXmppEvent(event);
+ }
+ }
+ else
+ {
+ DOMString shortJid;
+ DOMString dummy;
+ parseJid(fullJid, shortJid, dummy);
+ rosterShow(shortJid, show); //users in roster do not have resource
+
+ XmppEvent event(XmppEvent::EVENT_PRESENCE);
+ event.setFrom(fullJid);
+ event.setPresence(presence);
+ event.setShow(show);
+ event.setStatus(status);
+ dispatchXmppEvent(event);
+ }
+
+ return true;
+}
+
+
+
+bool XmppClient::processIq(Element *root)
+{
+ DOMString from = root->getTagAttribute("iq", "from");
+ DOMString id = root->getTagAttribute("iq", "id");
+ DOMString type = root->getTagAttribute("iq", "type");
+ DOMString xmlns = root->getTagAttribute("query", "xmlns");
+
+ if (id.size()<1)
+ return true;
+
+ //Group chat
+ if (strIndex(xmlns, "http://jabber.org/protocol/muc") >=0 )
+ {
+ printf("results of MUC query\n");
+ }
+ //printf("###IQ xmlns:%s\n", xmlns.c_str());
+
+ //### FILE TRANSFERS
+ DOMString siNamespace = "http://jabber.org/protocol/si";
+ if (root->getTagAttribute("si", "xmlns") == siNamespace)
+ {
+ if (type == "set")
+ {
+ DOMString streamId = root->getTagAttribute("si", "id");
+ DOMString fname = root->getTagAttribute("file", "name");
+ DOMString sizeStr = root->getTagAttribute("file", "size");
+ DOMString hash = root->getTagAttribute("file", "hash");
+ XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_RECEIVE);
+ event.setFrom(from);
+ event.setIqId(id);
+ event.setStreamId(streamId);
+ event.setFileName(fname);
+ event.setFileHash(hash);
+ event.setFileSize(atol(sizeStr.c_str()));
+ dispatchXmppEvent(event);
+ }
+ else //result
+ {
+ printf("Got result\n");
+ for (int i=0 ; i<fileSendCount ; i++)
+ {
+ XmppStream *outf = fileSends[i];
+ if (outf->getIqId() == id &&
+ from == outf->getPeerId())
+ {
+ if (type == "error")
+ {
+ outf->setState(STREAM_ERROR);
+ error("user '%s' rejected file", from.c_str());
+ return true;
+ }
+ else if (type == "result")
+ {
+ if (outf->getState() == STREAM_OPENING)
+ {
+ XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_ACCEPTED);
+ event.setFrom(from);
+ dispatchXmppEvent(event);
+ outf->setState(STREAM_OPEN);
+ }
+ else if (outf->getState() == STREAM_CLOSING)
+ {
+ outf->setState(STREAM_CLOSED);
+ }
+ return true;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+
+ //### IN-BAND BYTESTREAMS
+ //### Incoming stream requests
+ DOMString ibbNamespace = "http://jabber.org/protocol/ibb";
+ if (root->getTagAttribute("open", "xmlns") == ibbNamespace)
+ {
+ DOMString streamId = root->getTagAttribute("open", "sid");
+ XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_INIT);
+ dispatchXmppEvent(event);
+ if (streamId.size()>0)
+ {
+ for (int i=0 ; i<inputStreamCount ; i++)
+ {
+ XmppStream *ins = inputStreams[i];
+ if (ins->getStreamId() == streamId)
+ {
+ ins->setState(STREAM_OPENING);
+ ins->setIqId(id);
+ return true;
+ }
+ }
+ }
+ return true;
+ }
+ else if (root->getTagAttribute("close", "xmlns") == ibbNamespace)
+ {
+ XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_CLOSE);
+ dispatchXmppEvent(event);
+ DOMString streamId = root->getTagAttribute("close", "sid");
+ if (streamId.size()>0)
+ {
+ for (int i=0 ; i<inputStreamCount ; i++)
+ {
+ XmppStream *ins = inputStreams[i];
+ if (ins->getStreamId() == streamId &&
+ from == ins->getPeerId())
+ {
+ ins->setState(STREAM_CLOSING);
+ ins->setIqId(id);
+ return true;
+ }
+ }
+ }
+ return true;
+ }
+ //### Responses to outgoing requests
+ for (int i=0 ; i<outputStreamCount ; i++)
+ {
+ XmppStream *outs = outputStreams[i];
+ if (outs->getIqId() == id)
+ {
+ if (type == "error")
+ {
+ outs->setState(STREAM_ERROR);
+ return true;
+ }
+ else if (type == "result")
+ {
+ if (outs->getState() == STREAM_OPENING)
+ {
+ outs->setState(STREAM_OPEN);
+ }
+ else if (outs->getState() == STREAM_CLOSING)
+ {
+ outs->setState(STREAM_CLOSED);
+ }
+ return true;
+ }
+ }
+ }
+
+ //###Normal Roster stuff
+ if (root->getTagAttribute("query", "xmlns") == "jabber:iq:roster")
+ {
+ roster.clear();
+ std::vector<Element *>elems = root->findElements("item");
+ for (unsigned int i=0 ; i<elems.size() ; i++)
+ {
+ Element *item = elems[i];
+ DOMString userJid = item->getAttribute("jid");
+ DOMString name = item->getAttribute("name");
+ DOMString subscription = item->getAttribute("subscription");
+ DOMString group = item->getTagValue("group");
+ //printf("jid:%s name:%s sub:%s group:%s\n", userJid.c_str(), name.c_str(),
+ // subscription.c_str(), group.c_str());
+ XmppUser user(userJid, name, subscription, group);
+ roster.push_back(user);
+ }
+ XmppEvent event(XmppEvent::XmppEvent::EVENT_ROSTER);
+ dispatchXmppEvent(event);
+ }
+
+ return true;
+}
+
+
+
+bool XmppClient::receiveAndProcess()
+{
+ if (!keepGoing)
+ return false;
+
+ Parser parser;
+
+ DOMString recvBuf = readStanza();
+ recvBuf = trim(recvBuf);
+ if (recvBuf.size() < 1)
+ return true;
+
+ //Ugly hack. Apparently the first char can be dropped on timeouts
+ //if (recvBuf[0] != '<')
+ // recvBuf.insert(0, "<");
+
+ status("RECV: %s", recvBuf.c_str());
+ Element *root = parser.parse(recvBuf);
+ if (!root)
+ {
+ printf("Bad elem\n");
+ return true;
+ }
+
+ //#### MESSAGE
+ std::vector<Element *>elems = root->findElements("message");
+ if (elems.size()>0)
+ {
+ if (!processMessage(root))
+ return false;
+ }
+
+ //#### PRESENCE
+ elems = root->findElements("presence");
+ if (elems.size()>0)
+ {
+ if (!processPresence(root))
+ return false;
+ }
+
+ //#### INFO
+ elems = root->findElements("iq");
+ if (elems.size()>0)
+ {
+ if (!processIq(root))
+ return false;
+ }
+
+ delete root;
+
+ return true;
+}
+
+
+bool XmppClient::receiveAndProcessLoop()
+{
+ keepGoing = true;
+ while (true)
+ {
+ if (!keepGoing)
+ {
+ printf("Abort requested\n");
+ break;
+ }
+ if (!receiveAndProcess())
+ return false;
+ }
+ return true;
+}
+
+//#######################
+//# SENDING
+//#######################
+
+bool XmppClient::write(char *fmt, ...)
+{
+ va_list args;
+ va_start(args,fmt);
+ vsnprintf((char *)writeBuf, writeBufLen, fmt,args);
+ va_end(args) ;
+ status("SEND: %s", writeBuf);
+ if (!sock->write((char *)writeBuf))
+ {
+ error("Cannot write to socket");
+ return false;
+ }
+ return true;
+}
+
+//#######################
+//# CONNECT
+//#######################
+
+bool XmppClient::checkConnect()
+{
+ if (!connected)
+ {
+ XmppEvent evt(XmppEvent::EVENT_ERROR);
+ evt.setData("Attempted operation while disconnected");
+ dispatchXmppEvent(evt);
+ return false;
+ }
+ return true;
+}
+
+bool XmppClient::iqAuthenticate(const DOMString &streamId)
+{
+ Parser parser;
+
+ char *fmt =
+ "<iq type='get' to='%s' id='auth%d'>"
+ "<query xmlns='jabber:iq:auth'><username>%s</username></query>"
+ "</iq>\n";
+ if (!write(fmt, realm.c_str(), msgId++, username.c_str()))
+ return false;
+
+ DOMString recbuf = readStanza();
+ //printf("iq received: '%s'\n", recbuf.c_str());
+ Element *elem = parser.parse(recbuf);
+ //elem->print();
+ DOMString iqType = elem->getTagAttribute("iq", "type");
+ //printf("##iqType:%s\n", iqType.c_str());
+ delete elem;
+
+ if (iqType != "result")
+ {
+ error("error:server does not allow login");
+ return false;
+ }
+
+ bool digest = true;
+ if (digest)
+ {
+ //## Digest authentication
+ DOMString digest = streamId;
+ digest.append(password);
+ digest = Sha1::hashHex((unsigned char *)digest.c_str(), digest.size());
+ //printf("digest:%s\n", digest.c_str());
+ fmt =
+ "<iq type='set' id='auth%d'>"
+ "<query xmlns='jabber:iq:auth'>"
+ "<username>%s</username>"
+ "<digest>%s</digest>"
+ "<resource>%s</resource>"
+ "</query>"
+ "</iq>\n";
+ if (!write(fmt, msgId++, username.c_str(),
+ digest.c_str(), resource.c_str()))
+ return false;
+ }
+ else
+ {
+
+ //## Plaintext authentication
+ fmt =
+ "<iq type='set' id='auth%d'>"
+ "<query xmlns='jabber:iq:auth'>"
+ "<username>%s</username>"
+ "<password>%s</password>"
+ "<resource>%s</resource>"
+ "</query>"
+ "</iq>\n";
+ if (!write(fmt, msgId++, username.c_str(),
+ password.c_str(), resource.c_str()))
+ return false;
+ }
+
+ recbuf = readStanza();
+ //printf("iq received: '%s'\n", recbuf.c_str());
+ elem = parser.parse(recbuf);
+ //elem->print();
+ iqType = elem->getTagAttribute("iq", "type");
+ //printf("##iqType:%s\n", iqType.c_str());
+ delete elem;
+
+ if (iqType != "result")
+ {
+ error("server does not allow login");
+ return false;
+ }
+
+ return true;
+}
+
+
+
+bool XmppClient::saslMd5Authenticate()
+{
+ Parser parser;
+ char *fmt =
+ "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
+ "mechanism='DIGEST-MD5'/>\n";
+ if (!write(fmt))
+ return false;
+
+ DOMString recbuf = readStanza();
+ status("challenge received: '%s'", recbuf.c_str());
+ Element *elem = parser.parse(recbuf);
+ //elem->print();
+ DOMString b64challenge = elem->getTagValue("challenge");
+ delete elem;
+
+ if (b64challenge.size() < 1)
+ {
+ error("login: no SASL challenge offered by server");
+ return false;
+ }
+ DOMString challenge = Base64Decoder::decodeToString(b64challenge);
+ status("challenge:'%s'", challenge.c_str());
+
+ unsigned int p1 = challenge.find("nonce=\"");
+ if (p1 == DOMString::npos)
+ {
+ error("login: no SASL nonce sent by server");
+ return false;
+ }
+ p1 += 7;
+ unsigned int p2 = challenge.find("\"", p1);
+ if (p2 == DOMString::npos)
+ {
+ error("login: unterminated SASL nonce sent by server");
+ return false;
+ }
+ DOMString nonce = challenge.substr(p1, p2-p1);
+ //printf("nonce: '%s'\n", nonce.c_str());
+ char idBuf[7];
+ snprintf(idBuf, 6, "%dsasl", msgId++);
+ DOMString cnonce = Sha1::hashHex((unsigned char *)idBuf, 7);
+ DOMString authzid = username; authzid.append("@"); authzid.append(host);
+ DOMString digest_uri = "xmpp/"; digest_uri.append(host);
+
+ //## Make A1
+ Md5 md5;
+ md5.append(username);
+ md5.append(":");
+ md5.append(realm);
+ md5.append(":");
+ md5.append(password);
+ unsigned char a1tmp[16];
+ md5.finish(a1tmp);
+ md5.init();
+ md5.append(a1tmp, 16);
+ md5.append(":");
+ md5.append(nonce);
+ md5.append(":");
+ md5.append(cnonce);
+ md5.append(":");
+ md5.append(authzid);
+ DOMString a1 = md5.finishHex();
+ status("##a1:'%s'", a1.c_str());
+
+ //# Make A2
+ md5.init();
+ md5.append("AUTHENTICATE:");
+ md5.append(digest_uri);
+ DOMString a2 = md5.finishHex();
+ status("##a2:'%s'", a2.c_str());
+
+ //# Now make the response
+ md5.init();
+ md5.append(a1);
+ md5.append(":");
+ md5.append(nonce);
+ md5.append(":");
+ md5.append("00000001");//nc
+ md5.append(":");
+ md5.append(cnonce);
+ md5.append(":");
+ md5.append("auth");//qop
+ md5.append(":");
+ md5.append(a2);
+ DOMString response = md5.finishHex();
+
+ DOMString resp;
+ resp.append("username=\""); resp.append(username); resp.append("\",");
+ resp.append("realm=\""); resp.append(realm); resp.append("\",");
+ resp.append("nonce=\""); resp.append(nonce); resp.append("\",");
+ resp.append("cnonce=\""); resp.append(cnonce); resp.append("\",");
+ resp.append("nc=00000001,qop=auth,");
+ resp.append("digest-uri=\""); resp.append(digest_uri); resp.append("\"," );
+ resp.append("authzid=\""); resp.append(authzid); resp.append("\",");
+ resp.append("response=\""); resp.append(response); resp.append("\",");
+ resp.append("charset=utf-8");
+ status("sending response:'%s'", resp.c_str());
+ resp = Base64Encoder::encode(resp);
+ status("base64 response:'%s'", resp.c_str());
+ fmt =
+ "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>%s</response>\n";
+ if (!write(fmt, resp.c_str()))
+ return false;
+
+ recbuf = readStanza();
+ status("server says:: '%s'", recbuf.c_str());
+ elem = parser.parse(recbuf);
+ //elem->print();
+ b64challenge = elem->getTagValue("challenge");
+ delete elem;
+
+ if (b64challenge.size() < 1)
+ {
+ error("login: no second SASL challenge offered by server");
+ return false;
+ }
+
+ challenge = Base64Decoder::decodeToString(b64challenge);
+ status("challenge: '%s'", challenge.c_str());
+ p1 = challenge.find("rspauth=");
+ if (p1 == DOMString::npos)
+ {
+ error("login: no SASL respauth sent by server\n");
+ return false;
+ }
+
+ fmt =
+ "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>\n";
+ if (!write(fmt))
+ return false;
+
+ recbuf = readStanza();
+ status("server says: '%s", recbuf.c_str());
+ elem = parser.parse(recbuf);
+ //elem->print();
+ b64challenge = elem->getTagValue("challenge");
+ bool success = (elem->findElements("success").size() > 0);
+ delete elem;
+
+ return success;
+}
+
+bool XmppClient::saslPlainAuthenticate()
+{
+ Parser parser;
+
+ DOMString id = username;
+ //id.append("@");
+ //id.append(host);
+ Base64Encoder encoder;
+ encoder.append('\0');
+ encoder.append(id);
+ encoder.append('\0');
+ encoder.append(password);
+ DOMString base64Auth = encoder.finish();
+ //printf("authbuf:%s\n", base64Auth.c_str());
+
+ char *fmt =
+ "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
+ "mechanism='PLAIN'>%s</auth>\n";
+ if (!write(fmt, base64Auth.c_str()))
+ return false;
+ DOMString recbuf = readStanza();
+ status("challenge received: '%s'", recbuf.c_str());
+ Element *elem = parser.parse(recbuf);
+
+ bool success = (elem->findElements("success").size() > 0);
+ delete elem;
+
+ return success;
+}
+
+
+
+bool XmppClient::saslAuthenticate()
+{
+ Parser parser;
+
+ DOMString recbuf = readStanza();
+ status("RECV: '%s'\n", recbuf.c_str());
+ Element *elem = parser.parse(recbuf);
+ //elem->print();
+
+ //Check for starttls
+ bool wantStartTls = false;
+ if (elem->findElements("starttls").size() > 0)
+ {
+ wantStartTls = true;
+ if (elem->findElements("required").size() > 0)
+ status("login: STARTTLS required");
+ else
+ status("login: STARTTLS available");
+ }
+
+ if (wantStartTls)
+ {
+ delete elem;
+ char *fmt =
+ "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n";
+ if (!write(fmt))
+ return false;
+ recbuf = readStanza();
+ status("RECV: '%s'\n", recbuf.c_str());
+ elem = parser.parse(recbuf);
+ if (elem->getTagAttribute("proceed", "xmlns").size()<1)
+ {
+ error("Server rejected TLS negotiation");
+ disconnect();
+ return false;
+ }
+ delete elem;
+ if (!sock->startTls())
+ {
+ error("Could not start TLS");
+ disconnect();
+ return false;
+ }
+
+ fmt =
+ "<stream:stream xmlns='jabber:client' "
+ "xmlns:stream='http://etherx.jabber.org/streams' "
+ "to='%s' version='1.0'>\n\n";
+ if (!write(fmt, realm.c_str()))
+ return false;
+
+ recbuf = readStanza();
+ status("RECVx: '%s'", recbuf.c_str());
+ recbuf.append("</stream:stream>");
+ elem = parser.parse(recbuf);
+ bool success =
+ (elem->getTagAttribute("stream:stream", "id").size()>0);
+ if (!success)
+ {
+ error("STARTTLS negotiation failed");
+ disconnect();
+ return false;
+ }
+ delete elem;
+ recbuf = readStanza();
+ status("RECV: '%s'\n", recbuf.c_str());
+ elem = parser.parse(recbuf);
+ }
+
+ //check for sasl authentication mechanisms
+ std::vector<Element *> elems =
+ elem->findElements("mechanism");
+ if (elems.size() < 1)
+ {
+ error("login: no SASL mechanism offered by server");
+ return false;
+ }
+ bool md5Found = false;
+ bool plainFound = false;
+ for (unsigned int i=0 ; i<elems.size() ; i++)
+ {
+ DOMString mech = elems[i]->getValue();
+ if (mech == "DIGEST-MD5")
+ {
+ status("MD5 authentication offered");
+ md5Found = true;
+ }
+ else if (mech == "PLAIN")
+ {
+ status("PLAIN authentication offered");
+ plainFound = true;
+ }
+ }
+ delete elem;
+
+ bool success = false;
+ if (md5Found)
+ {
+ success = saslMd5Authenticate();
+ }
+ else if (plainFound)
+ {
+ success = saslPlainAuthenticate();
+ }
+ else
+ {
+ error("not able to handle sasl authentication mechanisms");
+ return false;
+ }
+
+ if (success)
+ status("###### SASL authentication success\n");
+ else
+ error("###### SASL authentication failure\n");
+
+ return success;
+}
+
+
+
+
+
+bool XmppClient::createSession()
+{
+
+ Parser parser;
+ if (port==443 || port==5223)
+ sock->enableSSL(true);
+ if (!sock->connect(host, port))
+ {
+ return false;
+ }
+
+ char *fmt =
+ "<stream:stream "
+ "to='%s' "
+ "xmlns='jabber:client' "
+ "xmlns:stream='http://etherx.jabber.org/streams' "
+ "version='1.0'>\n\n";
+ if (!write(fmt, realm.c_str()))
+ return false;
+
+ DOMString recbuf = readStanza();
+ //printf("received: '%s'\n", recbuf.c_str());
+ recbuf.append("</stream:stream>");
+ Element *elem = parser.parse(recbuf);
+ //elem->print();
+ bool useSasl = false;
+ DOMString streamId = elem->getTagAttribute("stream:stream", "id");
+ //printf("### StreamID: %s\n", streamId.c_str());
+ DOMString streamVersion = elem->getTagAttribute("stream:stream", "version");
+ if (streamVersion == "1.0")
+ useSasl = true;
+
+ if (useSasl)
+ {
+ if (!saslAuthenticate())
+ return false;
+ fmt =
+ "<stream:stream "
+ "to='%s' "
+ "xmlns='jabber:client' "
+ "xmlns:stream='http://etherx.jabber.org/streams' "
+ "version='1.0'>\n\n";
+
+ if (!write(fmt, realm.c_str()))
+ return false;
+ recbuf = readStanza();
+ recbuf.append("</stream:stream>\n");
+ //printf("now server says:: '%s'\n", recbuf.c_str());
+ elem = parser.parse(recbuf);
+ //elem->print();
+ delete elem;
+
+ recbuf = readStanza();
+ //printf("now server says:: '%s'\n", recbuf.c_str());
+ elem = parser.parse(recbuf);
+ bool hasBind = (elem->findElements("bind").size() > 0);
+ //elem->print();
+ delete elem;
+
+ if (!hasBind)
+ {
+ error("no binding provided by server");
+ return false;
+ }
+
+
+ }
+ else // not SASL
+ {
+ if (!iqAuthenticate(streamId))
+ return false;
+ }
+
+
+ //### Resource binding
+ fmt =
+ "<iq type='set' id='bind%d'>"
+ "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
+ "<resource>%s</resource>"
+ "</bind></iq>\n";
+ if (!write(fmt, msgId++, resource.c_str()))
+ return false;
+
+ recbuf = readStanza();
+ status("bind result: '%s'", recbuf.c_str());
+ elem = parser.parse(recbuf);
+ //elem->print();
+ DOMString bindType = elem->getTagAttribute("iq", "type");
+ //printf("##bindType:%s\n", bindType.c_str());
+ delete elem;
+
+ if (bindType != "result")
+ {
+ error("no binding with server failed");
+ return false;
+ }
+
+ fmt =
+ "<iq type='set' id='sess%d'>"
+ "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
+ "</iq>\n";
+ if (!write(fmt, msgId++))
+ return false;
+
+ recbuf = readStanza();
+ status("session received: '%s'", recbuf.c_str());
+ elem = parser.parse(recbuf);
+ //elem->print();
+ DOMString sessionType = elem->getTagAttribute("iq", "type");
+ //printf("##sessionType:%s\n", sessionType.c_str());
+ delete elem;
+
+ if (sessionType != "result")
+ {
+ error("no session provided by server");
+ return false;
+ }
+
+ //printf("########## COOL #########\n");
+ //Now that we are bound, we have a valid JID
+ jid = username;
+ jid.append("@");
+ jid.append(realm);
+ jid.append("/");
+ jid.append(resource);
+
+ //We are now done with the synchronous handshaking. Let's go into
+ //async mode
+
+ fmt =
+ "<iq type='get' id='roster%d'><query xmlns='jabber:iq:roster'/></iq>\n";
+ if (!write(fmt, msgId++))
+ return false;
+
+ fmt =
+ "<iq type='get' id='discoItems%d' to='%s'>"
+ "<query xmlns='http://jabber.org/protocol/disco#items'/></iq>\n";
+ if (!write(fmt, msgId++, realm.c_str()))
+ return false;
+
+ fmt =
+ "<iq type='get' id='discoInfo%d' to='conference.%s'>"
+ "<query xmlns='http://jabber.org/protocol/disco#info'/></iq>\n";
+ if (!write(fmt, msgId++, realm.c_str()))
+ return false;
+
+ fmt =
+ "<presence/>\n";
+ if (!write(fmt))
+ return false;
+
+ /*
+ recbuf = readStanza();
+ status("stream received: '%s'", recbuf.c_str());
+ elem = parser.parse(recbuf);
+ //elem->print();
+ delete elem;
+ */
+
+ //We are now logged in
+ status("Connected");
+ connected = true;
+ XmppEvent evt(XmppEvent::EVENT_CONNECTED);
+ dispatchXmppEvent(evt);
+ //Thread::sleep(1000000);
+
+ sock->setReceiveTimeout(1000);
+ ReceiverThread runner(*this);
+ Thread thread(runner);
+ thread.start();
+
+ return true;
+}
+
+bool XmppClient::connect()
+{
+ if (!createSession())
+ {
+ disconnect();
+ return false;
+ }
+ return true;
+}
+
+
+bool XmppClient::connect(DOMString hostArg, int portArg,
+ DOMString usernameArg,
+ DOMString passwordArg,
+ DOMString resourceArg)
+{
+ host = hostArg;
+ port = portArg;
+ password = passwordArg;
+ resource = resourceArg;
+
+ //parse this one
+ setUsername(usernameArg);
+
+ bool ret = connect();
+ return ret;
+}
+
+bool XmppClient::disconnect()
+{
+ if (connected)
+ {
+ char *fmt =
+ "<presence from='%s' type='unavailable'/>\n";
+ write(fmt, jid.c_str());
+ }
+ keepGoing = false;
+ connected = false;
+ Thread::sleep(3000); //allow receiving thread to quit
+ sock->disconnect();
+ roster.clear();
+ groupChatsClear();
+ XmppEvent event(XmppEvent::EVENT_DISCONNECTED);
+ dispatchXmppEvent(event);
+ return true;
+}
+
+//#######################
+//# ROSTER
+//#######################
+
+bool XmppClient::rosterAdd(const DOMString &rosterGroup,
+ const DOMString &otherJid,
+ const DOMString &name)
+{
+ if (!checkConnect())
+ return false;
+ char *fmt =
+ "<iq from='%s' type='set' id='roster_%d'>"
+ "<query xmlns='jabber:iq:roster'>"
+ "<item jid='%s' name='%s'><group>%s</group></item>"
+ "</query></iq>\n";
+ if (!write(fmt, jid.c_str(), msgId++, otherJid.c_str(),
+ name.c_str(), rosterGroup.c_str()))
+ {
+ return false;
+ }
+ return true;
+}
+
+bool XmppClient::rosterDelete(const DOMString &otherJid)
+{
+ if (!checkConnect())
+ return false;
+ char *fmt =
+ "<iq from='%s' type='set' id='roster_%d'>"
+ "<query xmlns='jabber:iq:roster'>"
+ "<item jid='%s' subscription='remove'><group>%s</group></item>"
+ "</query></iq>\n";
+ if (!write(fmt, jid.c_str(), msgId++, otherJid.c_str()))
+ {
+ return false;
+ }
+ return true;
+}
+
+
+static bool xmppRosterCompare(const XmppUser& p1, const XmppUser& p2)
+{
+ DOMString s1 = p1.group;
+ DOMString s2 = p2.group;
+ for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
+ {
+ int comp = tolower(s1[len]) - tolower(s2[len]);
+ if (comp)
+ return (comp<0);
+ }
+
+ s1 = p1.jid;
+ s2 = p2.jid;
+ for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
+ {
+ int comp = tolower(s1[len]) - tolower(s2[len]);
+ if (comp)
+ return (comp<0);
+ }
+ return false;
+}
+
+std::vector<XmppUser> XmppClient::getRoster()
+{
+ std::vector<XmppUser> ros = roster;
+ std::sort(ros.begin(), ros.end(), xmppRosterCompare);
+ return ros;
+}
+
+void XmppClient::rosterShow(const DOMString &jid, const DOMString &show)
+{
+ DOMString theShow = show;
+ if (theShow == "")
+ theShow = "available";
+
+ std::vector<XmppUser>::iterator iter;
+ for (iter=roster.begin() ; iter != roster.end() ; iter++)
+ {
+ if (iter->jid == jid)
+ iter->show = theShow;
+ }
+}
+
+//#######################
+//# CHAT (individual)
+//#######################
+
+bool XmppClient::message(const DOMString &user, const DOMString &subj,
+ const DOMString &msg)
+{
+ if (!checkConnect())
+ return false;
+
+ DOMString xmlSubj = toXml(subj);
+ DOMString xmlMsg = toXml(msg);
+
+ if (xmlSubj.size() > 0)
+ {
+ char *fmt =
+ "<message from='%s' to='%s' type='chat'>"
+ "<subject>%s</subject><body>%s</body></message>\n";
+ if (!write(fmt, jid.c_str(), user.c_str(),
+ xmlSubj.c_str(), xmlMsg.c_str()))
+ return false;
+ }
+ else
+ {
+ char *fmt =
+ "<message from='%s' to='%s'>"
+ "<body>%s</body></message>\n";
+ if (!write(fmt, jid.c_str(), user.c_str(), xmlMsg.c_str()))
+ return false;
+ }
+ return true;
+}
+
+
+
+bool XmppClient::message(const DOMString &user, const DOMString &msg)
+{
+ return message(user, "", msg);
+}
+
+
+
+bool XmppClient::presence(const DOMString &presence)
+{
+ if (!checkConnect())
+ return false;
+
+ DOMString xmlPres = toXml(presence);
+
+ char *fmt =
+ "<presence from='%s'><show>%s</show></presence>\n";
+ if (!write(fmt, jid.c_str(), xmlPres.c_str()))
+ return false;
+ return true;
+}
+
+//#######################
+//# GROUP CHAT
+//#######################
+
+bool XmppClient::groupChatCreate(const DOMString &groupJid)
+{
+ std::vector<XmppGroupChat *>::iterator iter;
+ for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
+ {
+ if ((*iter)->getGroupJid() == groupJid)
+ {
+ error("Group chat '%s' already exists", groupJid.c_str());
+ return false;
+ }
+ }
+ XmppGroupChat *chat = new XmppGroupChat(groupJid);
+ groupChats.push_back(chat);
+ return true;
+}
+
+/**
+ *
+ */
+void XmppClient::groupChatDelete(const DOMString &groupJid)
+{
+ std::vector<XmppGroupChat *>::iterator iter;
+ for (iter=groupChats.begin() ; iter!=groupChats.end() ; )
+ {
+ XmppGroupChat *chat = *iter;
+ if (chat->getGroupJid() == groupJid)
+ {
+ iter = groupChats.erase(iter);
+ delete chat;
+ }
+ else
+ iter++;
+ }
+}
+
+/**
+ *
+ */
+bool XmppClient::groupChatExists(const DOMString &groupJid)
+{
+ std::vector<XmppGroupChat *>::iterator iter;
+ for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
+ if ((*iter)->getGroupJid() == groupJid)
+ return true;
+ return false;
+}
+
+/**
+ *
+ */
+void XmppClient::groupChatsClear()
+{
+ std::vector<XmppGroupChat *>::iterator iter;
+ for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
+ delete (*iter);
+ groupChats.clear();
+}
+
+
+/**
+ *
+ */
+void XmppClient::groupChatUserAdd(const DOMString &groupJid,
+ const DOMString &nick,
+ const DOMString &jid)
+{
+ std::vector<XmppGroupChat *>::iterator iter;
+ for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
+ {
+ if ((*iter)->getGroupJid() == groupJid)
+ {
+ (*iter)->userAdd(nick, jid);
+ }
+ }
+}
+
+/**
+ *
+ */
+void XmppClient::groupChatUserShow(const DOMString &groupJid,
+ const DOMString &nick,
+ const DOMString &show)
+{
+ std::vector<XmppGroupChat *>::iterator iter;
+ for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
+ {
+ if ((*iter)->getGroupJid() == groupJid)
+ {
+ (*iter)->userShow(nick, show);
+ }
+ }
+}
+
+/**
+ *
+ */
+void XmppClient::groupChatUserDelete(const DOMString &groupJid,
+ const DOMString &nick)
+{
+ std::vector<XmppGroupChat *>::iterator iter;
+ for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
+ {
+ if ((*iter)->getGroupJid() == groupJid)
+ {
+ (*iter)->userDelete(nick);
+ }
+ }
+}
+
+static bool xmppUserCompare(const XmppUser& p1, const XmppUser& p2)
+{
+ DOMString s1 = p1.nick;
+ DOMString s2 = p2.nick;
+ int comp = 0;
+ for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
+ {
+ comp = tolower(s1[len]) - tolower(s2[len]);
+ if (comp)
+ break;
+ }
+ return (comp<0);
+}
+
+
+std::vector<XmppUser> XmppClient::groupChatGetUserList(
+ const DOMString &groupJid)
+{
+ if (!checkConnect())
+ {
+ std::vector<XmppUser> dummy;
+ return dummy;
+ }
+
+ std::vector<XmppGroupChat *>::iterator iter;
+ for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
+ {
+ if ((*iter)->getGroupJid() == groupJid )
+ {
+ std::vector<XmppUser> uList = (*iter)->getUserList();
+ std::sort(uList.begin(), uList.end(), xmppUserCompare);
+ return uList;
+ }
+ }
+ std::vector<XmppUser> dummy;
+ return dummy;
+}
+
+bool XmppClient::groupChatJoin(const DOMString &groupJid,
+ const DOMString &nick,
+ const DOMString &pass)
+{
+ if (!checkConnect())
+ return false;
+
+ DOMString user = nick;
+ if (user.size()<1)
+ user = username;
+
+ char *fmt =
+ "<presence to='%s/%s'>"
+ "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
+ if (!write(fmt, groupJid.c_str(), user.c_str()))
+ return false;
+ return true;
+}
+
+
+bool XmppClient::groupChatLeave(const DOMString &groupJid,
+ const DOMString &nick)
+{
+ if (!checkConnect())
+ return false;
+
+ DOMString user = nick;
+ if (user.size()<1)
+ user = username;
+
+ char *fmt =
+ "<presence to='%s/%s' type='unavailable'>"
+ "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
+ if (!write(fmt, groupJid.c_str(), user.c_str()))
+ return false;
+ return true;
+}
+
+
+bool XmppClient::groupChatMessage(const DOMString &groupJid,
+ const DOMString &msg)
+{
+ if (!checkConnect())
+ {
+ return false;
+ }
+
+ DOMString xmlMsg = toXml(msg);
+
+ char *fmt =
+ "<message from='%s' to='%s' type='groupchat'>"
+ "<body>%s</body></message>\n";
+ if (!write(fmt, jid.c_str(), groupJid.c_str(), xmlMsg.c_str()))
+ return false;
+ return true;
+}
+
+bool XmppClient::groupChatPrivateMessage(const DOMString &groupJid,
+ const DOMString &toNick,
+ const DOMString &msg)
+{
+ if (!checkConnect())
+ return false;
+
+ DOMString xmlMsg = toXml(msg);
+
+ char *fmt =
+ "<message from='%s' to='%s/%s' type='chat'>"
+ "<body>%s</body></message>\n";
+ if (!write(fmt, jid.c_str(), groupJid.c_str(),
+ toNick.c_str(), xmlMsg.c_str()))
+ return false;
+ return true;
+}
+
+bool XmppClient::groupChatPresence(const DOMString &groupJid,
+ const DOMString &myNick,
+ const DOMString &presence)
+{
+ if (!checkConnect())
+ return false;
+
+ DOMString user = myNick;
+ if (user.size()<1)
+ user = username;
+
+ DOMString xmlPresence = toXml(presence);
+
+ char *fmt =
+ "<presence from='%s' to='%s/%s' type='unavailable'>"
+ "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
+ if (!write(fmt, jid.c_str(), groupJid.c_str(), user.c_str(), xmlPresence.c_str()))
+ return true;
+ return true;
+}
+
+
+
+//#######################
+//# S T R E A M S
+//#######################
+
+
+/**
+ *
+ */
+int XmppClient::outputStreamOpen(const DOMString &destId,
+ const DOMString &streamIdArg)
+{
+ int i;
+ for (i=0; i<outputStreamCount ; i++)
+ if (outputStreams[i]->getState() == STREAM_AVAILABLE)
+ break;
+ if (i>=outputStreamCount)
+ {
+ error("No available output streams");
+ return -1;
+ }
+ int streamNr = i;
+ XmppStream *outs = outputStreams[streamNr];
+
+ outs->setState(STREAM_OPENING);
+
+ char buf[32];
+ snprintf(buf, 31, "inband%d", getMsgId());
+ DOMString iqId = buf;
+
+ DOMString streamId = streamIdArg;
+ if (streamId.size()<1)
+ {
+ snprintf(buf, 31, "stream%d", getMsgId());
+ DOMString streamId = buf;
+ }
+ outs->setIqId(iqId);
+ outs->setStreamId(streamId);
+ outs->setPeerId(destId);
+
+ char *fmt =
+ "<iq type='set' from='%s' to='%s' id='%s'>"
+ "<open sid='%s' block-size='4096'"
+ " xmlns='http://jabber.org/protocol/ibb'/></iq>\n";
+ if (!write(fmt, jid.c_str(),
+ destId.c_str(), iqId.c_str(),
+ streamId.c_str()))
+ {
+ outs->reset();
+ return -1;
+ }
+
+ int state = outs->getState();
+ for (int tim=0 ; tim<20 ; tim++)
+ {
+ if (state == STREAM_OPEN)
+ break;
+ else if (state == STREAM_ERROR)
+ {
+ printf("ERROR\n");
+ outs->reset();
+ return -1;
+ }
+ Thread::sleep(1000);
+ state = outs->getState();
+ }
+ if (state != STREAM_OPEN)
+ {
+ printf("TIMEOUT ERROR\n");
+ outs->reset();
+ return -1;
+ }
+
+ return streamNr;
+}
+
+/**
+ *
+ */
+int XmppClient::outputStreamWrite(int streamNr,
+ const unsigned char *buf, unsigned long len)
+{
+ XmppStream *outs = outputStreams[streamNr];
+
+ unsigned long outLen = 0;
+ unsigned char *p = (unsigned char *)buf;
+
+ while (outLen < len)
+ {
+ unsigned long chunksize = 1024;
+ if (chunksize + outLen > len)
+ chunksize = len - outLen;
+
+ Base64Encoder encoder;
+ encoder.append(p, chunksize);
+ DOMString b64data = encoder.finish();
+ p += chunksize;
+ outLen += chunksize;
+
+ char *fmt =
+ "<message from='%s' to='%s' id='msg%d'>"
+ "<data xmlns='http://jabber.org/protocol/ibb' sid='%s' seq='%d'>"
+ "%s"
+ "</data>"
+ "<amp xmlns='http://jabber.org/protocol/amp'>"
+ "<rule condition='deliver-at' value='stored' action='error'/>"
+ "<rule condition='match-resource' value='exact' action='error'/>"
+ "</amp>"
+ "</message>\n";
+ if (!write(fmt, jid.c_str(),
+ outs->getPeerId().c_str(),
+ getMsgId(),
+ outs->getStreamId().c_str(),
+ outs->getSeqNr(),
+ b64data.c_str()))
+ {
+ outs->reset();
+ return -1;
+ }
+ pause(5000);
+ }
+ return outLen;
+}
+
+/**
+ *
+ */
+int XmppClient::outputStreamClose(int streamNr)
+{
+ XmppStream *outs = outputStreams[streamNr];
+
+ char buf[32];
+ snprintf(buf, 31, "inband%d", getMsgId());
+ DOMString iqId = buf;
+ outs->setIqId(iqId);
+
+ outs->setState(STREAM_CLOSING);
+ char *fmt =
+ "<iq type='set' from='%s' to='%s' id='%s'>"
+ "<close sid='%s' xmlns='http://jabber.org/protocol/ibb'/></iq>\n";
+ if (!write(fmt, jid.c_str(),
+ outs->getPeerId().c_str(),
+ iqId.c_str(),
+ outs->getStreamId().c_str()))
+ return false;
+
+ int state = outs->getState();
+ for (int tim=0 ; tim<20 ; tim++)
+ {
+ if (state == STREAM_CLOSED)
+ break;
+ else if (state == STREAM_ERROR)
+ {
+ printf("ERROR\n");
+ outs->reset();
+ return -1;
+ }
+ Thread::sleep(1000);
+ state = outs->getState();
+ }
+ if (state != STREAM_CLOSED)
+ {
+ printf("TIMEOUT ERROR\n");
+ outs->reset();
+ return -1;
+ }
+
+ outs->reset();
+ return 1;
+}
+
+
+/**
+ *
+ */
+int XmppClient::inputStreamOpen(const DOMString &fromJid, const DOMString &streamId,
+ const DOMString &iqId)
+{
+ int i;
+ for (i=0 ; i<inputStreamCount ; i++)
+ {
+ if (inputStreams[i]->getState() == STREAM_AVAILABLE)
+ break;
+ }
+ if (i>=inputStreamCount)
+ {
+ error("No available input streams");
+ return -1;
+ }
+ int streamNr = i;
+ XmppStream *ins = inputStreams[streamNr];
+ ins->reset();
+ ins->setPeerId(fromJid);
+ ins->setState(STREAM_CLOSED);
+ ins->setStreamId(streamId);
+
+ int state = ins->getState();
+ for (int tim=0 ; tim<20 ; tim++)
+ {
+ if (state == STREAM_OPENING)
+ break;
+ else if (state == STREAM_ERROR)
+ {
+ printf("ERROR\n");
+ ins->reset();
+ return -1;
+ }
+ Thread::sleep(1000);
+ state = ins->getState();
+ }
+ if (state != STREAM_OPENING)
+ {
+ printf("TIMEOUT ERROR\n");
+ ins->reset();
+ return -1;
+ }
+ char *fmt =
+ "<iq type='result' from='%s' to='%s' id='%s'/>\n";
+ if (!write(fmt, jid.c_str(), fromJid.c_str(), ins->getIqId().c_str()))
+ {
+ return -1;
+ }
+
+ ins->setState(STREAM_OPEN);
+ return streamNr;
+}
+
+/**
+ *
+ */
+int XmppClient::inputStreamAvailable(int streamNr)
+{
+ XmppStream *ins = inputStreams[streamNr];
+ return ins->available();
+}
+
+/**
+ *
+ */
+std::vector<unsigned char> XmppClient::inputStreamRead(int streamNr)
+{
+ XmppStream *ins = inputStreams[streamNr];
+ return ins->read();
+}
+
+/**
+ *
+ */
+bool XmppClient::inputStreamClosing(int streamNr)
+{
+ XmppStream *ins = inputStreams[streamNr];
+ if (ins->getState() == STREAM_CLOSING)
+ return true;
+ return false;
+}
+
+
+/**
+ *
+ */
+int XmppClient::inputStreamClose(int streamNr)
+{
+ int ret=1;
+ XmppStream *ins = inputStreams[streamNr];
+ if (ins->getState() == STREAM_CLOSING)
+ {
+ char *fmt =
+ "<iq type='result' from='%s' to='%s' id='%s'/>\n";
+ if (!write(fmt, jid.c_str(), ins->getPeerId().c_str(),
+ ins->getIqId().c_str()))
+ {
+ ret = -1;
+ }
+ }
+ ins->reset();
+ return ret;
+}
+
+
+
+
+//#######################
+//# FILE TRANSFERS
+//#######################
+
+
+/**
+ *
+ */
+bool XmppClient::fileSend(const DOMString &destJidArg,
+ const DOMString &offeredNameArg,
+ const DOMString &fileNameArg,
+ const DOMString &descriptionArg)
+{
+ DOMString destJid = destJidArg;
+ DOMString offeredName = offeredNameArg;
+ DOMString fileName = fileNameArg;
+ DOMString description = descriptionArg;
+
+ int i;
+ for (i=0; i<fileSendCount ; i++)
+ if (fileSends[i]->getState() == STREAM_AVAILABLE)
+ break;
+ if (i>=fileSendCount)
+ {
+ error("No available file send streams");
+ return false;
+ }
+ int fileSendNr = i;
+ XmppStream *outf = fileSends[fileSendNr];
+
+ outf->setState(STREAM_OPENING);
+
+ struct stat finfo;
+ if (stat(fileName.c_str(), &finfo)<0)
+ {
+ error("Cannot stat file '%s' for sending", fileName.c_str());
+ return false;
+ }
+ long fileLen = finfo.st_size;
+ if (!fileLen > 1000000)
+ {
+ error("'%s' too large", fileName.c_str());
+ return false;
+ }
+ if (!S_ISREG(finfo.st_mode))
+ {
+ error("'%s' is not a regular file", fileName.c_str());
+ return false;
+ }
+ FILE *f = fopen(fileName.c_str(), "rb");
+ if (!f)
+ {
+ error("cannot open '%s' for sending", fileName.c_str());
+ return false;
+ }
+ unsigned char *sendBuf = (unsigned char *)malloc(fileLen+1);
+ if (!sendBuf)
+ {
+ error("cannot cannot allocate send buffer for %s", fileName.c_str());
+ return false;
+ }
+ for (long i=0 ; i<fileLen && !feof(f); i++)
+ {
+ sendBuf[i] = fgetc(f);
+ }
+ fclose(f);
+
+ //## get the last path segment from the whole path
+ if (offeredName.size()<1)
+ {
+ int slashPos = -1;
+ for (unsigned int i=0 ; i<fileName.size() ; i++)
+ {
+ int ch = fileName[i];
+ if (ch == '/' || ch == '\\')
+ slashPos = i;
+ }
+ if (slashPos>=0 && slashPos<=(int)(fileName.size()-1))
+ {
+ offeredName = fileName.substr(slashPos+1,
+ fileName.size()-slashPos-1);
+ printf("offeredName:%s\n", offeredName.c_str());
+ }
+ }
+
+ char buf[32];
+ snprintf(buf, 31, "file%d", getMsgId());
+ DOMString iqId = buf;
+ outf->setIqId(iqId);
+
+ snprintf(buf, 31, "stream%d", getMsgId());
+ DOMString streamId = buf;
+ //outf->setStreamId(streamId);
+
+ DOMString hash = Md5::hashHex(sendBuf, fileLen);
+ printf("Hash:%s\n", hash.c_str());
+
+ outf->setPeerId(destJid);
+
+ char dtgBuf[81];
+ struct tm *timeVal = gmtime(&(finfo.st_mtime));
+ strftime(dtgBuf, 80, "%Y-%m-%dT%H:%M:%Sz", timeVal);
+
+ char *fmt =
+ "<iq type='set' id='%s' to='%s'>"
+ "<si xmlns='http://jabber.org/protocol/si' id='%s'"
+ " mime-type='text/plain'"
+ " profile='http://jabber.org/protocol/si/profile/file-transfer'>"
+ "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'"
+ " name='%s' size='%d' hash='%s' date='%s'><desc>%s</desc></file>"
+ "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
+ "<x xmlns='jabber:x:data' type='form'>"
+ "<field var='stream-method' type='list-single'>"
+ //"<option><value>http://jabber.org/protocol/bytestreams</value></option>"
+ "<option><value>http://jabber.org/protocol/ibb</value></option>"
+ "</field></x></feature></si></iq>\n";
+ if (!write(fmt, iqId.c_str(), destJid.c_str(),
+ streamId.c_str(), offeredName.c_str(), fileLen,
+ hash.c_str(), dtgBuf, description.c_str()))
+ {
+ free(sendBuf);
+ return false;
+ }
+
+ int state = outf->getState();
+ for (int tim=0 ; tim<20 ; tim++)
+ {
+ printf("##### waiting for open\n");
+ if (state == STREAM_OPEN)
+ {
+ outf->reset();
+ break;
+ }
+ else if (state == STREAM_ERROR)
+ {
+ printf("ERROR\n");
+ outf->reset();
+ return false;
+ }
+ Thread::sleep(1000);
+ state = outf->getState();
+ }
+ if (state != STREAM_OPEN)
+ {
+ printf("TIMEOUT ERROR\n");
+ outf->reset();
+ return false;
+ }
+
+ //free up this reqource
+ outf->reset();
+
+ int streamNr = outputStreamOpen(destJid, streamId);
+ if (streamNr<0)
+ {
+ error("cannot open output stream %s", streamId.c_str());
+ outf->reset();
+ return false;
+ }
+
+ int ret = outputStreamWrite(streamNr, sendBuf, fileLen);
+
+ if (ret<0)
+ {
+ }
+
+ outputStreamClose(streamNr);
+
+ free(sendBuf);
+ return true;
+}
+
+
+class FileSendThread : public Thread
+{
+public:
+
+ FileSendThread(XmppClient &par,
+ const DOMString &destJidArg,
+ const DOMString &offeredNameArg,
+ const DOMString &fileNameArg,
+ const DOMString &descriptionArg) : client(par)
+ {
+ destJid = destJidArg;
+ offeredName = offeredNameArg;
+ fileName = fileNameArg;
+ description = descriptionArg;
+ }
+
+ virtual ~FileSendThread() {}
+
+ void run()
+ {
+ client.fileSend(destJid, offeredName,
+ fileName, description);
+ }
+
+private:
+
+ XmppClient &client;
+ DOMString destJid;
+ DOMString offeredName;
+ DOMString fileName;
+ DOMString description;
+};
+
+/**
+ *
+ */
+bool XmppClient::fileSendBackground(const DOMString &destJid,
+ const DOMString &offeredName,
+ const DOMString &fileName,
+ const DOMString &description)
+{
+ FileSendThread thread(*this, destJid, offeredName,
+ fileName, description);
+ thread.start();
+ return true;
+}
+
+
+/**
+ *
+ */
+bool XmppClient::fileReceive(const DOMString &fromJid,
+ const DOMString &iqId,
+ const DOMString &streamId,
+ const DOMString &fileName,
+ long fileSize,
+ const DOMString &fileHash)
+{
+ char *fmt =
+ "<iq type='result' to='%s' id='%s'>"
+ "<si xmlns='http://jabber.org/protocol/si'>"
+ "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'/>"
+ "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
+ "<x xmlns='jabber:x:data' type='submit'>"
+ "<field var='stream-method'>"
+ "<value>http://jabber.org/protocol/ibb</value>"
+ "</field></x></feature></si></iq>\n";
+ if (!write(fmt, fromJid.c_str(), iqId.c_str()))
+ {
+ return false;
+ }
+
+ int streamNr = inputStreamOpen(fromJid, streamId, iqId);
+ if (streamNr < 0)
+ {
+ return false;
+ }
+
+
+ Md5 md5;
+ FILE *f = fopen(fileName.c_str(), "wb");
+ if (!f)
+ {
+ return false;
+ }
+
+ while (true)
+ {
+ if (inputStreamAvailable(streamNr)<1)
+ {
+ if (inputStreamClosing(streamNr))
+ break;
+ pause(100);
+ continue;
+ }
+ std::vector<unsigned char> ret = inputStreamRead(streamNr);
+ std::vector<unsigned char>::iterator iter;
+ for (iter=ret.begin() ; iter!=ret.end() ; iter++)
+ {
+ unsigned char ch = *iter;
+ md5.append(&ch, 1);
+ fwrite(&ch, 1, 1, f);
+ }
+ }
+
+ inputStreamClose(streamNr);
+ fclose(f);
+
+ DOMString hash = md5.finishHex();
+ printf("received file hash:%s\n", hash.c_str());
+
+ return true;
+}
+
+
+
+class FileReceiveThread : public Thread
+{
+public:
+
+ FileReceiveThread(XmppClient &par,
+ const DOMString &fromJidArg,
+ const DOMString &iqIdArg,
+ const DOMString &streamIdArg,
+ const DOMString &fileNameArg,
+ long fileSizeArg,
+ const DOMString &fileHashArg) : client(par)
+ {
+ fromJid = fromJidArg;
+ iqId = iqIdArg;
+ streamId = streamIdArg;
+ fileName = fileNameArg;
+ fileSize = fileSizeArg;
+ fileHash = fileHashArg;
+ }
+
+ virtual ~FileReceiveThread() {}
+
+ void run()
+ {
+ client.fileReceive(fromJid, iqId, streamId,
+ fileName, fileSize, fileHash);
+ }
+
+private:
+
+ XmppClient &client;
+ DOMString fromJid;
+ DOMString iqId;
+ DOMString streamId;
+ DOMString fileName;
+ long fileSize;
+ DOMString fileHash;
+};
+
+/**
+ *
+ */
+bool XmppClient::fileReceiveBackground(const DOMString &fromJid,
+ const DOMString &iqId,
+ const DOMString &streamId,
+ const DOMString &fileName,
+ long fileSize,
+ const DOMString &fileHash)
+{
+ FileReceiveThread thread(*this, fromJid, iqId, streamId,
+ fileName, fileSize, fileHash);
+ thread.start();
+ return true;
+}
+
+
+
+//########################################################################
+//# X M P P G R O U P C H A T
+//########################################################################
+
+/**
+ *
+ */
+XmppGroupChat::XmppGroupChat(const DOMString &groupJidArg)
+{
+ groupJid = groupJidArg;
+}
+
+/**
+ *
+ */
+XmppGroupChat::XmppGroupChat(const XmppGroupChat &other)
+{
+ groupJid = other.groupJid;
+ userList = other.userList;
+}
+
+/**
+ *
+ */
+XmppGroupChat::~XmppGroupChat()
+{
+}
+
+
+/**
+ *
+ */
+DOMString XmppGroupChat::getGroupJid()
+{
+ return groupJid;
+}
+
+
+void XmppGroupChat::userAdd(const DOMString &nick,
+ const DOMString &jid)
+{
+ std::vector<XmppUser>::iterator iter;
+ for (iter= userList.begin() ; iter!=userList.end() ; iter++)
+ {
+ if (iter->nick == nick)
+ return;
+ }
+ XmppUser user(jid, nick);
+ userList.push_back(user);
+}
+
+void XmppGroupChat::userShow(const DOMString &nick,
+ const DOMString &show)
+{
+ DOMString theShow = show;
+ if (theShow == "")
+ theShow = "available"; // a join message will now have a show
+ std::vector<XmppUser>::iterator iter;
+ for (iter= userList.begin() ; iter!=userList.end() ; iter++)
+ {
+ if (iter->nick == nick)
+ iter->show = theShow;
+ }
+}
+
+void XmppGroupChat::userDelete(const DOMString &nick)
+{
+ std::vector<XmppUser>::iterator iter;
+ for (iter= userList.begin() ; iter!=userList.end() ; )
+ {
+ if (iter->nick == nick)
+ iter = userList.erase(iter);
+ else
+ iter++;
+ }
+}
+
+std::vector<XmppUser> XmppGroupChat::getUserList() const
+{
+ return userList;
+}
+
+
+
+
+
+
+} //namespace Pedro
+//########################################################################
+//# E N D O F F I L E
+//########################################################################
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pedro/pedroxmpp.h b/src/pedro/pedroxmpp.h
--- /dev/null
+++ b/src/pedro/pedroxmpp.h
@@ -0,0 +1,1040 @@
+#ifndef __XMPP_H__
+#define __XMPP_H__
+/*
+ * API for the Pedro mini-XMPP client.
+ *
+ * Authors:
+ * Bob Jamison
+ *
+ * Copyright (C) 2005 Bob Jamison
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <vector>
+
+#include <string>
+
+#include "pedrodom.h"
+
+namespace Pedro
+{
+
+typedef std::string DOMString;
+
+
+//########################################################################
+//# X M P P E V E N T
+//########################################################################
+class XmppUser
+{
+public:
+ XmppUser()
+ {
+ }
+ XmppUser(const DOMString &jidArg, const DOMString &nickArg)
+ {
+ jid = jidArg;
+ nick = nickArg;
+ }
+ XmppUser(const DOMString &jidArg, const DOMString &nickArg,
+ const DOMString &subscriptionArg, const DOMString &groupArg)
+ {
+ jid = jidArg;
+ nick = nickArg;
+ subscription = subscriptionArg;
+ group = groupArg;
+ }
+ XmppUser(const XmppUser &other)
+ {
+ jid = other.jid;
+ nick = other.nick;
+ subscription = other.subscription;
+ group = other.group;
+ show = other.show;
+ }
+ XmppUser &operator=(const XmppUser &other)
+ {
+ jid = other.jid;
+ nick = other.nick;
+ subscription = other.subscription;
+ group = other.group;
+ show = other.show;
+ return *this;
+ }
+ virtual ~XmppUser()
+ {}
+ DOMString jid;
+ DOMString nick;
+ DOMString subscription;
+ DOMString group;
+ DOMString show;
+};
+
+class XmppEvent
+{
+
+public:
+
+typedef enum
+ {
+ EVENT_NONE,
+ EVENT_STATUS,
+ EVENT_ERROR,
+ EVENT_CONNECTED,
+ EVENT_DISCONNECTED,
+ EVENT_PRESENCE,
+ EVENT_ROSTER,
+ EVENT_MESSAGE,
+ EVENT_MUC_JOIN,
+ EVENT_MUC_LEAVE,
+ EVENT_MUC_PRESENCE,
+ EVENT_MUC_MESSAGE,
+ EVENT_STREAM_RECEIVE_INIT,
+ EVENT_STREAM_RECEIVE,
+ EVENT_STREAM_RECEIVE_CLOSE,
+ EVENT_FILE_ACCEPTED,
+ EVENT_FILE_RECEIVE
+ } XmppEventType;
+
+
+ /**
+ *
+ */
+ XmppEvent(int type);
+
+ /**
+ *
+ */
+ XmppEvent(const XmppEvent &other);
+
+ /**
+ *
+ */
+ virtual XmppEvent &operator=(const XmppEvent &other);
+
+ /**
+ *
+ */
+ virtual ~XmppEvent();
+
+ /**
+ *
+ */
+ virtual void assign(const XmppEvent &other);
+
+ /**
+ *
+ */
+ virtual int getType() const;
+
+
+ /**
+ *
+ */
+ virtual DOMString getIqId() const;
+
+
+ /**
+ *
+ */
+ virtual void setIqId(const DOMString &val);
+
+ /**
+ *
+ */
+ virtual DOMString getStreamId() const;
+
+
+ /**
+ *
+ */
+ virtual void setStreamId(const DOMString &val);
+
+ /**
+ *
+ */
+ virtual bool getPresence() const;
+
+
+ /**
+ *
+ */
+ virtual void setPresence(bool val);
+
+ /**
+ *
+ */
+ virtual DOMString getShow() const;
+
+
+ /**
+ *
+ */
+ virtual void setShow(const DOMString &val);
+
+ /**
+ *
+ */
+ virtual DOMString getStatus() const;
+
+ /**
+ *
+ */
+ virtual void setStatus(const DOMString &val);
+
+ /**
+ *
+ */
+ virtual DOMString getTo() const;
+
+ /**
+ *
+ */
+ virtual void setTo(const DOMString &val);
+
+ /**
+ *
+ */
+ virtual DOMString getFrom() const;
+
+ /**
+ *
+ */
+ virtual void setFrom(const DOMString &val);
+
+ /**
+ *
+ */
+ virtual DOMString getGroup() const;
+
+ /**
+ *
+ */
+ virtual void setGroup(const DOMString &val);
+
+ /**
+ *
+ */
+ virtual Element *getDOM() const;
+
+
+ /**
+ *
+ */
+ virtual void setDOM(const Element *val);
+
+ /**
+ *
+ */
+ virtual std::vector<XmppUser> getUserList() const;
+
+ /**
+ *
+ */
+ virtual void setUserList(const std::vector<XmppUser> &userList);
+
+ /**
+ *
+ */
+ virtual DOMString getFileName() const;
+
+
+ /**
+ *
+ */
+ virtual void setFileName(const DOMString &val);
+
+
+ /**
+ *
+ */
+ virtual DOMString getFileDesc() const;
+
+
+ /**
+ *
+ */
+ virtual void setFileDesc(const DOMString &val);
+
+
+ /**
+ *
+ */
+ virtual long getFileSize() const;
+
+
+ /**
+ *
+ */
+ virtual void setFileSize(long val);
+
+ /**
+ *
+ */
+ virtual DOMString getFileHash() const;
+
+ /**
+ *
+ */
+ virtual void setFileHash(const DOMString &val);
+
+ /**
+ *
+ */
+ virtual DOMString getData() const;
+
+
+ /**
+ *
+ */
+ virtual void setData(const DOMString &val);
+
+private:
+
+ int eventType;
+
+ DOMString iqId;
+
+ DOMString streamId;
+
+ bool presence;
+
+ DOMString show;
+
+ DOMString status;
+
+ DOMString to;
+
+ DOMString from;
+
+ DOMString group;
+
+ DOMString data;
+
+ DOMString fileName;
+ DOMString fileDesc;
+ long fileSize;
+ DOMString fileHash;
+
+ Element *dom;
+
+ std::vector<XmppUser>userList;
+
+};
+
+
+//########################################################################
+//# X M P P E V E N T L I S T E N E R
+//########################################################################
+
+class XmppEventListener
+{
+public:
+
+ /**
+ *
+ */
+ XmppEventListener()
+ {}
+
+ /**
+ *
+ */
+ XmppEventListener(const XmppEventListener &other)
+ {}
+
+
+ /**
+ *
+ */
+ virtual ~XmppEventListener()
+ {}
+
+ /**
+ *
+ */
+ virtual void processXmppEvent(const XmppEvent &event)
+ {}
+
+};
+
+
+
+//########################################################################
+//# X M P P E V E N T T A R G E T
+//########################################################################
+
+class XmppEventTarget
+{
+public:
+
+ /**
+ *
+ */
+ XmppEventTarget();
+
+ /**
+ *
+ */
+ XmppEventTarget(const XmppEventTarget &other);
+
+ /**
+ *
+ */
+ virtual ~XmppEventTarget();
+
+
+ //###########################
+ //# M E S S A G E S
+ //###########################
+
+
+ /**
+ * Send an error message to all subscribers
+ */
+ void error(char *fmt, ...);
+
+
+ /**
+ * Send a status message to all subscribers
+ */
+ void status(char *fmt, ...);
+
+ //###########################
+ //# LISTENERS
+ //###########################
+
+ /**
+ *
+ */
+ virtual void dispatchXmppEvent(const XmppEvent &event);
+
+ /**
+ *
+ */
+ virtual void addXmppEventListener(const XmppEventListener &listener);
+
+ /**
+ *
+ */
+ virtual void removeXmppEventListener(const XmppEventListener &listener);
+
+ /**
+ *
+ */
+ virtual void clearXmppEventListeners();
+
+ /**
+ *
+ */
+ void eventQueueEnable(bool val);
+
+ /**
+ *
+ */
+ int eventQueueAvailable();
+
+ /**
+ *
+ */
+ XmppEvent eventQueuePop();
+
+
+private:
+
+ std::vector<XmppEventListener *> listeners;
+
+ std::vector<XmppEvent> eventQueue;
+ bool eventQueueEnabled;
+
+ static const int targetWriteBufLen = 2048;
+
+ char targetWriteBuf[targetWriteBufLen];
+};
+
+
+
+
+
+//########################################################################
+//# X M P P C L I E N T
+//########################################################################
+
+
+class TcpSocket;
+class XmppChat;
+class XmppGroupChat;
+class XmppStream;
+
+class XmppClient : public XmppEventTarget
+{
+
+public:
+
+ //###########################
+ //# CONSTRUCTORS
+ //###########################
+
+ /**
+ *
+ */
+ XmppClient();
+
+ /**
+ *
+ */
+ XmppClient(const XmppClient &other);
+
+ /**
+ *
+ */
+ void assign(const XmppClient &other);
+
+ /**
+ *
+ */
+ virtual ~XmppClient();
+
+
+ //###########################
+ //# UTILITY
+ //###########################
+
+ /**
+ *
+ */
+ virtual bool pause(unsigned long millis);
+
+ /**
+ *
+ */
+ DOMString toXml(const DOMString &str);
+
+ //###########################
+ //# CONNECTION
+ //###########################
+
+ /**
+ *
+ */
+ virtual bool connect();
+
+ /**
+ *
+ */
+ virtual bool connect(DOMString host, int port,
+ DOMString userName,
+ DOMString password,
+ DOMString resource);
+
+ /**
+ *
+ */
+ virtual bool disconnect();
+
+
+ /**
+ *
+ */
+ virtual bool write(char *fmt, ...);
+
+ /**
+ *
+ */
+ virtual bool isConnected()
+ { return connected; }
+
+ /**
+ *
+ */
+ virtual DOMString getHost()
+ { return host; }
+
+ /**
+ *
+ */
+ virtual void setHost(const DOMString &val)
+ { host = val; }
+
+ /**
+ *
+ */
+ virtual DOMString getRealm()
+ { return realm; }
+
+ /**
+ *
+ */
+ virtual void setRealm(const DOMString &val)
+ { realm = val; }
+
+ /**
+ *
+ */
+ virtual int getPort()
+ { return port; }
+
+ /**
+ *
+ */
+ virtual void setPort(int val)
+ { port = val; }
+
+ /**
+ *
+ */
+ virtual DOMString getUsername();
+
+ /**
+ *
+ */
+ virtual void setUsername(const DOMString &val);
+
+ /**
+ *
+ */
+ virtual DOMString getPassword()
+ { return password; }
+
+ /**
+ *
+ */
+ virtual void setPassword(const DOMString &val)
+ { password = val; }
+
+ /**
+ *
+ */
+ virtual DOMString getResource()
+ { return resource; }
+
+ /**
+ *
+ */
+ virtual void setResource(const DOMString &val)
+ { resource = val; }
+
+ /**
+ *
+ */
+ virtual DOMString getJid()
+ { return jid; }
+ /**
+ *
+ */
+ virtual int getMsgId()
+ { return msgId++; }
+
+ /**
+ *
+ */
+ bool processMessage(Element *root);
+
+ /**
+ *
+ */
+ bool processPresence(Element *root);
+
+ /**
+ *
+ */
+ bool processIq(Element *root);
+
+ /**
+ *
+ */
+ virtual bool receiveAndProcess();
+
+ /**
+ *
+ */
+ virtual bool receiveAndProcessLoop();
+
+ //#######################
+ //# ROSTER
+ //#######################
+
+ /**
+ *
+ */
+ bool rosterAdd(const DOMString &rosterGroup,
+ const DOMString &otherJid,
+ const DOMString &name);
+
+ /**
+ *
+ */
+ bool rosterDelete(const DOMString &otherJid);
+
+ /**
+ *
+ */
+ std::vector<XmppUser> getRoster();
+
+ /**
+ *
+ */
+ virtual void rosterShow(const DOMString &jid, const DOMString &show);
+
+ //#######################
+ //# CHAT (individual)
+ //#######################
+
+ /**
+ *
+ */
+ virtual bool message(const DOMString &user, const DOMString &subj,
+ const DOMString &text);
+
+ /**
+ *
+ */
+ virtual bool message(const DOMString &user, const DOMString &text);
+
+ /**
+ *
+ */
+ virtual bool presence(const DOMString &presence);
+
+ //#######################
+ //# GROUP CHAT
+ //#######################
+
+ /**
+ *
+ */
+ virtual bool groupChatCreate(const DOMString &groupJid);
+
+ /**
+ *
+ */
+ virtual void groupChatDelete(const DOMString &groupJid);
+
+ /**
+ *
+ */
+ bool groupChatExists(const DOMString &groupJid);
+
+ /**
+ *
+ */
+ virtual void groupChatsClear();
+
+ /**
+ *
+ */
+ virtual void groupChatUserAdd(const DOMString &groupJid,
+ const DOMString &nick,
+ const DOMString &jid);
+ /**
+ *
+ */
+ virtual void groupChatUserShow(const DOMString &groupJid,
+ const DOMString &nick,
+ const DOMString &show);
+
+ /**
+ *
+ */
+ virtual void groupChatUserDelete(const DOMString &groupJid,
+ const DOMString &nick);
+
+ /**
+ *
+ */
+ virtual std::vector<XmppUser>
+ XmppClient::groupChatGetUserList(const DOMString &groupJid);
+
+ /**
+ *
+ */
+ virtual bool groupChatJoin(const DOMString &groupJid,
+ const DOMString &nick,
+ const DOMString &pass);
+
+ /**
+ *
+ */
+ virtual bool groupChatLeave(const DOMString &groupJid,
+ const DOMString &nick);
+
+ /**
+ *
+ */
+ virtual bool groupChatMessage(const DOMString &groupJid,
+ const DOMString &msg);
+
+ /**
+ *
+ */
+ virtual bool groupChatPrivateMessage(const DOMString &groupJid,
+ const DOMString &toNick,
+ const DOMString &msg);
+
+ /**
+ *
+ */
+ virtual bool groupChatPresence(const DOMString &groupJid,
+ const DOMString &nick,
+ const DOMString &presence);
+
+
+ //#######################
+ //# STREAMS
+ //#######################
+
+ typedef enum
+ {
+ STREAM_AVAILABLE,
+ STREAM_OPENING,
+ STREAM_OPEN,
+ STREAM_CLOSING,
+ STREAM_CLOSED,
+ STREAM_ERROR
+ } StreamStates;
+
+ /**
+ *
+ */
+ virtual int outputStreamOpen(const DOMString &jid,
+ const DOMString &streamId);
+
+ /**
+ *
+ */
+ virtual int outputStreamWrite(int streamId,
+ const unsigned char *buf, unsigned long len);
+
+ /**
+ *
+ */
+ virtual int outputStreamClose(int streamId);
+
+ /**
+ *
+ */
+ virtual int inputStreamOpen(const DOMString &jid,
+ const DOMString &streamId,
+ const DOMString &iqId);
+
+ /**
+ *
+ */
+ virtual int inputStreamAvailable(int streamId);
+
+ /**
+ *
+ */
+ virtual std::vector<unsigned char> inputStreamRead(int streamId);
+
+ /**
+ *
+ */
+ virtual bool inputStreamClosing(int streamId);
+
+ /**
+ *
+ */
+ virtual int inputStreamClose(int streamId);
+
+
+ //#######################
+ //# FILE TRANSFERS
+ //#######################
+
+ /**
+ *
+ */
+ virtual bool fileSend(const DOMString &destJid,
+ const DOMString &offeredName,
+ const DOMString &fileName,
+ const DOMString &description);
+
+ /**
+ *
+ */
+ virtual bool fileSendBackground(const DOMString &destJid,
+ const DOMString &offeredName,
+ const DOMString &fileName,
+ const DOMString &description);
+
+ /**
+ *
+ */
+ virtual bool fileReceive(const DOMString &fromJid,
+ const DOMString &iqId,
+ const DOMString &streamId,
+ const DOMString &fileName,
+ long fileSize,
+ const DOMString &fileHash);
+ /**
+ *
+ */
+ virtual bool fileReceiveBackground(const DOMString &fromJid,
+ const DOMString &iqId,
+ const DOMString &streamId,
+ const DOMString &fileName,
+ long fileSize,
+ const DOMString &fileHash);
+
+
+private:
+
+ void init();
+
+ DOMString host;
+
+ /**
+ * will be same as host, unless username is
+ * user@realm
+ */
+ DOMString realm;
+
+ int port;
+
+ DOMString username;
+
+ DOMString password;
+
+ DOMString resource;
+
+ DOMString jid;
+
+ int msgId;
+
+ TcpSocket *sock;
+
+ bool connected;
+
+ bool createSession();
+
+ bool checkConnect();
+
+ DOMString readStanza();
+
+ bool saslMd5Authenticate();
+
+ bool saslPlainAuthenticate();
+
+ bool saslAuthenticate();
+
+ bool iqAuthenticate(const DOMString &streamId);
+
+ bool keepGoing;
+
+ static const int writeBufLen = 2048;
+
+ unsigned char writeBuf[writeBufLen];
+
+ std::vector<XmppGroupChat *>groupChats;
+
+ static const int outputStreamCount = 16;
+
+ XmppStream *outputStreams[outputStreamCount];
+
+ static const int inputStreamCount = 16;
+
+ XmppStream *inputStreams[inputStreamCount];
+
+ static const int fileSendCount = 16;
+
+ XmppStream *fileSends[fileSendCount];
+
+ std::vector<XmppUser>roster;
+};
+
+
+
+
+//########################################################################
+//# X M P P G R O U P C H A T
+//########################################################################
+
+/**
+ *
+ */
+class XmppGroupChat
+{
+public:
+
+ /**
+ *
+ */
+ XmppGroupChat(const DOMString &groupJid);
+
+ /**
+ *
+ */
+ XmppGroupChat(const XmppGroupChat &other);
+
+ /**
+ *
+ */
+ virtual ~XmppGroupChat();
+
+ /**
+ *
+ */
+ virtual DOMString getGroupJid();
+
+ /**
+ *
+ */
+ virtual void userAdd(const DOMString &nick,
+ const DOMString &jid);
+ /**
+ *
+ */
+ virtual void userShow(const DOMString &nick,
+ const DOMString &show);
+
+ /**
+ *
+ */
+ virtual void userDelete(const DOMString &nick);
+
+ /**
+ *
+ */
+ virtual std::vector<XmppUser> getUserList() const;
+
+
+private:
+
+ DOMString groupJid;
+
+ std::vector<XmppUser>userList;
+
+};
+
+
+
+
+
+
+
+
+
+
+} //namespace Pedro
+
+#endif /* __XMPP_H__ */
+
+//########################################################################
+//# E N D O F F I L E
+//########################################################################
+
diff --git a/src/pedro/work/filerec.cpp b/src/pedro/work/filerec.cpp
--- /dev/null
@@ -0,0 +1,129 @@
+
+
+#include <stdio.h>
+
+#include "pedroxmpp.h"
+
+//########################################################################
+//# T E S T
+//########################################################################
+
+
+class TestListener : public Pedro::XmppEventListener
+{
+public:
+ TestListener()
+ {
+ incoming = false;
+ }
+
+ virtual ~TestListener(){}
+
+ virtual void processXmppEvent(const Pedro::XmppEvent &evt)
+ {
+ int typ = evt.getType();
+ switch (typ)
+ {
+ case Pedro::XmppEvent::EVENT_STATUS:
+ {
+ printf("STATUS: %s\n", evt.getData().c_str());
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_ERROR:
+ {
+ printf("ERROR: %s\n", evt.getData().c_str());
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_CONNECTED:
+ {
+ printf("CONNECTED\n");
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_DISCONNECTED:
+ {
+ printf("DISCONNECTED\n");
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_MUC_PRESENCE:
+ {
+ printf("MUC PRESENCE\n");
+ printf("group : %s\n", evt.getGroup().c_str());
+ printf("from : %s\n", evt.getFrom().c_str());
+ printf("presence: %d\n", evt.getPresence());
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_FILE_RECEIVE:
+ {
+ printf("FILE RECEIVE\n");
+ from = evt.getFrom();
+ streamId = evt.getStreamId();
+ iqId = evt.getIqId();
+ fileName = evt.getFileName();
+ fileHash = evt.getFileHash();
+ fileSize = evt.getFileSize();
+ incoming = true;
+ break;
+ }
+
+ }
+ }
+
+ Pedro::DOMString from;
+ Pedro::DOMString streamId;
+ Pedro::DOMString iqId;
+ Pedro::DOMString fileName;
+ Pedro::DOMString fileHash;
+ long fileSize;
+ bool incoming;
+};
+
+
+bool doTest()
+{
+ printf("############ RECEIVING FILE\n");
+
+ Pedro::XmppClient client;
+ TestListener listener;
+ client.addXmppEventListener(listener);
+
+ //Host, port, user, pass, resource
+ if (!client.connect("jabber.org.uk", 443, "ishmal", "PASSWORD", "filerec"))
+ {
+ printf("Connect failed\n");
+ return false;
+ }
+
+ while (true)
+ {
+ printf("####Waiting for file\n");
+ if (listener.incoming)
+ break;
+ client.pause(2000);
+ }
+
+ printf("#####GOT A FILE\n");
+
+ if (!client.fileReceive(listener.from,
+ listener.iqId,
+ listener.streamId,
+ listener.fileName,
+ "text.sav",
+ listener.fileHash))
+ {
+ return false;
+ }
+
+ client.pause(1000000);
+
+ client.disconnect();
+
+ return true;
+}
+
+int main(int argc, char **argv)
+{
+ if (!doTest())
+ return 1;
+ return 0;
+}
+
diff --git a/src/pedro/work/filesend.cpp b/src/pedro/work/filesend.cpp
--- /dev/null
@@ -0,0 +1,95 @@
+
+
+#include <stdio.h>
+
+#include "pedroxmpp.h"
+
+//########################################################################
+//# T E S T
+//########################################################################
+
+
+class TestListener : public Pedro::XmppEventListener
+{
+public:
+ TestListener(){}
+
+ virtual ~TestListener(){}
+
+ virtual void processXmppEvent(const Pedro::XmppEvent &evt)
+ {
+ int typ = evt.getType();
+ switch (typ)
+ {
+ case Pedro::XmppEvent::EVENT_STATUS:
+ {
+ printf("STATUS: %s\n", evt.getData().c_str());
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_ERROR:
+ {
+ printf("ERROR: %s\n", evt.getData().c_str());
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_CONNECTED:
+ {
+ printf("CONNECTED\n");
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_DISCONNECTED:
+ {
+ printf("DISCONNECTED\n");
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_MUC_PRESENCE:
+ {
+ printf("MUC PRESENCE\n");
+ printf("group : %s\n", evt.getGroup().c_str());
+ printf("from : %s\n", evt.getFrom().c_str());
+ printf("presence: %d\n", evt.getPresence());
+ break;
+ }
+
+ }
+ }
+};
+
+
+bool doTest()
+{
+ printf("############ SENDING FILE\n");
+
+ Pedro::XmppClient client;
+ TestListener listener;
+ client.addXmppEventListener(listener);
+
+ //Host, port, user, pass, resource
+ if (!client.connect("jabber.org.uk", 443, "ishmal", "PASSWORD", "filesend"))
+ {
+ printf("Connect failed\n");
+ return false;
+ }
+
+
+ if (!client.fileSend("ishmal@jabber.org.uk/filerec",
+ "server.pem" , "server.pem",
+ "a short story by edgar allen poe"))
+ {
+ return false;
+ }
+
+ printf("OK\n");
+ client.pause(1000000);
+
+ client.disconnect();
+
+ return true;
+}
+
+int main(int argc, char **argv)
+{
+ if (!doTest())
+ return 1;
+ return 0;
+}
+
diff --git a/src/pedro/work/groupchat.cpp b/src/pedro/work/groupchat.cpp
--- /dev/null
@@ -0,0 +1,221 @@
+
+
+#include <stdio.h>
+#include <string.h>
+
+#include "pedroxmpp.h"
+
+//########################################################################
+//# T E S T
+//########################################################################
+
+using namespace Pedro;
+
+
+class Listener : public Pedro::XmppEventListener
+{
+public:
+ Listener(){}
+
+ virtual ~Listener(){}
+
+ virtual void processXmppEvent(const Pedro::XmppEvent &evt)
+ {
+ int typ = evt.getType();
+ switch (typ)
+ {
+ case Pedro::XmppEvent::EVENT_STATUS:
+ {
+ printf("STATUS: %s\n", evt.getData().c_str());
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_ERROR:
+ {
+ printf("ERROR: %s\n", evt.getData().c_str());
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_CONNECTED:
+ {
+ printf("CONNECTED\n");
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_DISCONNECTED:
+ {
+ printf("DISCONNECTED\n");
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_MESSAGE:
+ {
+ printf("<%s> %s\n", evt.getFrom().c_str(), evt.getData().c_str());
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_PRESENCE:
+ {
+ printf("PRESENCE\n");
+ printf("from : %s\n", evt.getFrom().c_str());
+ printf("presence : %s\n", evt.getPresence().c_str());
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_MUC_MESSAGE:
+ {
+ printf("<%s> %s\n", evt.getFrom().c_str(), evt.getData().c_str());
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_MUC_JOIN:
+ {
+ printf("MUC JOIN\n");
+ printf("group: %s\n", evt.getGroup().c_str());
+ printf("from : %s\n", evt.getFrom().c_str());
+ printf("presence: %s\n", evt.getPresence().c_str());
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_MUC_LEAVE:
+ {
+ printf("MUC LEAVE\n");
+ printf("group: %s\n", evt.getGroup().c_str());
+ printf("from : %s\n", evt.getFrom().c_str());
+ printf("presence: %s\n", evt.getPresence().c_str());
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_MUC_PRESENCE:
+ {
+ printf("MUC PRESENCE\n");
+ printf("group : %s\n", evt.getGroup().c_str());
+ printf("from : %s\n", evt.getFrom().c_str());
+ printf("presence: %s\n", evt.getPresence().c_str());
+ break;
+ }
+
+ }
+ }
+};
+
+
+class CommandLineGroupChat
+{
+public:
+ CommandLineGroupChat(const DOMString &hostArg,
+ int portArg,
+ const DOMString &userArg,
+ const DOMString &passArg,
+ const DOMString &resourceArg,
+ const DOMString &groupJidArg,
+ const DOMString &nickArg)
+ {
+ host = hostArg;
+ port = portArg;
+ user = userArg;
+ pass = passArg;
+ resource = resourceArg;
+ groupJid = groupJidArg;
+ nick = nickArg;
+ }
+ ~CommandLineGroupChat()
+ {
+ client.disconnect();
+ }
+
+ virtual bool run();
+ virtual bool processCommandLine();
+
+private:
+
+ DOMString host;
+ int port;
+ DOMString user;
+ DOMString pass;
+ DOMString resource;
+ DOMString groupJid;
+ DOMString nick;
+
+ XmppClient client;
+
+};
+
+
+bool CommandLineGroupChat::run()
+{
+ Listener listener;
+ client.addXmppEventListener(listener);
+
+ //Host, port, user, pass, resource
+ if (!client.connect(host, port, user, pass, resource))
+ {
+ return false;
+ }
+
+ //Group jabber id, nick, pass
+ if (!client.groupChatJoin(groupJid, nick, ""))
+ {
+ printf("failed join\n");
+ return false;
+ }
+
+ //Allow receive buffer to clear out
+ client.pause(10000);
+
+ while (true)
+ {
+ if (!processCommandLine())
+ break;
+ }
+
+ //Group jabber id, nick
+ client.groupChatLeave(groupJid, nick);
+
+
+ client.disconnect();
+
+ return true;
+}
+
+
+bool CommandLineGroupChat::processCommandLine()
+{
+ char buf[512];
+ printf("send>:");
+ fgets(buf, 511, stdin);
+
+ if (buf[0]=='/')
+ {
+ if (strncmp(buf, "/q", 2)==0)
+ return false;
+ else
+ {
+ printf("Unknown command\n");
+ return true;
+ }
+ }
+
+ else
+ {
+ DOMString msg = buf;
+ if (msg.size() > 0 )
+ {
+ if (!client.groupChatMessage(groupJid, buf))
+ {
+ printf("failed message send\n");
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+int main(int argc, char **argv)
+{
+ if (argc!=8)
+ {
+ printf("usage: %s host port user pass resource groupid nick\n", argv[0]);
+ return 1;
+ }
+ int port = atoi(argv[2]);
+ CommandLineGroupChat groupChat(argv[1], port, argv[3], argv[4],
+ argv[5], argv[6], argv[7]);
+ if (!groupChat.run())
+ return 1;
+ return 0;
+}
+
diff --git a/src/pedro/work/inklayout.svg b/src/pedro/work/inklayout.svg
--- /dev/null
@@ -0,0 +1,378 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="841.88977pt"
+ height="595.27557pt"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.42+devel"
+ version="1.0"
+ sodipodi:docbase="/home/rjamison/pedro"
+ sodipodi:docname="inklayout.svg">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Mend"
+ style="overflow:visible;">
+ <path
+ id="path4241"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
+ transform="scale(0.4) rotate(180)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Mstart"
+ style="overflow:visible">
+ <path
+ id="path4244"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+ transform="scale(0.4)" />
+ </marker>
+ <marker
+ inkscape:stockid="TriangleInL"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="TriangleInL"
+ style="overflow:visible">
+ <path
+ id="path4158"
+ d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+ transform="scale(-0.8)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow2Lstart"
+ style="overflow:visible">
+ <path
+ id="path4232"
+ style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ transform="scale(1.1) translate(-5,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Lstart"
+ style="overflow:visible">
+ <path
+ id="path4250"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+ transform="scale(0.8)" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.98994949"
+ inkscape:cx="544.45061"
+ inkscape:cy="385.08312"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ fill="#000000"
+ inkscape:window-width="899"
+ inkscape:window-height="951"
+ inkscape:window-x="284"
+ inkscape:window-y="59" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <rect
+ y="20.094482"
+ x="424.57144"
+ height="37.142857"
+ width="240"
+ id="rect3148"
+ style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1.20000005;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.20000005;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect2273"
+ width="240"
+ height="37.142857"
+ x="418.57144"
+ y="14.094482" />
+ <rect
+ style="fill:none;fill-opacity:0.75;stroke:#000000;stroke-width:0.57683086;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect1358"
+ width="83.244057"
+ height="35.076485"
+ x="191.22281"
+ y="182.43959" />
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="199.27339"
+ y="203.54922"
+ id="text2233"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan2235"
+ x="199.27339"
+ y="203.54922">Private chat</tspan></text>
+ <rect
+ y="278.15387"
+ x="282.6514"
+ height="35.076485"
+ width="83.244057"
+ id="rect2237"
+ style="fill:none;fill-opacity:0.75;stroke:#000000;stroke-width:0.57683086;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <text
+ id="text2239"
+ y="331.4064"
+ x="252.13055"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="331.4064"
+ x="252.13055"
+ id="tspan2241"
+ sodipodi:role="line">Gui Client Main Window</tspan></text>
+ <rect
+ y="182.43959"
+ x="289.22281"
+ height="35.076485"
+ width="83.244057"
+ id="rect2243"
+ style="fill:none;fill-opacity:0.75;stroke:#000000;stroke-width:0.57683086;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <text
+ id="text2245"
+ y="204.97781"
+ x="302.9877"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"
+ sodipodi:linespacing="125%"><tspan
+ y="204.97781"
+ x="302.9877"
+ id="tspan2247"
+ sodipodi:role="line">Group chat</tspan></text>
+ <rect
+ style="fill:none;fill-opacity:0.75;stroke:#000000;stroke-width:0.62177706;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect2249"
+ width="96.722092"
+ height="35.076485"
+ x="391.2695"
+ y="182.43959" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="419.9877"
+ y="173.54926"
+ id="text2251"><tspan
+ sodipodi:role="line"
+ id="tspan2253"
+ x="419.9877"
+ y="173.54926">Dialog</tspan></text>
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 249.87329,217.80449 L 307.24497,277.86545"
+ id="path2255"
+ inkscape:connector-type="polyline"
+ inkscape:connection-start="#rect1358"
+ inkscape:connection-end="#rect2237" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 329.62092,217.80449 L 325.49734,277.86545"
+ id="path2257"
+ inkscape:connector-type="polyline"
+ inkscape:connection-start="#rect2243"
+ inkscape:connection-end="#rect2237" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 418.11835,217.82696 L 345.75854,277.86545"
+ id="path2259"
+ inkscape:connector-type="polyline"
+ inkscape:connection-start="#rect2249"
+ inkscape:connection-end="#rect2237" />
+ <text
+ xml:space="preserve"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="428.57141"
+ y="39.808769"
+ id="text2261"
+ sodipodi:linespacing="100%"><tspan
+ sodipodi:role="line"
+ id="tspan2263"
+ x="428.57141"
+ y="39.808769">Inkboard Layout</tspan></text>
+ <rect
+ y="182.02116"
+ x="582.86676"
+ height="35.076485"
+ width="83.244057"
+ id="rect3150"
+ style="fill:none;fill-opacity:0.75;stroke:#000000;stroke-width:0.57683086;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <text
+ id="text3152"
+ y="173.84511"
+ x="603.06018"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="173.84511"
+ x="603.06018"
+ id="tspan3154"
+ sodipodi:role="line">Session</tspan></text>
+ <rect
+ style="fill:none;fill-opacity:0.75;stroke:#000000;stroke-width:0.57683086;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect3156"
+ width="83.244057"
+ height="35.076485"
+ x="674.29535"
+ y="277.73547" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="664.48877"
+ y="330.98801"
+ id="text3158"><tspan
+ sodipodi:role="line"
+ id="tspan3160"
+ x="664.48877"
+ y="330.98801">Session Manager</tspan></text>
+ <rect
+ style="fill:none;fill-opacity:0.75;stroke:#000000;stroke-width:0.57683086;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect3162"
+ width="83.244057"
+ height="35.076485"
+ x="680.86676"
+ y="182.02116" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="701.06018"
+ y="173.84511"
+ id="text3164"><tspan
+ sodipodi:role="line"
+ id="tspan3166"
+ x="701.06018"
+ y="173.84511">Session</tspan></text>
+ <rect
+ y="182.02116"
+ x="782.86676"
+ height="35.076485"
+ width="83.244057"
+ id="rect3168"
+ style="fill:none;fill-opacity:0.75;stroke:#000000;stroke-width:0.57683086;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <text
+ id="text3170"
+ y="173.84511"
+ x="803.06018"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="173.84511"
+ x="803.06018"
+ id="tspan3172"
+ sodipodi:role="line">Session</tspan></text>
+ <path
+ inkscape:connection-end="#rect2237"
+ inkscape:connection-start="#rect1358"
+ inkscape:connector-type="polyline"
+ id="path3174"
+ d="M 249.87329,217.80449 L 307.24497,277.86545"
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <text
+ id="text3180"
+ y="195.69208"
+ x="407.84482"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"
+ sodipodi:linespacing="125%"><tspan
+ y="195.69208"
+ x="407.84482"
+ id="tspan3182"
+ sodipodi:role="line">Private chat</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="393.70197"
+ y="208.54922"
+ id="text3184"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3186"
+ x="393.70197"
+ y="208.54922">w/ group member</tspan></text>
+ <text
+ id="text3188"
+ y="174.26353"
+ x="311.41626"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="174.26353"
+ x="311.41626"
+ id="tspan3190"
+ sodipodi:role="line">Dialog</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="213.41626"
+ y="174.26353"
+ id="text3192"><tspan
+ sodipodi:role="line"
+ id="tspan3194"
+ x="213.41626"
+ y="174.26353">Dialog</tspan></text>
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 804.26751,217.38606 L 736.13865,277.44706"
+ id="path3196"
+ inkscape:connector-type="polyline"
+ inkscape:connection-start="#rect3168"
+ inkscape:connection-end="#rect3156" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 721.26487,217.38606 L 717.14129,277.44706"
+ id="path3198"
+ inkscape:connector-type="polyline"
+ inkscape:connection-start="#rect3162"
+ inkscape:connection-end="#rect3156" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 641.51724,217.38606 L 698.88893,277.44706"
+ id="path3200"
+ inkscape:connector-type="polyline"
+ inkscape:connection-start="#rect3150"
+ inkscape:connection-end="#rect3156" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:6.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;marker-start:url(#Arrow1Mstart);marker-end:url(#Arrow1Mend)"
+ d="M 452.54834,260.23141 C 615.1829,260.23141 616.19305,260.23141 616.19305,260.23141"
+ id="path3202" />
+ </g>
+</svg>
diff --git a/src/pedro/work/test.cpp b/src/pedro/work/test.cpp
--- /dev/null
+++ b/src/pedro/work/test.cpp
@@ -0,0 +1,149 @@
+
+
+#include <stdio.h>
+
+#include "pedroxmpp.h"
+
+//########################################################################
+//# T E S T
+//########################################################################
+
+
+class TestListener : public Pedro::XmppEventListener
+{
+public:
+ TestListener(){}
+
+ virtual ~TestListener(){}
+
+ virtual void processXmppEvent(const Pedro::XmppEvent &evt)
+ {
+ int typ = evt.getType();
+ switch (typ)
+ {
+ case Pedro::XmppEvent::EVENT_STATUS:
+ {
+ printf("STATUS: %s\n", evt.getData().c_str());
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_ERROR:
+ {
+ printf("ERROR: %s\n", evt.getData().c_str());
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_CONNECTED:
+ {
+ printf("CONNECTED\n");
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_DISCONNECTED:
+ {
+ printf("DISCONNECTED\n");
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_MESSAGE:
+ {
+ printf("MESSAGE\n");
+ printf("from : %s\n", evt.getFrom().c_str());
+ printf("msg : %s\n", evt.getData().c_str());
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_PRESENCE:
+ {
+ printf("PRESENCE\n");
+ printf("from : %s\n", evt.getFrom().c_str());
+ printf("presence : %d\n", evt.getPresence());
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_MUC_MESSAGE:
+ {
+ printf("MUC GROUP MESSAGE\n");
+ printf("group: %s\n", evt.getGroup().c_str());
+ printf("from : %s\n", evt.getFrom().c_str());
+ printf("msg : %s\n", evt.getData().c_str());
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_MUC_JOIN:
+ {
+ printf("MUC JOIN\n");
+ printf("group: %s\n", evt.getGroup().c_str());
+ printf("from : %s\n", evt.getFrom().c_str());
+ printf("presence: %d\n", evt.getPresence());
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_MUC_LEAVE:
+ {
+ printf("MUC LEAVE\n");
+ printf("group: %s\n", evt.getGroup().c_str());
+ printf("from : %s\n", evt.getFrom().c_str());
+ printf("presence: %d\n", evt.getPresence());
+ break;
+ }
+ case Pedro::XmppEvent::EVENT_MUC_PRESENCE:
+ {
+ printf("MUC PRESENCE\n");
+ printf("group : %s\n", evt.getGroup().c_str());
+ printf("from : %s\n", evt.getFrom().c_str());
+ printf("presence: %d\n", evt.getPresence());
+ break;
+ }
+
+ }
+ }
+};
+
+
+bool doTest()
+{
+ printf("############ TESTING\n");
+
+ char *groupJid = "inkscape@conference.gristle.org";
+
+ Pedro::XmppClient client;
+ TestListener listener;
+ client.addXmppEventListener(listener);
+
+ //Host, port, user, pass, resource
+ if (!client.connect("jabber.org.uk", 443, "ishmal", "PASSWORD", "myclient"))
+ {
+ printf("Connect failed\n");
+ return false;
+ }
+
+ //Group jabber id, nick, pass
+ client.groupChatJoin(groupJid, "mynick", "");
+
+ client.pause(8000);
+
+ //Group jabber id, nick, msg
+ //client.groupChatMessage(groupJid, "hello, world");
+
+ client.pause(3000);
+
+ //client.groupChatGetUserList(groupJid);
+
+ client.pause(3000);
+
+ //client.groupChatPrivateMessage("inkscape2@conference.gristle.org",
+ // "ishmal", "hello, world");
+ client.message("ishmal@jabber.org.uk/https", "hey, bob");
+
+ client.pause(60000);
+
+ //Group jabber id, nick
+ client.groupChatLeave(groupJid, "mynick");
+
+ client.pause(1000000);
+
+ client.disconnect();
+
+ return true;
+}
+
+int main(int argc, char **argv)
+{
+ if (!doTest())
+ return 1;
+ return 0;
+}
+