From: ishmal Date: Mon, 15 May 2006 15:06:21 +0000 (+0000) Subject: Move from the jabber_whiteboard directory to its own, so that it can be updated in... X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=85645cf893478597c5d9c446a692a39108af79cc;p=inkscape.git Move from the jabber_whiteboard directory to its own, so that it can be updated in parallel. --- diff --git a/src/pedro/Makefile.mingw b/src/pedro/Makefile.mingw new file mode 100644 index 000000000..693dfca89 --- /dev/null +++ b/src/pedro/Makefile.mingw @@ -0,0 +1,183 @@ +########################################################################### +# +# +# Makefile for the Pedro mini-XMPP client +# +# Authors: +# Bob Jamison +# +# Copyright (C) 2005-2006 Bob Jamison +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +# +########################################################################## +# FILE SEPARATORS +# $(S) will be set to one of these +########################################################################## +BSLASH := \\# +FSLASH := / + +########################################################################## +# SENSE ARCHITECTURE +########################################################################## +ifdef ComSpec +ARCH=win32 +else +ARCH=xlib +endif + +########################################################################## +# WIN32 SETTINGS +########################################################################## +ifeq ($(ARCH),win32) + +####### Where is your GTK directory? +GTK=c:/gtk28 + +####### Same thing, DOS style +GTKDOS=c:\gtk28 + +#SSL=openssl-0.9.8a +SSL=$(GTK) + +CFLAGS = -g -Wall -DHAVE_SSL +INC = -I. -I$(GTK)/include -I$(SSL)/include +LIBS = -mwindows -L$(GTK)/lib -L$(SSL) -lssl -lcrypto -lgdi32 -lws2_32 +RM = del +CP = copy +S = $(BSLASH) + + +all: test.exe pedro.exe + +GTKINC = -DGLIBMM_DLL \ +-I$(GTK)/include/glibmm-2.4 -I$(GTK)/lib/glibmm-2.4/include \ +-I$(GTK)/include/gtkmm-2.4 -I$(GTK)/lib/gtkmm-2.4/include \ +-I$(GTK)/include/gdkmm-2.4 -I$(GTK)/lib/gdkmm-2.4/include \ +-I$(GTK)/include/pangomm-1.4 \ +-I$(GTK)/include/atkmm-1.6 -I$(GTK)/include/cairo \ +-I$(GTK)/include/sigc++-2.0 -I$(GTK)/lib/sigc++-2.0/include \ +-I$(GTK)/include/gtk-2.0 -I$(GTK)/lib/gtk-2.0/include \ +-I$(GTK)/include/atk-1.0 -I$(GTK)/include/pango-1.0 \ +-I$(GTK)/include/glib-2.0 -I$(GTK)/lib/glib-2.0/include + + +####### Our Gtk libs +GTKLIBS = -L$(GTK)/lib -lloudmouth-1 \ +-lgtkmm-2.4 -lgdkmm-2.4 -lglibmm-2.4 \ +-latkmm-1.6 -lpangomm-1.4 -lsigc-2.0 \ +-lgtk-win32-2.0 -lgdk-win32-2.0 -latk-1.0 \ +-lgdk_pixbuf-2.0 -lm -lpangoft2-1.0 -lpangowin32-1.0 -lpango-1.0 \ +-lgobject-2.0 -lgmodule-2.0 -lgthread-2.0 -lglib-2.0 + +endif + + +########################################################################## +# XLIB SETTINGS +########################################################################## +ifeq ($(ARCH),xlib) + +CFLAGS = -g -Wall -DHAVE_SSL -DHAVE_PTHREAD_H +XINC = -I/usr/X11R6/include +XLIB = -L/usr/X11R6/lib -lXrender -lX11 +INC = -I. -I.. $(XINC) +LIBS = $(XLIB) -lpthread -lssl +RM = rm -rf +CP = cp +S = $(FSLASH) +all: test pedro + +GTKINC += `pkg-config gtkmm-2.4 --cflags` +GTKLIBS += `pkg-config gtkmm-2.4 --libs` + +endif + + + +OBJ = \ +pedrodom.o \ +pedroxmpp.o + +GUIOBJ = \ +pedrogui.o \ +pedromain.o + +TESTOBJ = \ +work/test.o \ +work/filesend.o \ +work/filerec.o \ +work/groupchat.o + +test.exe: libpedro.a work/test.o + $(CXX) -o $@ work/test.o libpedro.a $(LIBS) +test: libpedro.a work/test.o + $(CXX) -o $@ work/test.o libpedro.a $(LIBS) + +filesend.exe: libpedro.a work/filesend.o + $(CXX) -o $@ work/filesend.o libpedro.a $(LIBS) +filesend: libpedro.a work/filesend.o + $(CXX) -o $@ work/filesend.o libpedro.a $(LIBS) + +filerec.exe: libpedro.a work/filerec.o + $(CXX) -o $@ work/filerec.o libpedro.a $(LIBS) +filerec: libpedro.a work/filerec.o + $(CXX) -o $@ work/filerec.o libpedro.a $(LIBS) + +groupchat.exe: libpedro.a work/groupchat.o + $(CXX) -o $@ work/groupchat.o libpedro.a $(LIBS) +groupchat: libpedro.a work/groupchat.o + $(CXX) -o $@ work/groupchat.o libpedro.a $(LIBS) + +pedro.exe: libpedro.a pedromain.o pedrogui.o + $(CXX) -o $@ pedromain.o pedrogui.o libpedro.a $(GTKLIBS) $(LIBS) +pedro: libpedro.a pedromain.o pedrogui.o + $(CXX) -o $@ pedromain.o pedrogui.o libpedro.a $(GTKLIBS) $(LIBS) + + +libpedro.a: $(OBJ) + ar crv libpedro.a $(OBJ) + +pedromain.o: pedromain.cpp + $(CXX) $(CFLAGS) $(INC) $(GTKINC) -c -o $@ $< + +pedrogui.o: pedrogui.cpp pedrogui.h + $(CXX) $(CFLAGS) $(INC) $(GTKINC) -c -o $@ $< + +.cpp.o: + $(CXX) $(CFLAGS) $(INC) -c -o $@ $< + +clean: + $(foreach a, $(OBJ), $(shell $(RM) $(subst /,$(S), $(a)))) + $(foreach a, $(GUIOBJ), $(shell $(RM) $(subst /,$(S), $(a)))) + $(foreach a, $(TESTOBJ), $(shell $(RM) $(subst /,$(S), $(a)))) + -$(RM) *.a + -$(RM) test + -$(RM) test.exe + -$(RM) filesend + -$(RM) filesend.exe + -$(RM) filerec + -$(RM) filerec.exe + -$(RM) groupchat + -$(RM) groupchat.exe + -$(RM) pedro + -$(RM) pedro.exe + -$(RM) core.* + +########################################################################### +# E N D O F F I L E +########################################################################### + diff --git a/src/pedro/certs/client.pem b/src/pedro/certs/client.pem new file mode 100644 index 000000000..06f2e6ce0 --- /dev/null +++ b/src/pedro/certs/client.pem @@ -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 index 000000000..aa68d98ec --- /dev/null +++ b/src/pedro/certs/dh1024.pem @@ -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 index 000000000..db0c59fbf --- /dev/null +++ b/src/pedro/certs/root.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICIjCCAYugAwIBAgIBADANBgkqhkiG9w0BAQQFADBXMQswCQYDVQQGEwJVUzET +MBEGA1UEChMKUlRGTSwgSW5jLjEZMBcGA1UECxMQV2lkZ2V0cyBEaXZpc2lvbjEY +MBYGA1UEAxMPVGVzdCBDQTIwMDEwNTE3MB4XDTAxMDUxNzE2MDExNFoXDTA2MTIy +NTE2MDExNFowVzELMAkGA1UEBhMCVVMxEzARBgNVBAoTClJURk0sIEluYy4xGTAX +BgNVBAsTEFdpZGdldHMgRGl2aXNpb24xGDAWBgNVBAMTD1Rlc3QgQ0EyMDAxMDUx +NzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAmkX40warmH0+lnwD9YjsJhRz +ZX6qXadFry0y2trZ6gMs8Mv33IKPwOu8TE7V+3PESEtjI2wr8juV9OkbIPOm+td5 +M8+6vXyIW+JBo3ch99i0QMTf5/jTgsW+3IjV8yEdiGcZFp2NWKLRvZPq2VRbuF7R +1pvgcaRuBJ0wGOohwnsCAwEAATANBgkqhkiG9w0BAQQFAAOBgQCUB8zMKIlX5io8 +TalbzH9Qke7BcvFAL+wp/5w1ToVsWkNrINSWKv6bl/jcqOD3aPhK7qhaeOU8ZWKL +PoPPCnRl9Wo+1JtsOO3qIgJP79Bl9ooLGahixF2v/gea5qNISjQvwYllLSa//APP +6kXHngO0RIRbiTBYHSkAzm6hDdsvVA== +-----END CERTIFICATE----- diff --git a/src/pedro/certs/server.pem b/src/pedro/certs/server.pem new file mode 100644 index 000000000..87376dbf0 --- /dev/null +++ b/src/pedro/certs/server.pem @@ -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 index 000000000..15c63291b 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 index 000000000..e88ebd45b 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 index 000000000..9bb899869 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 index 000000000..84ac5945a 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 index 000000000..25c7a6e5f 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 index 000000000..35febd2ba 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 index 000000000..06284fa30 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 index 000000000..0911e8a76 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 index 000000000..996566e7b --- /dev/null +++ b/src/pedro/mingwenv.bat @@ -0,0 +1,2 @@ +set PATH=c:\mingw\bin;%PATH% +set RM=del diff --git a/src/pedro/pedro.bat b/src/pedro/pedro.bat new file mode 100644 index 000000000..848bec5e0 --- /dev/null +++ b/src/pedro/pedro.bat @@ -0,0 +1,2 @@ +set path=c:\inkscape;%path% +pedro.exe diff --git a/src/pedro/pedrodom.cpp b/src/pedro/pedrodom.cpp new file mode 100644 index 000000000..5ac4a61d0 --- /dev/null +++ b/src/pedro/pedrodom.cpp @@ -0,0 +1,782 @@ +/* + * Implementation of the Pedro mini-DOM parser and tree + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + + +#include +#include +#include +#include +#include +#include + + +#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::iterator iter; + for (iter = children.begin(); iter != children.end() ; iter++) + { + elem->addChild((*iter)->clone()); + } + return elem; +} + + +void Element::findElementsRecursive(std::vector&res, const DOMString &name) +{ + if (getName() == name) + { + res.push_back(this); + } + for (unsigned int i=0; ifindElementsRecursive(res, name); +} + +std::vector Element::findElements(const DOMString &name) +{ + std::vector res; + findElementsRecursive(res, name); + return res; +} + +DOMString Element::getAttribute(const DOMString &name) +{ + for (unsigned int i=0 ; ielems = findElements(tagName); + if (elems.size() <1) + return ""; + DOMString res = elems[0]->getAttribute(attrName); + return res; +} + +DOMString Element::getTagValue(const DOMString &tagName) +{ + std::vectorelems = 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\n"); + + //Between the tags + if (value.size() > 0) + { + for (int i=0;iwriteIndentedRecursive(f, indent+2); + + //Closing tag + for (int i=0; i\n", name.c_str()); +} + +void Element::writeIndented(FILE *f) +{ + writeIndentedRecursive(f, 0); +} + +void Element::print() +{ + writeIndented(stdout); +} + + +//######################################################################## +//# P A R S E R +//######################################################################## + + + +typedef struct + { + char *escaped; + char value; + } EntityEntry; + +static EntityEntry entities[] = +{ + { "&" , '&' }, + { "<" , '<' }, + { ">" , '>' }, + { "'", '\'' }, + { """, '"' }, + { NULL , '\0' } +}; + + + +void Parser::getLineAndColumn(long pos, long *lineNr, long *colNr) +{ + long line = 1; + long col = 1; + for (long i=0 ; i= 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') + ret.append(">"); + else if (ch == '\'') + ret.append("'"); + else if (ch == '"') + ret.append("""); + else + ret.push_back(ch); + + } + return ret; +} + + +int Parser::match(long p0, const char *text) +{ + int p = p0; + while (*text) + { + if (peek(p) != *text) + return p0; + p++; text++; + } + return p; +} + + + +int Parser::skipwhite(long p) +{ + + while (p p) + { + p = p2; + while (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' || 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 ( pvalue ; 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 || peek(p)!='<') + return p0; + + p++; + + if (peek(p)!='!' || peek(p+1)=='-') + return p0; + p++; + + DOMString buf; + while (p') + { + 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') + break; + else if (ch=='/' && p') + { + 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 (pp) + { + p = p2; + while (p"); + if (p2 > p) + { + p = p2; + break; + } + p++; + } + } + + ch = peek(p); + //# END TAG + if (ch=='<' && !cdata && peek(p+1)=='/') + { + break; + } + //# CDATA + p2 = match(p, " 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 . 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 \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 index 000000000..9c6651259 --- /dev/null +++ b/src/pedro/pedrodom.h @@ -0,0 +1,343 @@ +#ifndef __PEDRODOM_H__ +#define __PEDRODOM_H__ +/* + * API for the Pedro mini-DOM parser and tree + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005-2006 Bob Jamison + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + + +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 getChildren() + { return children; } + + std::vector 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&res, const DOMString &name); + + void writeIndentedRecursive(FILE *f, int indent); + + Element *parent; + + std::vectorchildren; + + std::vector attributes; + std::vector 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 index 000000000..e2ab05a2e --- /dev/null +++ b/src/pedro/pedrogui.cpp @@ -0,0 +1,2175 @@ +/* + * Simple demo GUI for the Pedro mini-XMPP client. + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "pedrogui.h" + +#include + +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 model = rosterView.get_model(); + Glib::RefPtr 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 model = rosterView.get_model(); + Glib::RefPtr 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 model = rosterView.get_model(); + Glib::RefPtr 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(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 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 = + "" + " " + " " + " " + " " + ""; + + 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 items = parent->client.getRoster(); + + //## Add in tree fashion + DOMString lastGroup = ""; + Gtk::TreeModel::Row row = *(treeStore->append()); + row[rosterColumns.groupColumn] = ""; + for (unsigned int i=0 ; i0) + row = *(treeStore->append()); + row[rosterColumns.groupColumn] = user.group; + lastGroup = user.group; + } + Glib::RefPtr 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 table = + messageListBuffer->get_tag_table(); + Glib::RefPtr color0 = + Gtk::TextBuffer::Tag::create("color0"); + color0->property_foreground() = "DarkGreen"; + color0->property_weight() = Pango::WEIGHT_BOLD; + table->add(color0); + Glib::RefPtr color1 = + Gtk::TextBuffer::Tag::create("color1"); + color1->property_foreground() = "chocolate4"; + color1->property_weight() = Pango::WEIGHT_BOLD; + table->add(color1); + Glib::RefPtr color2 = + Gtk::TextBuffer::Tag::create("color2"); + color2->property_foreground() = "red4"; + color2->property_weight() = Pango::WEIGHT_BOLD; + table->add(color2); + Glib::RefPtr color3 = + Gtk::TextBuffer::Tag::create("color3"); + color3->property_foreground() = "MidnightBlue"; + color3->property_weight() = Pango::WEIGHT_BOLD; + table->add(color3); + Glib::RefPtr color4 = + Gtk::TextBuffer::Tag::create("color4"); + color4->property_foreground() = "turquoise4"; + color4->property_weight() = Pango::WEIGHT_BOLD; + table->add(color4); + Glib::RefPtr color5 = + Gtk::TextBuffer::Tag::create("color5"); + color5->property_foreground() = "OliveDrab"; + color5->property_weight() = Pango::WEIGHT_BOLD; + table->add(color5); + Glib::RefPtr color6 = + Gtk::TextBuffer::Tag::create("color6"); + color6->property_foreground() = "purple4"; + color6->property_weight() = Pango::WEIGHT_BOLD; + table->add(color6); + Glib::RefPtr 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 ; iinsert_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 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 model = userList.get_model(); + Glib::RefPtr 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 model = userList.get_model(); + Glib::RefPtr 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 model = userList.get_model(); + Glib::RefPtr 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(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 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 = + "" + " " + " " + " " + " " + ""; + + 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 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 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 uiManager = Gtk::UIManager::create(); + + uiManager->insert_action_group(actionGroup, 0); + add_accel_group(uiManager->get_accel_group()); + + Glib::ustring ui_info = + "" + " " + " " + " " + " " + " " + ""; + + 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 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 uiManager = Gtk::UIManager::create(); + + uiManager->insert_action_group(actionGroup, 0); + add_accel_group(uiManager->get_accel_group()); + + Glib::ustring ui_info = + "" + " " + " " + " " + " " + " " + ""; + + 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 memberList = + parent.client.groupChatGetUserList(groupJid); + for (unsigned int i=0 ; i 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 uiManager = Gtk::UIManager::create(); + + uiManager->insert_action_group(actionGroup, 0); + add_accel_group(uiManager->get_accel_group()); + + Glib::ustring ui_info = + "" + " " + " " + " " + " " + " " + " " + " " + ""; + + 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 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 uiManager = Gtk::UIManager::create(); + + uiManager->insert_action_group(actionGroup, 0); + add_accel_group(uiManager->get_accel_group()); + + Glib::ustring ui_info = + "" + " " + " " + " " + " " + " " + " " + " " + ""; + + 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 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 uiManager = Gtk::UIManager::create(); + + uiManager->insert_action_group(actionGroup, 0); + add_accel_group(uiManager->get_accel_group()); + + Glib::ustring ui_info = + "" + " " + " " + " " + " " + " " + " " + " " + ""; + + 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 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 uiManager = Gtk::UIManager::create(); + + uiManager->insert_action_group(actionGroup, 0); + add_accel_group(uiManager->get_accel_group()); + + Glib::ustring ui_info = + "" + " " + " " + " " + " " + " " + " " + " " + ""; + + 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 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 uiManager = Gtk::UIManager::create(); + + uiManager->insert_action_group(actionGroup, 0); + add_accel_group(uiManager->get_accel_group()); + + Glib::ustring ui_info = + "" + " " + " " + " " + " " + " " + " " + " " + ""; + + 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::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::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::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::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::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::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::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::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::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::vectorauthors; + 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 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 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 = + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + + 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 index 000000000..b42ee7ab8 --- /dev/null +++ b/src/pedro/pedrogui.h @@ -0,0 +1,731 @@ +#ifndef __PEDROGUI_H__ +#define __PEDROGUI_H__ +/* + * Simple demo GUI for the Pedro mini-XMPP client. + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + + +#include + +#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 pixbuf_available; + Glib::RefPtr pixbuf_away; + Glib::RefPtr pixbuf_chat; + Glib::RefPtr pixbuf_dnd; + Glib::RefPtr pixbuf_error; + Glib::RefPtr pixbuf_offline; + Glib::RefPtr pixbuf_xa; + + class RosterColumns : public Gtk::TreeModel::ColumnRecord + { + public: + RosterColumns() + { + add(groupColumn); + add(statusColumn); add(userColumn); + add(nameColumn); add(subColumn); + } + + Gtk::TreeModelColumn groupColumn; + Gtk::TreeModelColumn > statusColumn; + Gtk::TreeModelColumn userColumn; + Gtk::TreeModelColumn nameColumn; + Gtk::TreeModelColumn subColumn; + }; + + RosterColumns rosterColumns; + + Glib::RefPtr uiManager; + + Glib::RefPtr 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 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 pixbuf_available; + Glib::RefPtr pixbuf_away; + Glib::RefPtr pixbuf_chat; + Glib::RefPtr pixbuf_dnd; + Glib::RefPtr pixbuf_error; + Glib::RefPtr pixbuf_offline; + Glib::RefPtr pixbuf_xa; + + class UserListColumns : public Gtk::TreeModel::ColumnRecord + { + public: + UserListColumns() + { add(statusColumn); add(userColumn); } + + Gtk::TreeModelColumn userColumn; + Gtk::TreeModelColumn > statusColumn; + }; + + UserListColumns userListColumns; + + Glib::RefPtr uiManager; + + Glib::RefPtr 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 uiManager; + void actionEnable(const DOMString &name, bool val); + + std::vectorchats; + + std::vectorgroupChats; + + 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 index 000000000..9cee6d992 --- /dev/null +++ b/src/pedro/pedromain.cpp @@ -0,0 +1,32 @@ +#include + +#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 + + +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 index 000000000..f5ce69e0b --- /dev/null +++ b/src/pedro/pedroxmpp.cpp @@ -0,0 +1,4842 @@ +/* + * Implementation the Pedro mini-XMPP client + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include +#include + +#include + +#include "pedroxmpp.h" +#include "pedrodom.h" + +#ifdef __WIN32__ + +#include + +#else /* UNIX */ + +#include +#include +#include +#include +#include +#include + +#include + +#endif + +#ifdef HAVE_SSL +#include +#include +#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 finish(); + + static std::vector decode(const DOMString &str); + + static DOMString decodeToString(const DOMString &str); + +private: + + int inBytes[4]; + int inCount; + std::vector 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 Base64Decoder::finish() +{ + std::vector ret = buf; + reset(); + return ret; +} + +std::vector Base64Decoder::decode(const DOMString &str) +{ + Base64Decoder decoder; + decoder.append(str); + std::vector ret = decoder.finish(); + return ret; +} + +DOMString Base64Decoder::decodeToString(const DOMString &str) +{ + Base64Decoder decoder; + decoder.append(str); + std::vector ret = decoder.finish(); + DOMString buf; + for (unsigned int i=0 ; i>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<>(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 XmppEvent::getUserList() const +{ + return userList; +} + +void XmppEvent::setUserList(const std::vector &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::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::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::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 &newData); + + /** + * + */ + std::vector read(); + +private: + + + DOMString streamId; + + DOMString iqId; + + DOMString sourceId; + + int state; + + long seqNr; + + std::vector 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 &newData) +{ + std::vector::iterator iter; + for (iter=newData.begin() ; iter!=newData.end() ; iter++) + data.push_back(*iter); +} + +/** + * + */ +std::vector XmppStream::read() +{ + if (state != XmppClient::STREAM_OPEN) + { + std::vectordummy; + return dummy; + } + std::vector 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 ; i0 ; 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 tag + if (openCount <= 0 && !querySeen) + { + break; + } + //we know that this one will be open-ended + if (strIndex(buf, "= 0) + { + buf.append(""); + break; + } + } + else if (ch == '/') + { + if (inTag && !inQuote) + { + slashSeen = true; + if (textSeen) // <--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::vectorelems = root->findElements("x"); + for (unsigned int i=0 ; igetAttribute("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 ; igetStreamId().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::vectorbinData = + 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 = + "" + "" + "" + "\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 ; igetIqId() == 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 ; igetStreamId() == 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 ; igetStreamId() == streamId && + from == ins->getPeerId()) + { + ins->setState(STREAM_CLOSING); + ins->setIqId(id); + return true; + } + } + } + return true; + } + //### Responses to outgoing requests + for (int i=0 ; igetIqId() == 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::vectorelems = root->findElements("item"); + for (unsigned int i=0 ; igetAttribute("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::vectorelems = 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 = + "" + "%s" + "\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 = + "" + "" + "%s" + "%s" + "%s" + "" + "\n"; + if (!write(fmt, msgId++, username.c_str(), + digest.c_str(), resource.c_str())) + return false; + } + else + { + + //## Plaintext authentication + fmt = + "" + "" + "%s" + "%s" + "%s" + "" + "\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 = + "\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 = + "%s\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 = + "\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 = + "%s\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 = + "\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 = + "\n\n"; + if (!write(fmt, realm.c_str())) + return false; + + recbuf = readStanza(); + status("RECVx: '%s'", recbuf.c_str()); + recbuf.append(""); + 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 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 ; igetValue(); + 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 = + "\n\n"; + if (!write(fmt, realm.c_str())) + return false; + + DOMString recbuf = readStanza(); + //printf("received: '%s'\n", recbuf.c_str()); + recbuf.append(""); + 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 = + "\n\n"; + + if (!write(fmt, realm.c_str())) + return false; + recbuf = readStanza(); + recbuf.append("\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 = + "" + "" + "%s" + "\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 = + "" + "" + "\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 = + "\n"; + if (!write(fmt, msgId++)) + return false; + + fmt = + "" + "\n"; + if (!write(fmt, msgId++, realm.c_str())) + return false; + + fmt = + "" + "\n"; + if (!write(fmt, msgId++, realm.c_str())) + return false; + + fmt = + "\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 = + "\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 = + "" + "" + "%s" + "\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 = + "" + "" + "%s" + "\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 XmppClient::getRoster() +{ + std::vector 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::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 = + "" + "%s%s\n"; + if (!write(fmt, jid.c_str(), user.c_str(), + xmlSubj.c_str(), xmlMsg.c_str())) + return false; + } + else + { + char *fmt = + "" + "%s\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 = + "%s\n"; + if (!write(fmt, jid.c_str(), xmlPres.c_str())) + return false; + return true; +} + +//####################### +//# GROUP CHAT +//####################### + +bool XmppClient::groupChatCreate(const DOMString &groupJid) +{ + std::vector::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::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::iterator iter; + for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++) + if ((*iter)->getGroupJid() == groupJid) + return true; + return false; +} + +/** + * + */ +void XmppClient::groupChatsClear() +{ + std::vector::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::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::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::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 XmppClient::groupChatGetUserList( + const DOMString &groupJid) +{ + if (!checkConnect()) + { + std::vector dummy; + return dummy; + } + + std::vector::iterator iter; + for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++) + { + if ((*iter)->getGroupJid() == groupJid ) + { + std::vector uList = (*iter)->getUserList(); + std::sort(uList.begin(), uList.end(), xmppUserCompare); + return uList; + } + } + std::vector 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 = + "" + "\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 = + "" + "\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 = + "" + "%s\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 = + "" + "%s\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 = + "" + "\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; igetState() == 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 = + "" + "\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 = + "" + "" + "%s" + "" + "" + "" + "" + "" + "\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 = + "" + "\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 ; igetState() == 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 = + "\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 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 = + "\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; igetState() == 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=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 = + "" + "" + "%s" + "" + "" + "" + //"" + "" + "\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 = + "" + "" + "" + "" + "" + "" + "http://jabber.org/protocol/ibb" + "\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 ret = inputStreamRead(streamNr); + std::vector::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::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::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::iterator iter; + for (iter= userList.begin() ; iter!=userList.end() ; ) + { + if (iter->nick == nick) + iter = userList.erase(iter); + else + iter++; + } +} + +std::vector 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 index 000000000..d938a25fd --- /dev/null +++ b/src/pedro/pedroxmpp.h @@ -0,0 +1,1040 @@ +#ifndef __XMPP_H__ +#define __XMPP_H__ +/* + * API for the Pedro mini-XMPP client. + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include + +#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 getUserList() const; + + /** + * + */ + virtual void setUserList(const std::vector &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::vectoruserList; + +}; + + +//######################################################################## +//# 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 listeners; + + std::vector 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 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 + 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 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::vectorgroupChats; + + 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::vectorroster; +}; + + + + +//######################################################################## +//# 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 getUserList() const; + + +private: + + DOMString groupJid; + + std::vectoruserList; + +}; + + + + + + + + + + +} //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 index 000000000..9fc8b8647 --- /dev/null +++ b/src/pedro/work/filerec.cpp @@ -0,0 +1,129 @@ + + +#include + +#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 index 000000000..7a114abe2 --- /dev/null +++ b/src/pedro/work/filesend.cpp @@ -0,0 +1,95 @@ + + +#include + +#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 index 000000000..38a17abb6 --- /dev/null +++ b/src/pedro/work/groupchat.cpp @@ -0,0 +1,221 @@ + + +#include +#include + +#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 index 000000000..740a2de49 --- /dev/null +++ b/src/pedro/work/inklayout.svg @@ -0,0 +1,378 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + Private chat + + Gui Client Main Window + + Group chat + + Dialog + + + + Inkboard Layout + + Session + + Session Manager + + Session + + Session + + Private chat + w/ group member + Dialog + Dialog + + + + + + diff --git a/src/pedro/work/test.cpp b/src/pedro/work/test.cpp new file mode 100644 index 000000000..be0676ebc --- /dev/null +++ b/src/pedro/work/test.cpp @@ -0,0 +1,149 @@ + + +#include + +#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; +} +