Code

Move from the jabber_whiteboard directory to its own, so that it can be updated in...
authorishmal <ishmal@users.sourceforge.net>
Mon, 15 May 2006 15:06:21 +0000 (15:06 +0000)
committerishmal <ishmal@users.sourceforge.net>
Mon, 15 May 2006 15:06:21 +0000 (15:06 +0000)
27 files changed:
src/pedro/Makefile.mingw [new file with mode: 0644]
src/pedro/certs/client.pem [new file with mode: 0644]
src/pedro/certs/dh1024.pem [new file with mode: 0644]
src/pedro/certs/root.pem [new file with mode: 0644]
src/pedro/certs/server.pem [new file with mode: 0644]
src/pedro/icon/Thumbs.db [new file with mode: 0644]
src/pedro/icon/available.png [new file with mode: 0644]
src/pedro/icon/away.png [new file with mode: 0644]
src/pedro/icon/chat.png [new file with mode: 0644]
src/pedro/icon/dnd.png [new file with mode: 0644]
src/pedro/icon/error.png [new file with mode: 0644]
src/pedro/icon/offline.png [new file with mode: 0644]
src/pedro/icon/xa.png [new file with mode: 0644]
src/pedro/mingwenv.bat [new file with mode: 0644]
src/pedro/pedro.bat [new file with mode: 0644]
src/pedro/pedrodom.cpp [new file with mode: 0644]
src/pedro/pedrodom.h [new file with mode: 0644]
src/pedro/pedrogui.cpp [new file with mode: 0644]
src/pedro/pedrogui.h [new file with mode: 0644]
src/pedro/pedromain.cpp [new file with mode: 0644]
src/pedro/pedroxmpp.cpp [new file with mode: 0644]
src/pedro/pedroxmpp.h [new file with mode: 0644]
src/pedro/work/filerec.cpp [new file with mode: 0644]
src/pedro/work/filesend.cpp [new file with mode: 0644]
src/pedro/work/groupchat.cpp [new file with mode: 0644]
src/pedro/work/inklayout.svg [new file with mode: 0644]
src/pedro/work/test.cpp [new file with mode: 0644]

diff --git a/src/pedro/Makefile.mingw b/src/pedro/Makefile.mingw
new file mode 100644 (file)
index 0000000..693dfca
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..06f2e6c
--- /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
new file mode 100644 (file)
index 0000000..aa68d98
--- /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
new file mode 100644 (file)
index 0000000..db0c59f
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..87376db
--- /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
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
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
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
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
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
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
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
diff --git a/src/pedro/mingwenv.bat b/src/pedro/mingwenv.bat
new file mode 100644 (file)
index 0000000..996566e
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..848bec5
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..5ac4a61
--- /dev/null
@@ -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[] =
+{
+    { "&amp;" , '&'  },
+    { "&lt;"  , '<'  },
+    { "&gt;"  , '>'  },
+    { "&apos;", '\'' },
+    { "&quot;", '"'  },
+    { 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("&amp;");
+        else if (ch == '<')
+            ret.append("&lt;");
+        else if (ch == '>')
+            ret.append("&gt;");
+        else if (ch == '\'')
+            ret.append("&apos;");
+        else if (ch == '"')
+            ret.append("&quot;");
+        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
new file mode 100644 (file)
index 0000000..9c66512
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..e2ab05a
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..b42ee7a
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..9cee6d9
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..f5ce69e
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..d938a25
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..9fc8b86
--- /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
new file mode 100644 (file)
index 0000000..7a114ab
--- /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
new file mode 100644 (file)
index 0000000..38a17ab
--- /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
new file mode 100644 (file)
index 0000000..740a2de
--- /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
new file mode 100644 (file)
index 0000000..be0676e
--- /dev/null
@@ -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;
+}
+