[Pkg-voip-commits] [SCM] reSIProcate branch, master, updated. upstream/1.8.0_pre1-16-gcca9e38

Daniel Pocock daniel at pocock.com.au
Sun May 20 18:04:20 UTC 2012


The following commit has been merged in the master branch:
commit 5ac1acc9cbfe46cb93127db2c18e3a2bfc3597fd
Author: Daniel Pocock <daniel at pocock.com.au>
Date:   Sun May 20 20:03:01 2012 +0200

    Imported Upstream version 1.8.0~pre2

diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..8652221
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,89 @@
+
+Please add your name, email address and if you contribute on
+behalf of a company, please identity the name of the company,
+company registration number and the place of incorporation.
+
+This file identifies the authors/contributors/copyright holders who
+have contributed works of any kind to the main reSIProcate
+distribution tarball.
+
+This file does not cover the material in the contrib/ directory of
+the source control system, as each of those components is
+unmodified and distributed with it's own AUTHORS file.
+
+$ git log | grep ^Author | sort -u | cut -f2 -d' '
+
+adam
+adamr
+alan
+arosenberg
+bbramwel
+bcampen
+bko
+bob
+brocha
+bryan
+cbond
+cisco
+cktam
+dabryan
+daniel
+danweber
+Danweber
+davidb
+dbozzali
+dean
+derek
+Derek
+derekm
+dlb
+dpetrie
+dpocock: Daniel Pocock <daniel at pocock.com.au>
+dragos
+dsuh
+duanestorey
+dworley
+ekr
+emcmurry
+fjoanis
+fluffy
+greg
+jason
+jdeverick
+jgeras
+jmatthewsr
+jozsef
+kchmilar
+kdc
+kenho
+ken
+kittlitz
+kwhite
+lowekamp
+matthias
+maxb
+mfroman
+moetje
+nagendra
+nash
+nils
+(no
+partim
+pckizer
+rjsparks
+rohan
+ronh
+ryker
+sailesh
+satluri
+schanin
+sgodin
+shaun
+troll
+vann
+veer
+vinit
+wensong
+xmlscott
+
+
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d2036c3
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,54 @@
+License note:
+- each source file has a license enclosed, usually in comments at
+  the end of the file
+- individual authors have not always used an identical license
+  block in their code, yet all the license blocks are for most
+  purposes equivalent and compatible with the standard 3 clause
+  BSD license
+- the bulk of the code is licensed under the terms of the Vovida
+  license, which is like the BSD license with a 4th clause
+  added restricting the use of the term VOCAL in the name
+  of any derivative work.  A full copy of the Vovida license
+  is presented below.
+
+
+
+   The Vovida Software License, Version 1.0
+  
+   Copyright (c) 2000-2008 Vovida Networks, Inc.  All rights reserved.
+  
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+  
+   1. Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+  
+   2. Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in
+      the documentation and/or other materials provided with the
+      distribution.
+  
+   3. The names "VOCAL", "Vovida Open Communication Application Library",
+      and "Vovida Open Communication Application Library (VOCAL)" must
+      not be used to endorse or promote products derived from this
+      software without prior written permission. For written
+      permission, please contact vocal at vovida.org.
+  
+   4. Products derived from this software may not be called "VOCAL", nor
+      may "VOCAL" appear in their name, without prior written
+      permission of Vovida Networks, Inc.
+  
+   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
+   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
+   NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
+   NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
+   IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+   OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+   USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+   DAMAGE.
diff --git a/Makefile.in b/Makefile.in
index 602caae..dc64afa 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -44,9 +44,10 @@ host_triplet = @host@
 subdir = .
 DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \
 	$(srcdir)/Makefile.in $(srcdir)/config.h.in \
-	$(srcdir)/resip.spec.in $(top_srcdir)/configure INSTALL \
-	build-aux/config.guess build-aux/config.sub build-aux/depcomp \
-	build-aux/install-sh build-aux/ltmain.sh build-aux/missing
+	$(srcdir)/resip.spec.in $(top_srcdir)/configure AUTHORS \
+	COPYING INSTALL build-aux/config.guess build-aux/config.sub \
+	build-aux/depcomp build-aux/install-sh build-aux/ltmain.sh \
+	build-aux/missing
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_have_epoll.m4 \
 	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
diff --git a/RELEASE-PROCESS.txt b/RELEASE-PROCESS.txt
index d83591f..508632e 100644
--- a/RELEASE-PROCESS.txt
+++ b/RELEASE-PROCESS.txt
@@ -9,12 +9,13 @@ Deliverables
 ------------
 
 The only official deliverable is the tarball, for example,
-resiprocate-1.8.0.tar.gz
+
+    resiprocate-1.8.0.tar.gz
 
 As a courtesy to users, a contrib tarball is also produced,
-containing some third party code, some of it patched for
-use with a specific version of reSIProcate:
-resiprocate-contrib-1.8.0.tar.gz
+containing some third party code:
+
+    resiprocate-contrib-1.8.0.tar.gz
 
 Building of binary packages (e.g. for Debian, RPM, OpenCSW) is
 done after the tarball release.  That is not covered here.
@@ -35,10 +36,15 @@ git-svn is used because it makes it easier to build a clean tarball
 Version/tag
 -----------
 
+Here we give an example for building the release v1.8.5:
+
 Update version information and tag it:
 
   vi configure.ac         (update the version and ABIVERSION numbers)
-  git add configure.ac && git commit -m 'Update to version 1.8.5'
+
+  git add configure.ac
+  git commit -m 'Update to version 1.8.5'
+
   git svn dcommit         (send changes back to SVN)
 
   svn copy https://svn.resiprocate.org/rep/resiprocate/main/branches/release-1.8 https://svn.resiprocate.org/rep/resiprocate/main/tags/1.8.5
@@ -56,7 +62,18 @@ it should be discussed on the mailing list and noted in this document.
 Make a tarball
 --------------
 
-  ./configure --with-ssl && make dist
+  build/release-tarball.sh
+
+  * this script will call configure && make dist
+  * any previous configure settings will be overridden, so you
+    may want to preserve a copy of config.status and config.log
+    or just do this operation from a workspace that is independent
+    of your normal development workspace
+
+Make the contrib tarball
+------------------------
+
+  build/contrib-tarball.sh
 
 Sanity check on tarball
 -----------------------
diff --git a/apps/Makefile.am b/apps/Makefile.am
index 899edee..060b4ef 100644
--- a/apps/Makefile.am
+++ b/apps/Makefile.am
@@ -1,5 +1,7 @@
 
 SUBDIRS = clicktocall
 SUBDIRS += sipdial
+if BUILD_ICHAT_GW
 SUBDIRS += ichat-gw
+endif
 
diff --git a/apps/Makefile.in b/apps/Makefile.in
index bd80edf..5ee80bf 100644
--- a/apps/Makefile.in
+++ b/apps/Makefile.in
@@ -33,6 +33,7 @@ PRE_UNINSTALL = :
 POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
+ at BUILD_ICHAT_GW_TRUE@am__append_1 = ichat-gw
 subdir = apps
 DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
@@ -63,7 +64,7 @@ AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
 	distdir
 ETAGS = etags
 CTAGS = ctags
-DIST_SUBDIRS = $(SUBDIRS)
+DIST_SUBDIRS = clicktocall sipdial ichat-gw
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 am__relativize = \
   dir0=`pwd`; \
@@ -215,7 +216,7 @@ target_alias = @target_alias@
 top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
-SUBDIRS = clicktocall sipdial ichat-gw
+SUBDIRS = clicktocall sipdial $(am__append_1)
 all: all-recursive
 
 .SUFFIXES:
diff --git a/apps/ichat-gw/IChatIPPortData.cxx b/apps/ichat-gw/IChatIPPortData.cxx
index 43da35c..b3ddd19 100644
--- a/apps/ichat-gw/IChatIPPortData.cxx
+++ b/apps/ichat-gw/IChatIPPortData.cxx
@@ -1,3 +1,6 @@
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
 
 #include <rutil/Logger.hxx>
 #include <resip/stack/Helper.hxx>
@@ -63,9 +66,14 @@ IChatIPPortData::IChatIPPortData(const std::string& hexblob) : mHexBlob(hexblob)
       }
       else
       {
+#ifdef USE_IPV6
          in6_addr addr;
          memcpy(&addr, ipPortDataBlob->ipAddress, 16);
          addIPPortData(Data(ipPortDataBlob->interfaceName), Tuple(addr,ntohs(ipPortDataBlob->port),UDP));
+#else
+         ErrLog(<< "IPv6 support not enabled at compile time.");
+         assert(0);
+#endif
       }
       InfoLog(<< "IChatIPPortData: name=" << mIPPortDataList.back().first << " addr=" << mIPPortDataList.back().second);
    }
diff --git a/apps/ichat-gw/Makefile.am b/apps/ichat-gw/Makefile.am
index f29be2e..db26edc 100644
--- a/apps/ichat-gw/Makefile.am
+++ b/apps/ichat-gw/Makefile.am
@@ -9,7 +9,7 @@ SUBDIRS = .
 # this has more external dependencies, it has been adapted
 # for autotools and can be enabled manually if the deps
 # are available
-#SUBDIRS += jabberconnector
+SUBDIRS += jabberconnector
 
 #AM_CXXFLAGS = -DUSE_ARES
 
diff --git a/apps/ichat-gw/Makefile.in b/apps/ichat-gw/Makefile.in
index 722cbac..5dfc818 100644
--- a/apps/ichat-gw/Makefile.in
+++ b/apps/ichat-gw/Makefile.in
@@ -269,7 +269,10 @@ top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 EXTRA_DIST = ichat-gw.config doc *.sln *.vcproj
-SUBDIRS = .
+# this has more external dependencies, it has been adapted
+# for autotools and can be enabled manually if the deps
+# are available
+SUBDIRS = . jabberconnector
 ichat_gw_LDADD = ../../resip/dum/libdum.la \
 	../../resip/stack/libresip.la ../../rutil/librutil.la \
 	@LIBSSL_LIBADD@ -lpcre -lpthread
diff --git a/apps/ichat-gw/jabberconnector/IChatUser.cxx b/apps/ichat-gw/jabberconnector/IChatUser.cxx
new file mode 100644
index 0000000..1fa3ccb
--- /dev/null
+++ b/apps/ichat-gw/jabberconnector/IChatUser.cxx
@@ -0,0 +1,210 @@
+#include "IChatUser.hxx"
+#include "JabberComponent.hxx"
+
+using namespace gateway;
+using namespace std;
+
+namespace gateway 
+{
+
+IChatUser::IChatUser(JabberComponent& component, const std::string& jid) : 
+   mJID(jid),
+   mComponent(component)
+{
+}
+
+IChatUser::~IChatUser() 
+{
+   ResourceMap::iterator it = mResources.begin();
+   for(;it!=mResources.end();it++)
+   {
+      delete it->second;
+   }
+}
+
+void 
+IChatUser::updateResourceInfo(const std::string& resourceId, const gloox::Presence& presence, int priority, bool avAvail)
+{
+   bool originalUnavailability = isUnavailable();
+   if(resourceId.empty())
+   {
+      if(presence == gloox::PresenceUnavailable)
+      {
+	     // All resources are unanavaiable
+         ResourceMap::iterator it = mResources.begin();
+         for(; it != mResources.end(); it++)
+         {
+            delete it->second;
+		 }
+		 mResources.clear();
+	  }
+	  else
+	  {
+		  // PresenceAvailable with no resource is meaningless
+	  }
+   }
+   else
+   {
+      ResourceMap::iterator it = mResources.find(resourceId);
+      if(it != mResources.end())
+      {
+         if(presence == gloox::PresenceUnavailable)
+         {
+            delete it->second;
+            mResources.erase(it);
+         }
+         else
+         {
+            it->second->mPresence = presence;
+            it->second->mPriority = priority;
+            it->second->mAvAvail = avAvail;
+         }
+      }
+      else
+      {
+         if(presence != gloox::PresenceUnavailable)
+         {
+            mResources[resourceId] = new ResourceInfo(presence, priority, avAvail);
+		 }
+      }
+   }
+   if(originalUnavailability && !isUnavailable())
+   {
+      // User was originally unavailable and now they are available - register SIP endpoint
+      mComponent.sipRegisterJabberUser(mJID);
+   }
+   else if(!originalUnavailability && isUnavailable())
+   {
+      // User was originally available and now they are unavailable - unregister SIP endpoint
+      mComponent.sipUnregisterJabberUser(mJID);      
+   }
+}
+
+bool 
+IChatUser::isUnavailable() 
+{ 
+   return mResources.empty(); 
+}
+
+IChatUser::ResourceMap::iterator 
+IChatUser::getMostAvailableResourceItr()
+{
+   ResourceMap::iterator itBestResource = mResources.end();
+   ResourceMap::iterator it = mResources.begin();
+   for(;it!=mResources.end();it++)
+   {
+      if(it->second->mAvAvail)
+      {
+         if(itBestResource == mResources.end())
+         {
+            itBestResource=it;
+         }
+         else
+         {
+            if(it->second->mPresence < itBestResource->second->mPresence)
+            {
+               itBestResource=it;
+            }
+            else if(it->second->mPresence == itBestResource->second->mPresence)
+            {
+               if(it->second->mPriority < itBestResource->second->mPriority)
+               {
+                  itBestResource=it;
+               }
+            }
+         }
+      }
+   }
+   return itBestResource;
+}
+
+const std::string& 
+IChatUser::getMostAvailableResource()
+{
+   static const std::string empty;
+   ResourceMap::iterator itBestResource = getMostAvailableResourceItr();
+   if(itBestResource != mResources.end())
+   {
+      return itBestResource->first;
+   }
+   return empty;
+}
+
+bool
+IChatUser::getMostAvailableResourceList(std::list<std::string>& resourceList)
+{
+   bool ret = false;
+
+   // Pass one - go through list and see what highest mPresence availability is
+   ResourceMap::iterator itBestResource = getMostAvailableResourceItr();
+
+   // Pass two - return all resources with matching mPresence and mPriority
+   if(itBestResource != mResources.end())
+   {
+      ResourceMap::iterator it = mResources.begin();
+      for(;it!=mResources.end();it++)
+      {
+         if(it->second->mAvAvail && 
+            it->second->mPresence == itBestResource->second->mPresence &&
+            it->second->mPriority == itBestResource->second->mPriority)
+         {
+            resourceList.push_back(it->first);
+            ret = true;
+         }
+      }
+   }
+   return ret;
+}
+
+void 
+IChatUser::addSubscribedGatewayUser(const std::string& jid)
+{
+   mSubscribedGatewayUsers.insert(jid);
+}
+
+void 
+IChatUser::removeSubscribedGatewayUser(const std::string& jid)
+{
+   SubscribedGatewayUserList::iterator it = mSubscribedGatewayUsers.find(jid);
+   if(it!=mSubscribedGatewayUsers.end())
+   {
+      mSubscribedGatewayUsers.erase(it);
+   }
+}
+
+}
+
+/* ====================================================================
+
+ Copyright (c) 2009, SIP Spectrum, Inc.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are 
+ met:
+
+ 1. Redistributions of source code must retain the above copyright 
+    notice, this list of conditions and the following disclaimer. 
+
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution. 
+
+ 3. Neither the name of SIP Spectrum nor the names of its contributors 
+    may be used to endorse or promote products derived from this 
+    software without specific prior written permission. 
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ ==================================================================== */
+
diff --git a/apps/ichat-gw/AddressTranslator.hxx b/apps/ichat-gw/jabberconnector/IChatUser.hxx
similarity index 54%
copy from apps/ichat-gw/AddressTranslator.hxx
copy to apps/ichat-gw/jabberconnector/IChatUser.hxx
index 6d6a85d..f792579 100644
--- a/apps/ichat-gw/AddressTranslator.hxx
+++ b/apps/ichat-gw/jabberconnector/IChatUser.hxx
@@ -1,47 +1,72 @@
-#if !defined(AddressTranslator_hxx)
-#define AddressTranslator_hxx
+#if !defined(IChatUser_hxx)
+#define IChatUser_hxx
 
+#include <map>
+#include <set>
 #include <list>
 
 #ifdef WIN32
-#include <pcreposix.h>
+#define RESIP_CONTRIB_GLOOX
+#endif
+
+// Gloox includes
+#ifndef RESIP_CONTRIB_GLOOX
+#include <gloox/component.h>
+#include <gloox/presence.h>
 #else
-#include <regex.h>
+#include <src/component.h>
+#include <src/presence.h>
 #endif
-#include "rutil/Data.hxx"
 
 namespace gateway
 {
 
-class AddressTranslator
+class JabberComponent;
+
+class ResourceInfo
 {
-   public:
-      AddressTranslator();
-      ~AddressTranslator();
-      
-      void addTranslation(const resip::Data& matchingPattern,
-                          const resip::Data& rewriteExpression);
-      
-      void removeTranslation(const resip::Data& matchingPattern);
-                  
-      bool translate(const resip::Data& address, resip::Data& translation, bool failIfNoRule=false);
-
-   private:
-      class FilterOp
-      {
-         public:
-            resip::Data mMatchingPattern;
-            resip::Data mRewriteExpression;
-            regex_t *preq;
-      };
-      
-      typedef std::list<FilterOp> FilterOpList;
-      FilterOpList mFilterOperators; 
+public:
+   ResourceInfo(const gloox::Presence& presence, int priority, bool avAvail) :
+      mPresence(presence), mPriority(priority), mAvAvail(avAvail) {}
+   ~ResourceInfo() {}
+
+   gloox::Presence mPresence;
+   int mPriority;
+   bool mAvAvail;
+};
+
+class IChatUser
+{
+public:
+   IChatUser(JabberComponent& component, const std::string& jid);
+   ~IChatUser();
+
+   typedef std::set<std::string> SubscribedGatewayUserList;
+
+   void updateResourceInfo(const std::string& resourceId, const gloox::Presence& presence, int priority, bool avAvail);
+   bool isUnavailable();
+   const std::string& getMostAvailableResource();
+   bool getMostAvailableResourceList(std::list<std::string>& resourceList);
+
+   void addSubscribedGatewayUser(const std::string& jid);
+   void removeSubscribedGatewayUser(const std::string& jid);
+   const SubscribedGatewayUserList& getSubscribedGatewayUserList() { return mSubscribedGatewayUsers; }
+
+private:
+   std::string mJID;
+
+   typedef std::map<std::string, ResourceInfo*> ResourceMap;
+   ResourceMap mResources;
+
+   ResourceMap::iterator getMostAvailableResourceItr();
+
+   SubscribedGatewayUserList mSubscribedGatewayUsers;
+   JabberComponent& mComponent;
 };
 
 }
-#endif  
 
+#endif
 
 /* ====================================================================
 
diff --git a/apps/ichat-gw/jabberconnector/JabberComponent.cxx b/apps/ichat-gw/jabberconnector/JabberComponent.cxx
new file mode 100644
index 0000000..6f477b3
--- /dev/null
+++ b/apps/ichat-gw/jabberconnector/JabberComponent.cxx
@@ -0,0 +1,1181 @@
+
+#if defined( WIN32 )
+#include <time.h>
+#endif
+
+#include <sstream>
+#include <assert.h>
+#include <algorithm>
+#include "../Version.hxx"
+#include "JabberComponent.hxx"
+#include "IChatUser.hxx"
+
+#ifndef RESIP_CONTRIB_GLOOX
+#include <gloox/disco.h>
+#include <gloox/mutex.h>
+#else
+#include <src/disco.h>
+#include <src/mutex.h>
+#endif
+
+using namespace gateway;
+using namespace gloox;
+using namespace std;
+
+extern void sleepSeconds(unsigned int seconds);
+
+void
+IChatCallRequest::sendIChatVCRequest(const std::string& fullTo)
+{
+   // Only allow one VCRequest per resource - this guards against receiving multiple
+   // presence messages from the same resource
+   if(mPendingVCRequestSet.find(fullTo) == mPendingVCRequestSet.end())
+   {
+      mJabberComponent->notifyIChatCallProceeding(mB2BSessionHandle, fullTo); 
+
+      assert(mJabberComponent);
+      mJabberComponent->sendPresence(fullTo, mJabberComponent->mControlJID, false /* advertiseIChatSupport */, true /* available */);  // Doing this let's us push the control presence as well send calls
+
+      std::string id = mJabberComponent->mComponent->getID();
+   
+      Tag *iq = new Tag("iq");
+      iq->addAttribute("type", "set");
+      iq->addAttribute("id", id);
+      iq->addAttribute("to", fullTo.c_str());
+      iq->addAttribute("from", mFrom.c_str());
+      Tag *query = new Tag(iq, "query");
+      query->addAttribute("xmlns", "apple:iq:vc:request");
+      new Tag(query, "VCNewCallerIPPortData", mJabberComponent->mLocalIChatPortListBlob);
+      new Tag(query, "isAudioOnly", "1");
+      new Tag(query, "extSIPPort", "0");
+      new Tag(query, "extIPAddr", "127.0.0.1");
+      new Tag(query, "VCProtocolVersion", "1");
+      mJabberComponent->mComponent->send(iq);
+
+      mPendingVCRequestSet.insert(fullTo);
+   }
+}
+
+void 
+IChatCallRequest::receivedIChatVCResponse(const std::string& from)
+{
+   std::set<std::string>::iterator it = mPendingVCRequestSet.find(from);
+   mPendingVCRequestSet.erase(it);  // Remove responded JID from list and cancel all others
+   sendIChatVCCancelToAll();
+}
+
+void
+IChatCallRequest::sendIChatVCCancelToAll()
+{
+   std::set<std::string>::iterator it = mPendingVCRequestSet.begin();
+   std::string id = mJabberComponent->mComponent->getID();
+
+   // Send a cancel out for each VCRequest
+   for(;it!=mPendingVCRequestSet.end();it++)
+   {
+      Tag *iq = new Tag("iq");
+      iq->addAttribute("type", "set");
+      iq->addAttribute("id", id);
+      iq->addAttribute("to", it->c_str());
+      iq->addAttribute("from", mFrom.c_str());
+      Tag *query = new Tag(iq, "query");
+      query->addAttribute("xmlns", "apple:iq:vc:cancel");
+      new Tag(query, "VCProtocolVersion", "1");
+      mJabberComponent->mComponent->send(iq);
+   }
+   mPendingVCRequestSet.clear();
+}
+
+void 
+IChatCallRequest::sendIChatVCResponse(bool accept)
+{
+   Tag *iq = new Tag( "iq" );
+   iq->addAttribute( "type", "set" );
+   iq->addAttribute( "id", mJabberComponent->mComponent->getID() );
+   iq->addAttribute( "to", mFrom );
+   iq->addAttribute( "from", mTo );
+   Tag *query = new Tag( iq, "query" );
+   query->addAttribute( "xmlns", "apple:iq:vc:response");
+   if(accept)
+   {
+     new Tag( query, "response", "0" );
+     new Tag( query, "connectData", mJabberComponent->mLocalIChatPortListBlob);
+   }
+   else
+   {
+     new Tag( query, "response", "1" );
+     new Tag( query, "connectData", "");
+   }
+   //new Tag( query, "responseData", "1" );  // doesn't appear to be required
+   new Tag( query, "VCProtocolVersion", "1" );
+   mJabberComponent->mComponent->send(iq);
+}
+
+class IPCMutexGloox : public IPCMutex
+{
+public:
+   IPCMutexGloox() {}
+   virtual ~IPCMutexGloox() {}
+   virtual void lock() { mMutex.lock(); }
+   virtual void unlock() { mMutex.unlock(); }
+private:
+   gloox::Mutex mMutex;
+};
+IPCMutexGloox g_IPCGlooxMutex;
+
+JabberComponent::JabberComponent(unsigned short jabberConnectorIPCPort,
+                                 unsigned short gatewayIPCPort,
+                                 const std::string& server, 
+                                 const std::string& component, 
+                                 const std::string& password, 
+                                 int port, 
+                                 unsigned int serverPingDuration,
+                                 const std::string& controlUser,
+                                 const std::string& localIChatPortListBlob) 
+   : mStopping(false),
+     mServerPingDuration(serverPingDuration),
+     mLocalIChatPortListBlob(localIChatPortListBlob),
+     mIPCThread(jabberConnectorIPCPort, gatewayIPCPort, this, &g_IPCGlooxMutex)
+{
+   mIPCThread.run();
+
+   mComponent = new Component("jabber:component:accept", server, component, password, port);
+
+   mComponent->registerMessageHandler(this);
+   mComponent->registerConnectionListener(this);
+   mComponent->registerPresenceHandler(this);
+   mComponent->registerSubscriptionHandler(this);
+   mComponent->registerIqHandler(this, "apple:iq:vc:request");
+   mComponent->registerIqHandler(this, "apple:iq:vc:cancel");
+   mComponent->registerIqHandler(this, "apple:iq:vc:response");
+   mComponent->registerIqHandler(this, "apple:iq:vc:counterProposal");
+   mComponent->logInstance().registerLogHandler(LogLevelDebug, LogAreaAll, this);
+
+   mComponent->disco()->registerNodeHandler(this, "apple:ichat:caps#avavail");
+   mComponent->disco()->registerNodeHandler(this, "apple:ichat:caps#audio");
+   mComponent->disco()->registerNodeHandler(this, "apple:ichat:caps#avcap");
+   mComponent->disco()->registerNodeHandler(this, "apple:ichat:caps#448");
+   mComponent->disco()->setIdentity("server", "ichat-gw");
+   mComponent->disco()->setVersion("ichat-gw", ICHATGW_VERSION_STRING);
+
+   //mComponent->setTls(tlsPolicy);  // This setting appears to have no effect on Component connections
+
+   mControlJID = controlUser + "@" + component;
+}
+
+JabberComponent::~JabberComponent()
+{
+   mIPCThread.shutdown();
+   mIPCThread.join();
+}
+
+void 
+JabberComponent::thread()
+{
+   handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::thread - starting...");
+   while(!mStopping)
+   {
+      mComponent->connect(false);
+      ConnectionError rc;
+      time_t lastRecvTime=time(0);
+      while((rc=mComponent->recv(mServerPingDuration * 1000000)) == ConnNoError)
+      {
+         time_t now = time(0);
+         if(now-lastRecvTime >= mServerPingDuration)
+         {
+            mComponent->whitespacePing();
+         }
+         lastRecvTime = now;
+      }
+
+      if(!mStopping)
+      {
+         std::ostringstream oss;
+         oss << "JabberComponent::thread - recv error, rc=" << rc;
+         handleLog(gloox::LogLevelError, gloox::LogAreaUser, oss.str());
+         // Wait 10 seconds then try again
+         sleepSeconds(10);
+         if(mComponent->state() != gloox::StateDisconnected)
+         {
+            mComponent->disconnect();
+         }
+      }
+   }
+   handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::thread - shutdown.");
+}
+
+void 
+JabberComponent::stop()
+{
+   mStopping = true;
+   disconnect();
+}
+
+void 
+JabberComponent::disconnect()
+{
+   if(mComponent->state() == gloox::StateDisconnected) return;
+
+   mIChatUserMutex.lock();
+
+   // Tell users that control user is now offline
+   IChatUserMap::iterator it = mIChatUsers.begin();
+   for(;it!=mIChatUsers.end();it++)
+   {
+      // Notify user that subscribed users are now offline
+      const IChatUser::SubscribedGatewayUserList& subscribedUserList = it->second->getSubscribedGatewayUserList();
+      IChatUser::SubscribedGatewayUserList::const_iterator it2 = subscribedUserList.begin();
+      for(;it2!=subscribedUserList.end();it2++)
+      {
+         sendPresence(it->first, *it2, false, false /* available? */);
+      }
+      delete it->second;
+   }
+   mIChatUsers.clear();
+
+   mIChatUserMutex.unlock();
+
+   mComponent->disconnect();
+}
+
+void
+JabberComponent::initiateIChatCall(const std::string& to, const std::string& from, unsigned int handle, bool alertOneOnly)
+{
+   JID jid(to);
+   mOutstandingClientIChatCallRequestsMutex.lock();
+
+   // Add call request data to map
+   std::string key = makeVCRequestKey(jid.bare(),from);
+   IChatCallRequest* iChatCallRequest = &(mOutstandingClientIChatCallRequests[key] = IChatCallRequest(this, to, from, handle));
+   handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::initiateiChatCall - key=" + key);
+
+   // If there is no resource, then we need to query the bare JID and find iChat resources
+   if(jid.resource().empty())
+   {
+      bool found = false;
+      if(alertOneOnly)
+      {
+         // First check if we have any info cached locally
+         std::string fullJID;
+         if(getMostAvailableIChatUserFullJID(jid, fullJID))  
+         {
+            found = true;
+            iChatCallRequest->sendIChatVCRequest(fullJID);
+         }
+      }
+      else
+      {
+         std::list<std::string> fullJIDList;
+         if(getMostAvailableIChatUserFullJIDList(jid, fullJIDList))
+         {
+            found = true;
+            std::list<std::string>::iterator it = fullJIDList.begin();
+            for(;it!=fullJIDList.end();it++)
+            {
+               iChatCallRequest->sendIChatVCRequest(*it);
+            }
+         }
+      }
+
+      if(!found)
+      {
+         // No local info - try to probe
+         Tag *iq = new Tag("presence");
+         iq->addAttribute("type", "probe");
+         iq->addAttribute("to", to.c_str());
+         iq->addAttribute("from", mControlJID);
+         mComponent->send(iq);
+      }
+   }
+   else // We have full JID - send the vc-request directly to the resource
+   {
+      iChatCallRequest->sendIChatVCRequest(to);
+   }
+   mOutstandingClientIChatCallRequestsMutex.unlock();
+}
+
+void 
+JabberComponent::cancelIChatCall(const std::string& to, const std::string& from)
+{
+   JID jid(to);
+   mOutstandingClientIChatCallRequestsMutex.lock();
+
+   IChatCallRequestMap::iterator it = findOutstandingClientIChatCallRequest(jid.bare(), from);
+   if(it != mOutstandingClientIChatCallRequests.end())
+   {
+      it->second.sendIChatVCCancelToAll();
+      handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::cancelIChatCall - success");
+      mOutstandingClientIChatCallRequests.erase(it);
+   }
+   else
+   {
+      handleLog(gloox::LogLevelWarning, gloox::LogAreaUser, "JabberComponent::cancelIChatCall - not found");
+   }
+   mOutstandingClientIChatCallRequestsMutex.unlock();
+}
+
+void 
+JabberComponent::proceedingIChatCall(const std::string& to, const std::string& from, unsigned int handle)
+{
+   mOutstandingServerIChatCallRequestsMutex.lock();
+   IChatCallRequestMap::iterator it = findOutstandingServerIChatCallRequest(to, from);
+   if(it!=mOutstandingServerIChatCallRequests.end())
+   {
+      // Store session handle for notifications to SIP layer
+      assert(it->second.mB2BSessionHandle == 0);
+      std::ostringstream oss;
+      oss << "JabberComponent::proceedingIChatCall - set handle to " << handle;
+      handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, oss.str());
+      it->second.mB2BSessionHandle = handle;
+
+      // If already cancelled, then notify SIP layer now
+      if(it->second.mCancelled)
+      {
+         notifyIChatCallCancelled(it->second.mB2BSessionHandle); 
+         mOutstandingServerIChatCallRequests.erase(it);
+      }
+   }    
+   mOutstandingServerIChatCallRequestsMutex.unlock();
+}
+
+void 
+JabberComponent::acceptIChatCall(const std::string& to, const std::string& from)
+{
+   mOutstandingServerIChatCallRequestsMutex.lock();
+   IChatCallRequestMap::iterator it = findOutstandingServerIChatCallRequest(to, from);
+   if(it!=mOutstandingServerIChatCallRequests.end())
+   {
+      it->second.sendIChatVCResponse(true);
+      mOutstandingServerIChatCallRequests.erase(it);
+   }    
+   mOutstandingServerIChatCallRequestsMutex.unlock();
+}
+
+void 
+JabberComponent::rejectIChatCall(const std::string& to, const std::string& from)
+{
+   mOutstandingServerIChatCallRequestsMutex.lock();
+   IChatCallRequestMap::iterator it = findOutstandingServerIChatCallRequest(to, from);
+   if(it!=mOutstandingServerIChatCallRequests.end())
+   {
+      it->second.sendIChatVCResponse(false);
+      mOutstandingServerIChatCallRequests.erase(it);
+   }    
+   mOutstandingServerIChatCallRequestsMutex.unlock();
+}
+
+std::string 
+JabberComponent::makeVCRequestKey(const std::string& bareTo, const std::string& bareFrom)
+{
+   std::string key = bareTo + "|" + bareFrom;
+#ifdef WIN32
+   std::transform(key.begin(), key.end(), key.begin(), tolower);
+#else
+   std::transform(key.begin(), key.end(), key.begin(),  (int(*)(int))std::tolower);
+#endif
+   return key;
+}
+
+JabberComponent::IChatCallRequestMap::iterator
+JabberComponent::findOutstandingClientIChatCallRequest(const std::string& bareTo, const std::string& bareFrom)
+{
+   std::string key = makeVCRequestKey(bareTo, bareFrom);
+   handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::findOutstandingClientIChatCallRequest - key=" + key);
+   return mOutstandingClientIChatCallRequests.find(key);
+}
+
+void
+JabberComponent::failOutstandingClientIChatCallRequest(const std::string& bareTo, const std::string& bareFrom, unsigned int code)
+{
+   mOutstandingClientIChatCallRequestsMutex.lock();
+   IChatCallRequestMap::iterator it = findOutstandingClientIChatCallRequest(bareTo, bareFrom);
+   if(it!=mOutstandingClientIChatCallRequests.end())
+   {
+      notifyIChatCallFailed(it->second.mB2BSessionHandle, code); 
+      mOutstandingClientIChatCallRequests.erase(it);
+   }    
+   mOutstandingClientIChatCallRequestsMutex.unlock();
+}
+
+void
+JabberComponent::failOutstandingClientIChatCallRequest(const std::string& bareTo, unsigned int code)
+{
+   mOutstandingClientIChatCallRequestsMutex.lock();
+   IChatCallRequestMap::iterator it = mOutstandingClientIChatCallRequests.begin();
+   while(it != mOutstandingClientIChatCallRequests.end())
+   {
+      if(it->second.mTo == bareTo)
+      {
+         notifyIChatCallFailed(it->second.mB2BSessionHandle, code);   
+         mOutstandingClientIChatCallRequests.erase(it++);
+      }
+      else
+      {
+         it++;
+      }
+   }
+   mOutstandingClientIChatCallRequestsMutex.unlock();
+}
+
+JabberComponent::IChatCallRequestMap::iterator
+JabberComponent::findOutstandingServerIChatCallRequest(const std::string& bareTo, const std::string& bareFrom)
+{
+   std::string key = makeVCRequestKey(bareTo, bareFrom);
+   handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::findOutstandingServerIChatCallRequest - key=" + key);
+   return mOutstandingServerIChatCallRequests.find(key);
+}
+
+void 
+JabberComponent::cancelOutstandingServerIChatCallRequest(const std::string& bareTo, const std::string& bareFrom)
+{
+   mOutstandingServerIChatCallRequestsMutex.lock();
+   IChatCallRequestMap::iterator it = findOutstandingServerIChatCallRequest(bareTo, bareFrom);
+   if(it!=mOutstandingServerIChatCallRequests.end())
+   {
+      if(it->second.mB2BSessionHandle != 0)
+      {
+         handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::cancelOutstandingServerIChatCallRequest - to=" + bareTo + " from=" + bareFrom);
+         notifyIChatCallCancelled(it->second.mB2BSessionHandle); 
+         mOutstandingServerIChatCallRequests.erase(it);
+      }
+      else
+      {
+         // We don't have a session handle yet, so flag request as cancelled, 
+         // when session handle arrives, call notifyIChatCallCancelled and remove entry
+         it->second.mCancelled = true;  
+         handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::cancelOutstandingServerIChatCallRequest - no session handle yet, pending - to=" + bareTo + " from=" + bareFrom);
+      }
+   }    
+   mOutstandingServerIChatCallRequestsMutex.unlock();
+}
+
+void 
+JabberComponent::probePresence(const std::string& to)
+{
+   Tag *presence = new Tag("presence");
+   presence->addAttribute("type", "probe");
+   presence->addAttribute("to", to);
+   presence->addAttribute("from", mControlJID);
+   mComponent->send(presence);
+}
+
+void 
+JabberComponent::sendPresenceForRequest(Stanza* stanza)
+{
+   sendPresence(stanza->from().bare(), stanza->to().full(), stanza->to().bare() != mControlJID, true /* available */);
+}
+
+void 
+JabberComponent::sendPresence(const std::string& to, const std::string& from, bool advertiseIChatSupport, bool available)
+{
+   Tag *presence = new Tag("presence");
+   if(!available)
+   {
+      presence->addAttribute("type", "unavailable");
+   }
+   presence->addAttribute("to", to);
+   presence->addAttribute("from", from);
+   new Tag(presence, "priority", "0");
+   if(advertiseIChatSupport)
+   {
+      Tag* c = new Tag(presence, "c");
+      c->addAttribute("xmlns", "http://jabber.org/protocol/caps");
+      c->addAttribute("node", "apple:ichat:caps"); 
+      c->addAttribute("ver", "448");   // Note:  Use version 448 so we don't collide with actual iChat version
+      c->addAttribute("ext", "avavail avcap audio");
+   }
+   mComponent->send(presence);
+}
+
+void
+JabberComponent::sendSubscriptionResponse(const std::string& to, const std::string& from, bool success)
+{
+   if(success)
+   {
+      Tag *p = new Tag( "presence" );
+      p->addAttribute( "type", "subscribed" );
+      p->addAttribute( "to", to );
+      p->addAttribute( "from", from );
+      mComponent->send( p );
+   
+      mUserDb.addSubscribedJID(to.c_str(), from.c_str());
+      storeIChatSubscribedUser(to, from);
+      sendPresence(to, from, from != mControlJID, true);
+
+      // Subscribe back to client to ensure we are subscribed to client
+      Tag *iq = new Tag("presence");
+      iq->addAttribute("type", "subscribe");
+      iq->addAttribute("to", to);
+      iq->addAttribute("from", mControlJID);
+      mComponent->send(iq);
+   }
+   else
+   {
+      Tag *p = new Tag( "presence" );
+      p->addAttribute( "type", "unsubscribed" );
+      p->addAttribute( "to", to );
+      p->addAttribute( "from", from );
+      mComponent->send( p );
+   }
+}
+
+void 
+JabberComponent::removeIChatSubscribedUser(const std::string& user, const std::string& subscribedJID)
+{
+   mIChatUserMutex.lock();
+
+   IChatUserMap::iterator it = mIChatUsers.find(user);
+   if(it != mIChatUsers.end())
+   {
+      it->second->removeSubscribedGatewayUser(subscribedJID);
+   }
+
+   mIChatUserMutex.unlock();
+}
+
+void 
+JabberComponent::storeIChatSubscribedUser(const std::string& user, const std::string& subscribedJID)
+{
+   mIChatUserMutex.lock();
+
+   IChatUserMap::iterator it = mIChatUsers.find(user);
+   if(it == mIChatUsers.end())
+   {
+      // User is not yet present in map
+      IChatUser* iChatUser = new IChatUser(*this, user);
+      iChatUser->addSubscribedGatewayUser(subscribedJID);
+      mIChatUsers[user] = iChatUser;
+   }
+   else
+   {
+      it->second->addSubscribedGatewayUser(subscribedJID);
+   }
+
+   mIChatUserMutex.unlock();
+}
+
+void 
+JabberComponent::storeIChatPresence(const gloox::JID& jid, const gloox::Presence& presence, int priority, bool avAvail)
+{
+   mIChatUserMutex.lock();
+
+   IChatUserMap::iterator it = mIChatUsers.find(jid.bare());
+   if(it == mIChatUsers.end())
+   {
+      if(presence != gloox::PresenceUnavailable && !jid.resource().empty())
+      {
+         // User is not yet present in map
+         IChatUser* iChatUser = new IChatUser(*this, jid.bare());
+         iChatUser->updateResourceInfo(jid.resource(), presence, priority, avAvail);
+         mIChatUsers[jid.bare()] = iChatUser;
+      }
+   }
+   else
+   {
+      it->second->updateResourceInfo(jid.resource(), presence, priority, avAvail);
+   }
+
+   mIChatUserMutex.unlock();
+}
+
+bool 
+JabberComponent::getMostAvailableIChatUserFullJID(const gloox::JID& jid, std::string& fullJID)
+{                
+   bool ret = false;
+   mIChatUserMutex.lock();
+
+   IChatUserMap::iterator it = mIChatUsers.find(jid.bare());
+   if(it != mIChatUsers.end())
+   {
+      std::string resource = it->second->getMostAvailableResource();
+      if(!resource.empty())
+      {
+         JID temp(jid);
+         temp.setResource(resource);
+         fullJID = temp.full();
+         ret = true;
+      }
+   }
+
+   mIChatUserMutex.unlock();
+
+   return ret;
+}
+
+bool 
+JabberComponent::getMostAvailableIChatUserFullJIDList(const gloox::JID& jid, std::list<std::string>& fullJIDList)
+{
+   bool ret = false;
+   mIChatUserMutex.lock();
+
+   IChatUserMap::iterator it = mIChatUsers.find(jid.bare());
+   if(it != mIChatUsers.end())
+   {
+      std::list<std::string> resourceList;
+      if(it->second->getMostAvailableResourceList(resourceList))
+      {
+         std::list<std::string>::iterator listIt = resourceList.begin();
+         for(;listIt!=resourceList.end();listIt++)
+         {
+            if(!listIt->empty())
+            {
+               JID temp(jid);
+               temp.setResource(*listIt);
+               fullJIDList.push_back(temp.full());
+               ret = true;
+            }
+         }
+      }
+   }
+
+   mIChatUserMutex.unlock();
+
+   return ret;
+}
+
+void 
+JabberComponent::notifyIChatCallRequest(const std::string& to, const std::string& from)
+{
+   IPCMsg msg;
+   msg.addArg("notifyIChatCallRequest");
+   msg.addArg(to.c_str());
+   msg.addArg(from.c_str());
+   mIPCThread.sendIPCMsg(msg);
+}
+
+void 
+JabberComponent::notifyIChatCallCancelled(unsigned int handle)
+{
+   IPCMsg msg;
+   msg.addArg("notifyIChatCallCancelled");
+   msg.addArg(handle);
+   mIPCThread.sendIPCMsg(msg);
+}
+
+void 
+JabberComponent::notifyIChatCallProceeding(unsigned int handle, const std::string& to)
+{
+   IPCMsg msg;
+   msg.addArg("notifyIChatCallProceeding");
+   msg.addArg(handle);
+   msg.addArg(to.c_str());
+   mIPCThread.sendIPCMsg(msg);
+}
+
+void 
+JabberComponent::notifyIChatCallFailed(unsigned int handle, unsigned int statusCode)
+{
+   IPCMsg msg;
+   msg.addArg("notifyIChatCallFailed");
+   msg.addArg(handle);
+   msg.addArg(statusCode);
+   mIPCThread.sendIPCMsg(msg);
+}
+
+void 
+JabberComponent::continueIChatCall(unsigned int handle, const std::string& remoteIPPortListBlob)
+{
+   IPCMsg msg;
+   msg.addArg("continueIChatCall");
+   msg.addArg(handle);
+   msg.addArg(remoteIPPortListBlob.c_str());
+   mIPCThread.sendIPCMsg(msg);
+}
+ 
+void 
+JabberComponent::sipRegisterJabberUser(const std::string& jidToRegister)
+{
+   IPCMsg msg;
+   msg.addArg("sipRegisterJabberUser");
+   msg.addArg(jidToRegister.c_str());
+   mIPCThread.sendIPCMsg(msg);
+}
+
+void 
+JabberComponent::sipUnregisterJabberUser(const std::string& jidToUnregister)
+{
+   IPCMsg msg;
+   msg.addArg("sipUnregisterJabberUser");
+   msg.addArg(jidToUnregister.c_str());
+   mIPCThread.sendIPCMsg(msg);
+}
+
+void 
+JabberComponent::checkSubscription(const std::string& to, const std::string& from)
+{
+   IPCMsg msg;
+   msg.addArg("checkSubscription");
+   msg.addArg(to.c_str());
+   msg.addArg(from.c_str());
+   mIPCThread.sendIPCMsg(msg);
+}
+
+void 
+JabberComponent::onNewIPCMsg(const IPCMsg& msg)
+{
+   const std::vector<std::string>& args = msg.getArgs();
+   assert(args.size() >= 1);
+   if(args.at(0) == "initiateIChatCall")
+   {
+      handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::onNewIPCMsg - initiateIChatCall");
+      assert(args.size() == 4);
+      initiateIChatCall(args.at(1).c_str(), args.at(2).c_str(), atoi(args.at(3).c_str()), false /* TODO - make setting? */);
+   }
+   else if(args.at(0) == "cancelIChatCall")
+   {
+      handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::onNewIPCMsg - cancelIChatCall");
+      assert(args.size() == 3);
+      cancelIChatCall(args.at(1).c_str(), args.at(2).c_str());
+   }
+   else if(args.at(0) == "proceedingIChatCall")
+   {
+      handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::onNewIPCMsg - proceedingIChatCall");
+      assert(args.size() == 4);
+      proceedingIChatCall(args.at(1).c_str(), args.at(2).c_str(), atoi(args.at(3).c_str()));
+   }
+   else if(args.at(0) == "acceptIChatCall")
+   {
+      handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::onNewIPCMsg - acceptIChatCall");
+      assert(args.size() == 3);
+      acceptIChatCall(args.at(1).c_str(), args.at(2).c_str());
+   }
+   else if(args.at(0) == "rejectIChatCall")
+   {
+      handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::onNewIPCMsg - rejectIChatCall");
+      assert(args.size() == 3);
+      rejectIChatCall(args.at(1).c_str(), args.at(2).c_str());
+   }
+   else if(args.at(0) == "sendSubscriptionResponse")
+   {
+      handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::onNewIPCMsg - sendSubscriptionResponse");
+      assert(args.size() == 4);
+      sendSubscriptionResponse(args.at(1).c_str(), args.at(2).c_str(), atoi(args.at(3).c_str()) != 0);
+   }
+   else
+   {
+      assert(false);
+   }
+}
+
+void 
+JabberComponent::handleLog(LogLevel level, LogArea area, const std::string& message)
+{
+   IPCMsg msg;
+   msg.addArg("log");
+
+   switch(level)
+   {
+   case LogLevelWarning:  /**< Non-crititcal warning messages. */
+      msg.addArg("warning");
+      break;
+   case LogLevelError:    /**< Critical, unrecoverable errors. */
+      msg.addArg("error");
+      break;
+   case LogLevelDebug:    /**< Debug messages. */
+   default:
+      msg.addArg("info");
+      break;
+   }
+   msg.addArg(message.c_str());
+   mIPCThread.sendIPCMsg(msg);
+}
+
+void 
+JabberComponent::onConnect()
+{
+   // connection established, auth done (see API docs for exceptions)
+   handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::onConnect");
+
+   // Get persisent User subscriptions - and send on-line message
+   const JabberUserDb::UserMap& users = mUserDb.getUserSubscriptions();
+   JabberUserDb::UserMap::const_iterator it = users.begin();
+   for(;it!=users.end();it++)
+   {
+      probePresence(it->first.c_str());  // Probe for users presence
+      JabberUserDb::SubscribeSet::const_iterator it2 = it->second.begin();
+      for(;it2!=it->second.end();it2++)
+      {
+         sendPresence(it->first.c_str(), it2->c_str(), it2->c_str() != mControlJID /* advertise iChat audio support */, true /* available */);
+         storeIChatSubscribedUser(it->first.c_str(), it2->c_str());
+      }
+   }
+}
+
+
+void 
+JabberComponent::onDisconnect(ConnectionError e)
+{
+   // connection established, auth done (see API docs for exceptions)
+   std::ostringstream oss;
+   oss << "JabberComponent::onDisconnect - error=" << e;
+   if(mStopping)
+   {
+      handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, oss.str());
+   }
+   else
+   {
+      handleLog(gloox::LogLevelWarning, gloox::LogAreaUser, oss.str());
+   }
+}
+
+bool 
+JabberComponent::onTLSConnect(const CertInfo& info)
+{
+   // Note:  currently gloox components to not support TLS connections
+
+   // examine certificate info
+   handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::onTLSConnect");
+   return true;
+}
+
+void 
+JabberComponent::handleSubscription( Stanza *stanza )
+{
+   switch( stanza->subtype() )
+   {
+      case StanzaS10nSubscribe:
+      {
+         handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::handleSubscription - StanzaS10nSubscribe");
+         if(stanza->to().full() == mControlJID)
+         {
+            sendSubscriptionResponse(stanza->from().bare(), stanza->to().full(), true);
+         }
+         else
+         {
+            checkSubscription(stanza->to().full(), stanza->from().bare());
+         }
+         break;
+      }
+      case StanzaS10nUnsubscribe:
+      {
+         handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::handleSubscription - StanzaS10nUnsubscribe");
+         Tag *p = new Tag( "presence" );
+         p->addAttribute( "type", "unsubscribed" );
+         p->addAttribute( "to", stanza->from().bare() );
+         p->addAttribute( "from", stanza->to().full() );
+         mComponent->send( p );
+
+         mUserDb.removeSubscribedJID(stanza->from().bare().c_str(), stanza->to().bare().c_str());
+         removeIChatSubscribedUser(stanza->from().bare(), stanza->to().bare());
+
+         break;
+      }
+      default:
+      {
+         handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::handleSubscription - subtype=" + stanza->subtype());
+         break;
+      }
+   }
+}
+
+void 
+JabberComponent::handlePresence(Stanza *stanza)
+{
+   bool iChatResource = false;
+   bool avAvail=false;
+
+   // Check if iChat endpoint
+   Tag* c = stanza->findChild("c");
+   if(c)
+   {
+      std::string node = c->findAttribute("node");
+      if(node == "apple:ichat:caps" ||
+         node == "http://www.apple.com/ichat/caps")
+      {
+         iChatResource = true;
+
+         // Check if caps include AV Available capability (note:  if iChat is already on a call then it does not have avavail capability - since iChat only allows one call at a time)
+         std::string ext = c->findAttribute("ext");
+         if(ext.find("avavail") != std::string::npos)
+         {
+            avAvail = true;
+         }
+      }
+   }
+
+   // presence info
+   switch(stanza->subtype())
+   {
+   case StanzaPresenceProbe:
+      // A request for an entity's current presence
+      handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::handlePresence - Probe from " + stanza->from().full());
+      sendPresenceForRequest(stanza);
+      break;
+
+   case StanzaPresenceAvailable:
+      // Signals to the server that the sender is online and available for communication.
+
+      if(iChatResource)
+      {
+         handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::handlePresence - PresenceAvailable from " + stanza->from().full());
+         // Ensure we are tracking client
+         storeIChatPresence(stanza->from(), stanza->presence(), stanza->priority(), avAvail);
+
+         // Check if we have an outstanding iChat Call request
+         if(avAvail)
+         {
+            mOutstandingClientIChatCallRequestsMutex.lock();
+            IChatCallRequestMap::iterator it = mOutstandingClientIChatCallRequests.begin();
+            bool callRequestFound = false;
+            while(it != mOutstandingClientIChatCallRequests.end())
+            {
+               if(it->second.mTo == stanza->from().bare())
+               {
+                  callRequestFound = true;
+                  handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::handlePresence - Available for " + stanza->from().full() + " - outstanding iChat call request - continuing call");
+   
+                  it->second.sendIChatVCRequest(stanza->from().full());
+               }
+               it++;
+            }
+            mOutstandingClientIChatCallRequestsMutex.unlock();
+         }
+      }
+      break;
+
+   case StanzaPresenceUnavailable:
+      // Signals that the entity is no longer available for communication.
+      handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::handlePresence - Unavailable for " + stanza->from().full());
+
+      // Ensure we are tracking client
+      storeIChatPresence(stanza->from(), stanza->presence(), stanza->priority(), avAvail);
+
+      // Check if we have an outstanding iChat Call request to fail
+      failOutstandingClientIChatCallRequest(stanza->from().bare(), 404);
+      break;
+
+   case StanzaPresenceError:
+      handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::handlePresence - Error for " + stanza->from().full());
+
+      {
+         unsigned int code = 0;
+         Tag *error = stanza->findChild( "error" );
+         if(error)
+         {
+            code = atoi(error->findAttribute("code").c_str());
+         }
+
+         // Check if we have an outstanding iChat Call request to fail
+         failOutstandingClientIChatCallRequest(stanza->from().bare(), code);
+      }
+      break;
+
+   default:
+      break;
+   }
+}
+
+void 
+JabberComponent::handleMessage(Stanza* stanza, MessageSession* session)
+{
+   std::ostringstream oss;
+   oss << "JabberComponent::handlePresence - " << *stanza;
+   handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, oss.str());
+   Stanza *s = Stanza::createMessageStanza(stanza->from().full(), "You have reached the ichat gateway!" );
+   s->addAttribute("from", stanza->to().full());
+
+   mComponent->send( s );
+}
+
+bool 
+JabberComponent::handleIq(Stanza *stanza)
+{
+   if(stanza->subtype() == StanzaIqSet && stanza->xmlns() == "apple:iq:vc:request")
+   {
+      handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "Jabber::handleIq - apple:iq:vc:request from=" + stanza->from().full() + ", to=" + stanza->to().full());
+      Tag::TagList tlist = stanza->children();
+      Tag* query = stanza->findChild("query");
+      Tag* vcNewCallerIPPortDataTag;
+      if(query)
+      {
+         vcNewCallerIPPortDataTag = query->findChild("VCNewCallerIPPortData");
+         if(vcNewCallerIPPortDataTag)
+         {               
+            std::ostringstream oss;
+            oss << "Jabber::handleIq: vc:request, VCNewCallerIPPortData=" << vcNewCallerIPPortDataTag->cdata() << " size=" << vcNewCallerIPPortDataTag->cdata().size();
+            handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, oss.str());
+         }
+         Tag* extSIPPort = query->findChild("extSIPPort");
+         if(extSIPPort)
+         {
+            handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "Jabber::handleIq - apple:iq:vc:request extSIPPort=" + extSIPPort->cdata());
+         }
+         Tag* extIPAddr = query->findChild("extIPAddr");
+         if(extIPAddr)
+         {
+            handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "Jabber::handleIq - apple:iq:vc:request extIPAddr=" + extIPAddr->cdata());
+         }
+         Tag* vcProtocolVersion = query->findChild("VCProtocolVersion");
+         if(vcProtocolVersion)
+         {
+            handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "Jabber::handleIq - apple:iq:vc:request VCProtocolVersion=" + vcProtocolVersion->cdata());
+         }
+      }
+
+      // Create entry in outstanding server requests map
+      std::string key = makeVCRequestKey(stanza->to().bare(),stanza->from().bare());
+      mOutstandingServerIChatCallRequestsMutex.lock();
+      IChatCallRequest* iChatCallRequest = &(mOutstandingServerIChatCallRequests[key] = IChatCallRequest(this, stanza->to().bare(), stanza->from().full(), 0));
+      mOutstandingServerIChatCallRequestsMutex.unlock();
+
+      // Pass request to SIP side
+      notifyIChatCallRequest(stanza->to().bare(), stanza->from().bare());
+   }
+   else if(stanza->subtype() == StanzaIqSet && stanza->xmlns() == "apple:iq:vc:counterProposal")
+   {
+      handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "Jabber::handleIq - apple:iq:vc:counterProposal from=" + stanza->from().full() + ", to=" + stanza->to().full());
+
+      // Build response - Do we even need to respond???
+      {
+         Tag *iq = new Tag( "iq" );
+         iq->addAttribute( "type", "set" );
+         iq->addAttribute( "id", mComponent->getID() );
+         iq->addAttribute( "to", stanza->from().full() );
+         iq->addAttribute( "from", stanza->to().full() );
+         Tag *query = new Tag( iq, "query" );
+         query->addAttribute( "xmlns", "apple:iq:vc:counterProposal");
+         new Tag( query, "connectData", mLocalIChatPortListBlob);
+         new Tag( query, "VCProtocolVersion", "1" );
+         mComponent->send(iq);
+      }
+   }
+   else if(stanza->subtype() == StanzaIqSet && stanza->xmlns() == "apple:iq:vc:cancel")
+   {
+      handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "Jabber::handleIq - apple:iq:vc:cancel from=" + stanza->from().full() + ", to=" + stanza->to().full());
+      cancelOutstandingServerIChatCallRequest(stanza->to().bare(), stanza->from().bare());
+   }
+   else if(stanza->subtype() == StanzaIqSet && stanza->xmlns() == "apple:iq:vc:response")
+   {
+      handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "Jabber::handleIq - apple:iq:vc:response from=" + stanza->from().full() + ", to=" + stanza->to().full());
+      Tag::TagList tlist = stanza->children();
+      Tag* query = stanza->findChild("query");
+      Tag* vcNewCallerIPPortDataTag;
+      if(query)
+      {
+         vcNewCallerIPPortDataTag = query->findChild("connectData");
+         if(vcNewCallerIPPortDataTag)
+         {               
+            mOutstandingClientIChatCallRequestsMutex.lock();
+
+            std::ostringstream oss;
+            oss << "Jabber::handleIq - connectData=" << vcNewCallerIPPortDataTag->cdata() << " size=" << vcNewCallerIPPortDataTag->cdata().size();
+            handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, oss.str());
+
+            IChatCallRequestMap::iterator it = findOutstandingClientIChatCallRequest(stanza->from().bare(), stanza->to().bare());
+            if(it!=mOutstandingClientIChatCallRequests.end())
+            {
+               handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "Jabber::handleIq - apple:iq:vc:response received - continuing SIP portion of call...");
+               it->second.receivedIChatVCResponse(stanza->from().full());
+               continueIChatCall(it->second.mB2BSessionHandle, vcNewCallerIPPortDataTag->cdata());    
+               mOutstandingClientIChatCallRequests.erase(it);
+            }
+            mOutstandingClientIChatCallRequestsMutex.unlock();
+         }
+      }
+   }
+   else if(stanza->subtype() == StanzaIqError && stanza->xmlns() == "apple:iq:vc:request")
+   {
+      unsigned int code = 0;
+      Tag *error = stanza->findChild( "error" );
+      if(error)
+      {
+        code = atoi(error->findAttribute("code").c_str());
+      }
+
+      failOutstandingClientIChatCallRequest(stanza->from().bare(), stanza->to().bare(), code);
+      handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "Jabber::handleIq - apple:iq:vc:request error received - notifying SIP portion of call...");
+   }
+   else
+   {
+      std::ostringstream oss;
+      oss << "Jabber::handleIq - " << *stanza;
+      handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, oss.str());
+   }
+   return true;
+}
+
+bool 
+JabberComponent::handleIqID(Stanza *stanza, int context)
+{
+   std::ostringstream oss;
+   oss << "Jabber::handleIqID - context=" << context;
+   handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, oss.str());
+   return false;
+}
+
+StringList 
+JabberComponent::handleDiscoNodeFeatures(const std::string& node)
+{
+   handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::handleDiscoNodeFeatures - node=" + node);
+   StringList slist;
+   if(node == "apple:ichat:caps#avavail")
+   {
+      slist.push_back("apple:iq:vc:available");
+   }
+   else if(node == "apple:ichat:caps#audio")
+   {
+      slist.push_back("apple:iq:vc:audio");
+   }
+   else if(node == "apple:ichat:caps#avcap")
+   {
+      slist.push_back("apple:iq:vc:capable");
+   }
+   else if(node == "apple:ichat:caps#448")
+   {
+      slist.push_back("jabber:iq:version");
+   }
+   return slist;
+}
+
+StringMap 
+JabberComponent::handleDiscoNodeIdentities(const std::string& node, std::string& name)
+{
+   handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::handleDiscoNodeIdentities - node=" + node + ", name=" + name);
+   StringMap smap;
+   smap["client"] = "pc";
+   return smap;
+}
+
+DiscoNodeItemList 
+JabberComponent::handleDiscoNodeItems(const std::string& node)
+{
+   handleLog(gloox::LogLevelDebug, gloox::LogAreaUser, "JabberComponent::handleDiscoNodeItems - node=" + node);
+   DiscoNodeItemList dlist;
+   return dlist;
+}
+
+
+/* ====================================================================
+
+ Copyright (c) 2009, SIP Spectrum, Inc.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are 
+ met:
+
+ 1. Redistributions of source code must retain the above copyright 
+    notice, this list of conditions and the following disclaimer. 
+
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution. 
+
+ 3. Neither the name of SIP Spectrum nor the names of its contributors 
+    may be used to endorse or promote products derived from this 
+    software without specific prior written permission. 
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ ==================================================================== */
+
diff --git a/apps/ichat-gw/jabberconnector/JabberComponent.hxx b/apps/ichat-gw/jabberconnector/JabberComponent.hxx
new file mode 100644
index 0000000..cfc47e2
--- /dev/null
+++ b/apps/ichat-gw/jabberconnector/JabberComponent.hxx
@@ -0,0 +1,215 @@
+#if !defined(JabberComponent_hxx)
+#define JabberComponent_hxx
+
+#include "JabberUserDb.hxx"
+#include "../IPCThread.hxx"
+
+#include <string>
+#include <map>
+#include <set>
+#include <list>
+
+#ifdef WIN32
+#define RESIP_CONTRIB_GLOOX
+#endif
+
+// Gloox includes
+#ifndef RESIP_CONTRIB_GLOOX
+#include <gloox/component.h>
+#include <gloox/mutex.h>
+#include <gloox/messagehandler.h>
+#include <gloox/presencehandler.h>
+#include <gloox/loghandler.h>
+#include <gloox/rostermanager.h>
+#include <gloox/connectionlistener.h>
+#include <gloox/stanzaextension.h>
+#include <gloox/iqhandler.h>
+#include <gloox/disco.h>
+#include <gloox/disconodehandler.h>
+#include <gloox/subscriptionhandler.h>
+#else
+#include <src/component.h>
+#include <src/mutex.h>
+#include <src/messagehandler.h>
+#include <src/presencehandler.h>
+#include <src/loghandler.h>
+#include <src/rostermanager.h>
+#include <src/connectionlistener.h>
+#include <src/stanzaextension.h>
+#include <src/iqhandler.h>
+#include <src/disco.h>
+#include <src/disconodehandler.h>
+#include <src/subscriptionhandler.h>
+#endif
+
+namespace gateway
+{
+
+class JabberComponent;
+class IChatUser;
+
+class IChatCallRequest
+{
+public:
+   IChatCallRequest() : mJabberComponent(0), mB2BSessionHandle(0), mCancelled(false) {}
+   IChatCallRequest(JabberComponent* jabberComponent, const std::string& to, const std::string& from, unsigned int handle) :
+      mJabberComponent(jabberComponent), mTo(to), mFrom(from), mB2BSessionHandle(handle), mCancelled(false) {}
+
+   // Client Methods
+   void sendIChatVCRequest(const std::string& fullTo);
+   void sendIChatVCCancelToAll();
+   void receivedIChatVCResponse(const std::string& from);
+
+   // Server Methods
+   void sendIChatVCResponse(bool accept);
+
+   JabberComponent* mJabberComponent;
+
+   std::string mTo;
+   std::string mFrom;
+   unsigned int mB2BSessionHandle;
+   bool mCancelled;
+   std::set<std::string> mPendingVCRequestSet;
+};
+
+class JabberComponent : public Thread,
+                        IPCHandler,
+                        gloox::ConnectionListener,
+                        gloox::PresenceHandler, 
+                        gloox::MessageHandler, 
+                        gloox::LogHandler, 
+                        gloox::DiscoNodeHandler, 
+                        gloox::IqHandler, 
+                        gloox::SubscriptionHandler
+{
+public:
+   JabberComponent(unsigned short jabberConnectorIPCPort,
+                   unsigned short gatewayIPCPort,
+                   const std::string& server, 
+                   const std::string& component, 
+                   const std::string& password, 
+                   int port, 
+                   unsigned int serverPingDuration,
+                   const std::string& controlUser,
+                   const std::string& localIChatPortListBlob);  
+   ~JabberComponent();
+
+   void stop();
+   void disconnect();
+
+   // Client Methods
+   void initiateIChatCall(const std::string& to, const std::string& from, unsigned int handle, bool alertOneOnly=true);
+   void cancelIChatCall(const std::string& to, const std::string& from);
+      
+   // Server Methods
+   void proceedingIChatCall(const std::string& to, const std::string& from, unsigned int handle);
+   void acceptIChatCall(const std::string& to, const std::string& from);  
+   void rejectIChatCall(const std::string& to, const std::string& from);  
+
+private:
+   void probePresence(const std::string& to);
+   void sendPresenceForRequest(gloox::Stanza* stanza);
+   void sendPresence(const std::string& to, const std::string& from, bool advertiseIChatSupport, bool available);
+   void sendSubscriptionResponse(const std::string& to, const std::string& from, bool success);
+
+   // Interfaces to send IPC messages
+   friend class IChatUser;
+   void notifyIChatCallRequest(const std::string& to, const std::string& from);  
+   void notifyIChatCallCancelled(unsigned int handle); 
+   void notifyIChatCallProceeding(unsigned int handle, const std::string& to);
+   void notifyIChatCallFailed(unsigned int handle, unsigned int statusCode);
+   void continueIChatCall(unsigned int, const std::string& remoteIPPortListBlob);
+   void sipRegisterJabberUser(const std::string& jidToRegister);
+   void sipUnregisterJabberUser(const std::string& jidToUnregister);
+   void checkSubscription(const std::string& to, const std::string& from);
+
+   // Handlers
+   virtual void onNewIPCMsg(const IPCMsg& msg);
+   virtual void handleLog(gloox::LogLevel level, gloox::LogArea area, const std::string& message);
+   virtual void onConnect();
+   virtual void onDisconnect(gloox::ConnectionError e);
+   virtual bool onTLSConnect(const gloox::CertInfo& info);
+   virtual void handleSubscription(gloox::Stanza *stanza);
+   virtual void handlePresence(gloox::Stanza *stanza);
+   virtual void handleMessage(gloox::Stanza* stanza, gloox::MessageSession* session = 0);
+   virtual bool handleIq(gloox::Stanza *stanza);
+   virtual bool handleIqID(gloox::Stanza *stanza, int context);
+   virtual gloox::StringList handleDiscoNodeFeatures(const std::string& node);
+   virtual gloox::StringMap handleDiscoNodeIdentities(const std::string& node, std::string& name);
+   virtual gloox::DiscoNodeItemList handleDiscoNodeItems(const std::string& node);
+
+   virtual void thread();
+
+   gloox::Component* mComponent;
+   bool mStopping;
+   unsigned int mServerPingDuration;
+   std::string mControlJID;
+   std::string mLocalIChatPortListBlob;
+
+   // Outstanding IChat call request maps
+   typedef std::map<std::string, IChatCallRequest> IChatCallRequestMap;
+   friend class IChatCallRequest;
+   std::string makeVCRequestKey(const std::string& bareTo, const std::string& bareFrom);
+
+   IChatCallRequestMap mOutstandingClientIChatCallRequests;
+   gloox::Mutex mOutstandingClientIChatCallRequestsMutex;
+   IChatCallRequestMap::iterator findOutstandingClientIChatCallRequest(const std::string& bareTo, const std::string& bareFrom);
+   void failOutstandingClientIChatCallRequest(const std::string& bareTo, const std::string& bareFrom, unsigned int code);
+   void failOutstandingClientIChatCallRequest(const std::string& bareTo, unsigned int code);
+
+   IChatCallRequestMap mOutstandingServerIChatCallRequests;
+   gloox::Mutex mOutstandingServerIChatCallRequestsMutex;
+   IChatCallRequestMap::iterator findOutstandingServerIChatCallRequest(const std::string& bareTo, const std::string& bareFrom);
+   void cancelOutstandingServerIChatCallRequest(const std::string& bareTo, const std::string& bareFrom);
+
+   gloox::Mutex mIChatUserMutex;
+   typedef std::map<std::string, IChatUser*> IChatUserMap;
+   IChatUserMap mIChatUsers;
+   void storeIChatSubscribedUser(const std::string& user, const std::string& subscribedJID);
+   void removeIChatSubscribedUser(const std::string& user, const std::string& subscribedJID);
+   void storeIChatPresence(const gloox::JID& jid, const gloox::Presence& presence, int priority, bool avAvail);
+   bool getMostAvailableIChatUserFullJID(const gloox::JID& jid, std::string& fullJID);
+   bool getMostAvailableIChatUserFullJIDList(const gloox::JID& jid, std::list<std::string>& fullJIDList);
+
+   JabberUserDb mUserDb;
+   IPCThread mIPCThread;
+};
+
+}
+#endif  
+
+
+/* ====================================================================
+
+ Copyright (c) 2009, SIP Spectrum, Inc.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are 
+ met:
+
+ 1. Redistributions of source code must retain the above copyright 
+    notice, this list of conditions and the following disclaimer. 
+
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution. 
+
+ 3. Neither the name of SIP Spectrum nor the names of its contributors 
+    may be used to endorse or promote products derived from this 
+    software without specific prior written permission. 
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ ==================================================================== */
+
diff --git a/apps/ichat-gw/jabberconnector/JabberUserDb.cxx b/apps/ichat-gw/jabberconnector/JabberUserDb.cxx
new file mode 100644
index 0000000..b83fc0c
--- /dev/null
+++ b/apps/ichat-gw/jabberconnector/JabberUserDb.cxx
@@ -0,0 +1,168 @@
+#include "JabberUserDb.hxx"
+
+#include <iostream>
+#include <fstream>
+#include <iterator>
+
+using namespace gateway;
+using namespace std;
+
+namespace gateway 
+{
+
+JabberUserDb::JabberUserDb()
+   : mDbFilename("ichat-gw.userdb")
+{
+   parseDbFile(mDbFilename);
+}
+
+JabberUserDb::~JabberUserDb()
+{
+}
+
+void 
+JabberUserDb::addSubscribedJID(const std::string& user, const std::string& subscribedJID)
+{
+   mUsers[user].insert(subscribedJID);
+   writeDbFile(mDbFilename);
+}
+
+void 
+JabberUserDb::removeSubscribedJID(const std::string& user, const std::string& subscribedJID)
+{
+   bool found = false;
+   UserMap::iterator it = mUsers.find(user);
+   if(it!=mUsers.end())
+   {
+      SubscribeSet::iterator it2 = it->second.find(subscribedJID);
+      if(it2!=it->second.end())
+      {
+         found = true;
+         it->second.erase(it2);
+      }
+      // Remove user entirely if no subscriptions left
+      if(it->second.size() == 0)
+      {
+         mUsers.erase(it);
+      }
+   }
+   if(found)
+   {
+      writeDbFile(mDbFilename);
+   }
+}
+
+void
+JabberUserDb::parseDbFile(const std::string& filename)
+{
+   ifstream dbFile(filename.c_str());
+   string lastUser;
+   string sline;                     
+
+   // Get first line and ensure version is present
+   if(getline(dbFile, sline))
+   {
+      if(sline != "v1")  // For now we just read version 1 format
+      {
+         cerr << "JabberUserDb::parseDbFile: invalid first line in file, expecting version line!" << endl;
+         return;
+      }
+   }
+   else
+   {
+      cerr << "JabberUserDb::parseDbFile: invalid first line in file, expecting version line!" << endl;
+      return;
+   }
+   while(getline(dbFile, sline)) 
+   {
+      if(sline.size() > 1 &&
+         sline.at(0) == '\t' &&
+         !lastUser.empty())
+      {
+         // This should be a local subscription JID
+         mUsers[lastUser].insert(sline.substr(1));
+         //cout << "'" << lastUser << "': '" << sline.substr(1) << "'" << endl;
+      }
+      else
+      {
+         // This should be a user
+         lastUser = sline;
+      }
+   }
+}
+
+void
+JabberUserDb::writeDbFile(const std::string& filename)
+{
+   std::string tempfilename = filename + ".tmp";
+
+   // safewrite - write to a temp file, delete original, then move temp file
+   ofstream dbFile(tempfilename.c_str(), std::ios_base::out | std::ios_base::trunc);
+   if(dbFile.is_open())
+   {
+      dbFile << "v1\n";
+      UserMap::iterator it = mUsers.begin();
+      for(;it!=mUsers.end();it++)
+      {
+         dbFile << it->first << "\n";
+         SubscribeSet::iterator it2 = it->second.begin();
+         for(;it2!=it->second.end();it2++)
+         {
+            dbFile << "\t" << *it2 << "\n";
+         }
+      }
+      dbFile.close();
+      if(remove(filename.c_str()) != 0)
+      {
+         cerr << "JabberUserDb::writeDbFile - error removing " << filename << " to update with new data." << endl;
+      }
+      else
+      {
+         if(rename(tempfilename.c_str(), filename.c_str()) != 0)
+         {
+            cerr << "JabberUserDb::writeDbFile - error renaming " << tempfilename << " to " << filename << " to update with new data." << endl;
+         }
+      }
+   }
+   else
+   {
+      cerr << "JabberUserDb::writeDbFile - error opening " << tempfilename << " for writing." << endl;
+   }
+}
+
+}
+
+/* ====================================================================
+
+ Copyright (c) 2009, SIP Spectrum, Inc.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are 
+ met:
+
+ 1. Redistributions of source code must retain the above copyright 
+    notice, this list of conditions and the following disclaimer. 
+
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution. 
+
+ 3. Neither the name of SIP Spectrum nor the names of its contributors 
+    may be used to endorse or promote products derived from this 
+    software without specific prior written permission. 
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ ==================================================================== */
+
diff --git a/apps/ichat-gw/IChatIPPortData.hxx b/apps/ichat-gw/jabberconnector/JabberUserDb.hxx
similarity index 73%
copy from apps/ichat-gw/IChatIPPortData.hxx
copy to apps/ichat-gw/jabberconnector/JabberUserDb.hxx
index e0bf887..d072733 100644
--- a/apps/ichat-gw/IChatIPPortData.hxx
+++ b/apps/ichat-gw/jabberconnector/JabberUserDb.hxx
@@ -1,34 +1,40 @@
-#if !defined(IChatIPPortData_hxx)
-#define IChatIPPortData_hxx
+#if !defined(JabberUserDb_hxx)
+#define JabberUserDb_hxx
 
 #include <list>
+#include <map>
+#include <set>
 #include <string>
 
-#include "rutil/Data.hxx"
-#include "resip/stack/Tuple.hxx"
-
 namespace gateway
 {
 
-class IChatIPPortData
+class JabberUserDb
 {
 public:
-   typedef std::list<std::pair<resip::Data,resip::Tuple> > IPPortDataList;
-   
-   IChatIPPortData();
-   IChatIPPortData(const std::string& hexblob);
+   JabberUserDb();
+   virtual ~JabberUserDb();
+
+   typedef std::set<std::string> SubscribeSet;
+   typedef std::map<std::string, SubscribeSet> UserMap;
 
-   void addIPPortData(const resip::Data& name, const resip::Tuple& ipPortData);
-   std::string& hexBlob();
-   const IPPortDataList& getIPPortDataList() const;
+   const UserMap& getUserSubscriptions() const { return mUsers; }
+
+   void addSubscribedJID(const std::string& user, const std::string& subscribedJID);
+   void removeSubscribedJID(const std::string& user, const std::string& subscribedJID);
 
 private:
-   IPPortDataList mIPPortDataList;
-   std::string mHexBlob;
-};
+   void parseDbFile(const std::string& filename);
+   void writeDbFile(const std::string& filename);
 
+   std::string mDbFilename;
+   UserMap mUsers;
+};
+ 
 }
-#endif  
+
+#endif
+
 
 /* ====================================================================
 
diff --git a/reTurn/test/Makefile.am b/apps/ichat-gw/jabberconnector/Makefile.am
similarity index 77%
copy from reTurn/test/Makefile.am
copy to apps/ichat-gw/jabberconnector/Makefile.am
index 33986fa..8efd9b0 100644
--- a/reTurn/test/Makefile.am
+++ b/apps/ichat-gw/jabberconnector/Makefile.am
@@ -1,31 +1,33 @@
 # $Id$
 
-EXTRA_DIST = stunTestVectors_10_0.vcxproj stunTestVectors_10_0.vcxproj.filters
-EXTRA_DIST += *.vcproj
+EXTRA_DIST = *.vcproj
 
-#AM_CXXFLAGS = -DUSE_ARES
+SUBDIRS = .
 
-LDADD = ../../rutil/librutil.la
-#LDADD += ../../contrib/ares/libares.a
-LDADD += $(LIBSSL_LIBADD) -lpthread
+#AM_CXXFLAGS = -DUSE_ARES
 
-#
-# This doesn't work because the Stun classes for reTurn are built
-# into a binary rather than a library.
-#
-# Strategy:
-# - take common classes for reTurn client and server, make a library for
-#   out of them
-# - use the library for building the client and server
-# - use the library for building this test case again
-#
-#TESTS = \
-#	stunTestVectors
+sbin_PROGRAMS = ichat-gw-jc
+ichat_gw_jc_LDADD = ../../../resip/dum/libdum.la
+ichat_gw_jc_LDADD += ../../../resip/stack/libresip.la
+ichat_gw_jc_LDADD += ../../../rutil/librutil.la
+ichat_gw_jc_LDADD += -lgloox
+ichat_gw_jc_LDADD += -lgnutls
+ichat_gw_jc_LDADD += -lidn
+ichat_gw_jc_LDADD += -lresolv
+ichat_gw_jc_LDADD += -lpthread
 
-#check_PROGRAMS = \
-#	stunTestVectors
+ichat_gw_jc_SOURCES = \
+        ichat-gw-jc.cxx \
+        IChatUser.cxx \
+        ../IPCThread.cxx \
+        JabberComponent.cxx \
+        JabberUserDb.cxx \
+        ../Thread.cxx
 
-#stunTestVectors_SOURCES = stunTestVectors.cxx
+ichat_gw_jcincludedir = $(includedir)/ichat-gw-jc
+nobase_ichat_gw_jcinclude_HEADERS = IChatUser.hxx \
+        JabberComponent.hxx \
+        JabberUserDb.hxx
 
 ##############################################################################
 # 
diff --git a/apps/clicktocall/Makefile.in b/apps/ichat-gw/jabberconnector/Makefile.in
similarity index 79%
copy from apps/clicktocall/Makefile.in
copy to apps/ichat-gw/jabberconnector/Makefile.in
index 6c988ad..63119c6 100644
--- a/apps/clicktocall/Makefile.in
+++ b/apps/ichat-gw/jabberconnector/Makefile.in
@@ -37,15 +37,14 @@ PRE_UNINSTALL = :
 POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
-sbin_PROGRAMS = clicktocall$(EXEEXT)
-subdir = apps/clicktocall
-DIST_COMMON = $(nobase_clicktocallinclude_HEADERS) \
+sbin_PROGRAMS = ichat-gw-jc$(EXEEXT)
+subdir = apps/ichat-gw/jabberconnector
+DIST_COMMON = $(nobase_ichat_gw_jcinclude_HEADERS) \
 	$(srcdir)/Makefile.am $(srcdir)/Makefile.in
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_have_epoll.m4 \
-	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
-	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
-	$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/acinclude.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
+	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+	$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
 	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
@@ -54,17 +53,13 @@ CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
 CONFIG_CLEAN_VPATH_FILES =
 am__installdirs = "$(DESTDIR)$(sbindir)" \
-	"$(DESTDIR)$(clicktocallincludedir)"
+	"$(DESTDIR)$(ichat_gw_jcincludedir)"
 PROGRAMS = $(sbin_PROGRAMS)
-am_clicktocall_OBJECTS = AddressTranslator.$(OBJEXT) \
-	AppSubsystem.$(OBJEXT) B2BSession.$(OBJEXT) \
-	clicktocall.$(OBJEXT) ConfigParser.$(OBJEXT) \
-	HttpBase.$(OBJEXT) HttpConnection.$(OBJEXT) Server.$(OBJEXT) \
-	WebAdmin.$(OBJEXT) WebAdminThread.$(OBJEXT) \
-	XmlRpcConnection.$(OBJEXT) XmlRpcServerBase.$(OBJEXT) \
-	XmlRpcServer.$(OBJEXT) XmlRpcServerThread.$(OBJEXT)
-clicktocall_OBJECTS = $(am_clicktocall_OBJECTS)
-clicktocall_DEPENDENCIES = ../../resip/dum/libdum.la \
+am_ichat_gw_jc_OBJECTS = ichat-gw-jc.$(OBJEXT) IChatUser.$(OBJEXT) \
+	IPCThread.$(OBJEXT) JabberComponent.$(OBJEXT) \
+	JabberUserDb.$(OBJEXT) Thread.$(OBJEXT)
+ichat_gw_jc_OBJECTS = $(am_ichat_gw_jc_OBJECTS)
+ichat_gw_jc_DEPENDENCIES = ../../resip/dum/libdum.la \
 	../../resip/stack/libresip.la ../../rutil/librutil.la
 DEFAULT_INCLUDES = -I. at am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
@@ -79,8 +74,8 @@ CXXLD = $(CXX)
 CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
 	--mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
 	$(LDFLAGS) -o $@
-SOURCES = $(clicktocall_SOURCES)
-DIST_SOURCES = $(clicktocall_SOURCES)
+SOURCES = $(ichat_gw_jc_SOURCES)
+DIST_SOURCES = $(ichat_gw_jc_SOURCES)
 RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
 	html-recursive info-recursive install-data-recursive \
 	install-dvi-recursive install-exec-recursive \
@@ -109,7 +104,7 @@ am__nobase_list = $(am__nobase_strip_setup); \
 am__base_list = \
   sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
   sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
-HEADERS = $(nobase_clicktocallinclude_HEADERS)
+HEADERS = $(nobase_ichat_gw_jcinclude_HEADERS)
 RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
   distclean-recursive maintainer-clean-recursive
 AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
@@ -181,11 +176,8 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
-LIBARES_LIBADD = @LIBARES_LIBADD@
-LIBGEOIP_LIBADD = @LIBGEOIP_LIBADD@
 LIBMYSQL_LIBADD = @LIBMYSQL_LIBADD@
 LIBOBJS = @LIBOBJS@
-LIBPOPT_LIBADD = @LIBPOPT_LIBADD@
 LIBRADIUS_LIBADD = @LIBRADIUS_LIBADD@
 LIBS = @LIBS@
 LIBSSL_LIBADD = @LIBSSL_LIBADD@
@@ -269,43 +261,23 @@ target_alias = @target_alias@
 top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
-EXTRA_DIST = clicktocall.config doc *.sln *.vcproj
+EXTRA_DIST = *.vcproj
 SUBDIRS = .
-clicktocall_LDADD = ../../resip/dum/libdum.la \
-	../../resip/stack/libresip.la ../../rutil/librutil.la \
-	@LIBSSL_LIBADD@ -lpcre -lpthread
-clicktocall_SOURCES = \
-        AddressTranslator.cxx \
-        AppSubsystem.cxx \
-        B2BSession.cxx \
-        clicktocall.cxx \
-        ConfigParser.cxx \
-        HttpBase.cxx \
-        HttpConnection.cxx \
-        Server.cxx \
-        WebAdmin.cxx \
-        WebAdminThread.cxx \
-        XmlRpcConnection.cxx \
-        XmlRpcServerBase.cxx \
-        XmlRpcServer.cxx \
-        XmlRpcServerThread.cxx
-
-clicktocallincludedir = $(includedir)/clicktocall
-nobase_clicktocallinclude_HEADERS = ConfigParser.hxx \
-        Version.hxx \
-        WebAdminThread.hxx \
-        XmlRpcServerBase.hxx \
-        HttpBase.hxx \
-        XmlRpcConnection.hxx \
-        AppSubsystem.hxx \
-        XmlRpcServerThread.hxx \
-        B2BSession.hxx \
-        AddressTranslator.hxx \
-        WebAdmin.hxx \
-        XmlRpcServer.hxx \
-        HttpConnection.hxx \
-        Server.hxx \
-        ClickToCallCmds.hxx
+ichat_gw_jc_LDADD = ../../resip/dum/libdum.la \
+	../../resip/stack/libresip.la ../../rutil/librutil.la -lgloox \
+	-lgnutls -lidn -lresolv -lpthread
+ichat_gw_jc_SOURCES = \
+        ichat-gw-jc.cxx \
+        IChatUser.cxx \
+        ../IPCThread.cxx \
+        JabberComponent.cxx \
+        JabberUserDb.cxx \
+        ../Thread.cxx
+
+ichat_gw_jcincludedir = $(includedir)/ichat-gw-jc
+nobase_ichat_gw_jcinclude_HEADERS = IChatUser.hxx \
+        JabberComponent.hxx \
+        JabberUserDb.hxx
 
 all: all-recursive
 
@@ -320,9 +292,9 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
 	      exit 1;; \
 	  esac; \
 	done; \
-	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu apps/clicktocall/Makefile'; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu apps/ichat-gw/jabberconnector/Makefile'; \
 	$(am__cd) $(top_srcdir) && \
-	  $(AUTOMAKE) --gnu apps/clicktocall/Makefile
+	  $(AUTOMAKE) --gnu apps/ichat-gw/jabberconnector/Makefile
 .PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
 	@case '$?' in \
@@ -384,9 +356,9 @@ clean-sbinPROGRAMS:
 	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
 	echo " rm -f" $$list; \
 	rm -f $$list
-clicktocall$(EXEEXT): $(clicktocall_OBJECTS) $(clicktocall_DEPENDENCIES) 
-	@rm -f clicktocall$(EXEEXT)
-	$(CXXLINK) $(clicktocall_OBJECTS) $(clicktocall_LDADD) $(LIBS)
+ichat-gw-jc$(EXEEXT): $(ichat_gw_jc_OBJECTS) $(ichat_gw_jc_DEPENDENCIES) 
+	@rm -f ichat-gw-jc$(EXEEXT)
+	$(CXXLINK) $(ichat_gw_jc_OBJECTS) $(ichat_gw_jc_LDADD) $(LIBS)
 
 mostlyclean-compile:
 	-rm -f *.$(OBJEXT)
@@ -394,20 +366,12 @@ mostlyclean-compile:
 distclean-compile:
 	-rm -f *.tab.c
 
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/AddressTranslator.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/AppSubsystem.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/B2BSession.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/ConfigParser.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/HttpBase.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/HttpConnection.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/Server.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/WebAdmin.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/WebAdminThread.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/XmlRpcConnection.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/XmlRpcServer.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/XmlRpcServerBase.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/XmlRpcServerThread.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/clicktocall.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/IChatUser.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/IPCThread.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/JabberComponent.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/JabberUserDb.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/Thread.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/ichat-gw-jc.Po at am__quote@
 
 .cxx.o:
 @am__fastdepCXX_TRUE@	$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -430,34 +394,62 @@ distclean-compile:
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@	$(LTCXXCOMPILE) -c -o $@ $<
 
+IPCThread.o: ../IPCThread.cxx
+ at am__fastdepCXX_TRUE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT IPCThread.o -MD -MP -MF $(DEPDIR)/IPCThread.Tpo -c -o IPCThread.o `test -f '../IPCThread.cxx' || echo '$(srcdir)/'`../IPCThread.cxx
+ at am__fastdepCXX_TRUE@	$(am__mv) $(DEPDIR)/IPCThread.Tpo $(DEPDIR)/IPCThread.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='../IPCThread.cxx' object='IPCThread.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o IPCThread.o `test -f '../IPCThread.cxx' || echo '$(srcdir)/'`../IPCThread.cxx
+
+IPCThread.obj: ../IPCThread.cxx
+ at am__fastdepCXX_TRUE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT IPCThread.obj -MD -MP -MF $(DEPDIR)/IPCThread.Tpo -c -o IPCThread.obj `if test -f '../IPCThread.cxx'; then $(CYGPATH_W) '../IPCThread.cxx'; else $(CYGPATH_W) '$(srcdir)/../IPCThread.cxx'; fi`
+ at am__fastdepCXX_TRUE@	$(am__mv) $(DEPDIR)/IPCThread.Tpo $(DEPDIR)/IPCThread.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='../IPCThread.cxx' object='IPCThread.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o IPCThread.obj `if test -f '../IPCThread.cxx'; then $(CYGPATH_W) '../IPCThread.cxx'; else $(CYGPATH_W) '$(srcdir)/../IPCThread.cxx'; fi`
+
+Thread.o: ../Thread.cxx
+ at am__fastdepCXX_TRUE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT Thread.o -MD -MP -MF $(DEPDIR)/Thread.Tpo -c -o Thread.o `test -f '../Thread.cxx' || echo '$(srcdir)/'`../Thread.cxx
+ at am__fastdepCXX_TRUE@	$(am__mv) $(DEPDIR)/Thread.Tpo $(DEPDIR)/Thread.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='../Thread.cxx' object='Thread.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o Thread.o `test -f '../Thread.cxx' || echo '$(srcdir)/'`../Thread.cxx
+
+Thread.obj: ../Thread.cxx
+ at am__fastdepCXX_TRUE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT Thread.obj -MD -MP -MF $(DEPDIR)/Thread.Tpo -c -o Thread.obj `if test -f '../Thread.cxx'; then $(CYGPATH_W) '../Thread.cxx'; else $(CYGPATH_W) '$(srcdir)/../Thread.cxx'; fi`
+ at am__fastdepCXX_TRUE@	$(am__mv) $(DEPDIR)/Thread.Tpo $(DEPDIR)/Thread.Po
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='../Thread.cxx' object='Thread.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o Thread.obj `if test -f '../Thread.cxx'; then $(CYGPATH_W) '../Thread.cxx'; else $(CYGPATH_W) '$(srcdir)/../Thread.cxx'; fi`
+
 mostlyclean-libtool:
 	-rm -f *.lo
 
 clean-libtool:
 	-rm -rf .libs _libs
-install-nobase_clicktocallincludeHEADERS: $(nobase_clicktocallinclude_HEADERS)
+install-nobase_ichat_gw_jcincludeHEADERS: $(nobase_ichat_gw_jcinclude_HEADERS)
 	@$(NORMAL_INSTALL)
-	test -z "$(clicktocallincludedir)" || $(MKDIR_P) "$(DESTDIR)$(clicktocallincludedir)"
-	@list='$(nobase_clicktocallinclude_HEADERS)'; test -n "$(clicktocallincludedir)" || list=; \
+	test -z "$(ichat_gw_jcincludedir)" || $(MKDIR_P) "$(DESTDIR)$(ichat_gw_jcincludedir)"
+	@list='$(nobase_ichat_gw_jcinclude_HEADERS)'; test -n "$(ichat_gw_jcincludedir)" || list=; \
 	$(am__nobase_list) | while read dir files; do \
 	  xfiles=; for file in $$files; do \
 	    if test -f "$$file"; then xfiles="$$xfiles $$file"; \
 	    else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \
 	  test -z "$$xfiles" || { \
 	    test "x$$dir" = x. || { \
-	      echo "$(MKDIR_P) '$(DESTDIR)$(clicktocallincludedir)/$$dir'"; \
-	      $(MKDIR_P) "$(DESTDIR)$(clicktocallincludedir)/$$dir"; }; \
-	    echo " $(INSTALL_HEADER) $$xfiles '$(DESTDIR)$(clicktocallincludedir)/$$dir'"; \
-	    $(INSTALL_HEADER) $$xfiles "$(DESTDIR)$(clicktocallincludedir)/$$dir" || exit $$?; }; \
+	      echo "$(MKDIR_P) '$(DESTDIR)$(ichat_gw_jcincludedir)/$$dir'"; \
+	      $(MKDIR_P) "$(DESTDIR)$(ichat_gw_jcincludedir)/$$dir"; }; \
+	    echo " $(INSTALL_HEADER) $$xfiles '$(DESTDIR)$(ichat_gw_jcincludedir)/$$dir'"; \
+	    $(INSTALL_HEADER) $$xfiles "$(DESTDIR)$(ichat_gw_jcincludedir)/$$dir" || exit $$?; }; \
 	done
 
-uninstall-nobase_clicktocallincludeHEADERS:
+uninstall-nobase_ichat_gw_jcincludeHEADERS:
 	@$(NORMAL_UNINSTALL)
-	@list='$(nobase_clicktocallinclude_HEADERS)'; test -n "$(clicktocallincludedir)" || list=; \
+	@list='$(nobase_ichat_gw_jcinclude_HEADERS)'; test -n "$(ichat_gw_jcincludedir)" || list=; \
 	$(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \
 	test -n "$$files" || exit 0; \
-	echo " ( cd '$(DESTDIR)$(clicktocallincludedir)' && rm -f" $$files ")"; \
-	cd "$(DESTDIR)$(clicktocallincludedir)" && rm -f $$files
+	echo " ( cd '$(DESTDIR)$(ichat_gw_jcincludedir)' && rm -f" $$files ")"; \
+	cd "$(DESTDIR)$(ichat_gw_jcincludedir)" && rm -f $$files
 
 # This directory's subdirectories are mostly independent; you can cd
 # into them and run `make' without going through this Makefile.
@@ -657,7 +649,7 @@ check: check-recursive
 all-am: Makefile $(PROGRAMS) $(HEADERS)
 installdirs: installdirs-recursive
 installdirs-am:
-	for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(clicktocallincludedir)"; do \
+	for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(ichat_gw_jcincludedir)"; do \
 	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
 	done
 install: install-recursive
@@ -708,7 +700,7 @@ info: info-recursive
 
 info-am:
 
-install-data-am: install-nobase_clicktocallincludeHEADERS
+install-data-am: install-nobase_ichat_gw_jcincludeHEADERS
 
 install-dvi: install-dvi-recursive
 
@@ -754,7 +746,7 @@ ps: ps-recursive
 
 ps-am:
 
-uninstall-am: uninstall-nobase_clicktocallincludeHEADERS \
+uninstall-am: uninstall-nobase_ichat_gw_jcincludeHEADERS \
 	uninstall-sbinPROGRAMS
 
 .MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \
@@ -768,14 +760,14 @@ uninstall-am: uninstall-nobase_clicktocallincludeHEADERS \
 	install install-am install-data install-data-am install-dvi \
 	install-dvi-am install-exec install-exec-am install-html \
 	install-html-am install-info install-info-am install-man \
-	install-nobase_clicktocallincludeHEADERS install-pdf \
+	install-nobase_ichat_gw_jcincludeHEADERS install-pdf \
 	install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \
 	install-strip installcheck installcheck-am installdirs \
 	installdirs-am maintainer-clean maintainer-clean-generic \
 	mostlyclean mostlyclean-compile mostlyclean-generic \
 	mostlyclean-libtool pdf pdf-am ps ps-am tags tags-recursive \
 	uninstall uninstall-am \
-	uninstall-nobase_clicktocallincludeHEADERS \
+	uninstall-nobase_ichat_gw_jcincludeHEADERS \
 	uninstall-sbinPROGRAMS
 
 
diff --git a/apps/ichat-gw/ichat-gw.cxx b/apps/ichat-gw/jabberconnector/ichat-gw-jc.cxx
similarity index 60%
copy from apps/ichat-gw/ichat-gw.cxx
copy to apps/ichat-gw/jabberconnector/ichat-gw-jc.cxx
index 9650e35..5aa54b4 100644
--- a/apps/ichat-gw/ichat-gw.cxx
+++ b/apps/ichat-gw/jabberconnector/ichat-gw-jc.cxx
@@ -1,21 +1,12 @@
 #include <signal.h>
+#include <iostream>
+#include <assert.h>
 
-#include "AppSubsystem.hxx"
-#include "Server.hxx"
-
-#include <rutil/Log.hxx>
-#include <rutil/Logger.hxx>
-#include <rutil/DnsUtil.hxx>
-#include <rutil/BaseException.hxx>
-#include <resip/stack/NameAddr.hxx>
-#include <rutil/WinLeakCheck.hxx>
+#include "JabberComponent.hxx"
 
 using namespace gateway;
-using namespace resip;
 using namespace std;
 
-#define RESIPROCATE_SUBSYSTEM AppSubsystem::GATEWAY
-
 void sleepSeconds(unsigned int seconds)
 {
 #ifdef WIN32
@@ -25,13 +16,13 @@ void sleepSeconds(unsigned int seconds)
 #endif
 }
 
-static bool finished = false;
+JabberComponent* g_component;
 
 static void
 signalHandler(int signo)
 {
-   //std::cerr << "Shutting down..." << endl;
-   finished = true;
+   //std::cerr << "Shutting down" << endl;
+   g_component->stop();
 }
 
 int 
@@ -43,10 +34,6 @@ main (int argc, char** argv)
       cerr << "Couldn't install signal handler for SIGPIPE" << endl;
       exit(-1);
    }
-#else
-#if defined(_DEBUG) && defined(LEAK_CHECK)
-   resip::FindMemoryLeaks fml;
-#endif
 #endif
 
    if ( signal( SIGINT, signalHandler ) == SIG_ERR )
@@ -61,33 +48,50 @@ main (int argc, char** argv)
       exit( -1 );
    }
 
-   initNetwork();
+   if(argc != 11 || std::string(argv[0]) != std::string("ichat-gw"))
+   {
+      cerr << "argc=" << argc << ", argv[0]=" << argv[0] << endl;
+      cerr << "Jabber connector process must not be launched manually, it is launched automatically from the main ichat-gw program." << endl;
+      exit(-1);
+   }
 
-   //////////////////////////////////////////////////////////////////////////////
-   // Create Server
-   //////////////////////////////////////////////////////////////////////////////
+#if defined(WIN32)
+   WORD wVersionRequested = MAKEWORD( 2, 2 );
+   WSADATA wsaData;
+   int err = WSAStartup( wVersionRequested, &wsaData );
+   if ( err != 0 ) 
    {
-      Server server(argc, argv);
+      // could not find a usable WinSock DLL
+      cerr << "Could not load winsock" << endl;
+      assert(0); 
+      exit(1);
+   }    
+#endif
 
-      //////////////////////////////////////////////////////////////////////////////
-      // Startup and run...
-      //////////////////////////////////////////////////////////////////////////////
+   unsigned int jabberConnectorIPCPort = atoi(argv[1]);
+   unsigned int gatewayIPCPort = atoi(argv[2]);
 
-      server.startup();
+   g_component = new JabberComponent(jabberConnectorIPCPort,
+                                     gatewayIPCPort,
+                                     argv[3],   // Jabber server
+                                     argv[4],   // Jabber component name
+                                     argv[5],   // Jabber component password
+                                     atoi(argv[6]),   // Jabber component port 
+                                     atoi(argv[7]),   // Jabber server ping duration 
+                                     argv[8],   // Jabber control username 
+                                     argv[9]);  // IPPort Data blob
 
-      while(true)
-      {
-         server.process(50);
-         if(finished) break;
-      }
 
-      server.shutdown();
-   }
+   g_component->run();
 
-   InfoLog(<< "ichat-gw is shutdown.");
+   g_component->join();
+   delete g_component;
+
+   cout << "ichat-gw-jc is shutdown." << endl;
    sleepSeconds(2);
 }
 
+
 /* ====================================================================
 
  Copyright (c) 2009, SIP Spectrum, Inc.
diff --git a/apps/ichat-gw/ichat-gw_8_0.vcproj b/apps/ichat-gw/jabberconnector/jabberconnector_8_0.vcproj
similarity index 62%
copy from apps/ichat-gw/ichat-gw_8_0.vcproj
copy to apps/ichat-gw/jabberconnector/jabberconnector_8_0.vcproj
index 6993933..27e3376 100644
--- a/apps/ichat-gw/ichat-gw_8_0.vcproj
+++ b/apps/ichat-gw/jabberconnector/jabberconnector_8_0.vcproj
@@ -2,9 +2,9 @@
 <VisualStudioProject
 	ProjectType="Visual C++"
 	Version="8.00"
-	Name="ichat-gw"
-	ProjectGUID="{8BA87397-9B42-4820-BA53-62FF35C22CAD}"
-	RootNamespace="ichatgw"
+	Name="jabberconnector"
+	ProjectGUID="{A77DCD34-20CA-4602-B185-EE3EAD97713D}"
+	RootNamespace="jabberconnector"
 	Keyword="Win32Proj"
 	>
 	<Platforms>
@@ -40,7 +40,7 @@
 			<Tool
 				Name="VCCLCompilerTool"
 				Optimization="0"
-				AdditionalIncludeDirectories=""$(ProjectDir)../";"$(ProjectDir)../contrib/openssl/include";"$(ProjectDir)../contrib/openssl/inc32";"$(ProjectDir)../contrib/pcre""
+				AdditionalIncludeDirectories=""$(ProjectDir)../../";"$(ProjectDir)../../contrib/gloox""
 				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;USE_SSL;USE_IPV6"
 				MinimalRebuild="true"
 				BasicRuntimeChecks="3"
@@ -61,7 +61,8 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="Ws2_32.lib Dnsapi.lib Iphlpapi.lib crypt32.lib "$(ProjectDir)..\contrib\openssl\lib\vc\static\libeay32MDd.lib" "$(ProjectDir)..\contrib\openssl\lib\vc\static\ssleay32MDd.lib""
+				AdditionalDependencies="Ws2_32.lib Dnsapi.lib Iphlpapi.lib crypt32.lib "$(ProjectDir)..\..\contrib\openssl\lib\vc\static\libeay32MDd.lib" "$(ProjectDir)..\..\contrib\openssl\lib\vc\static\ssleay32MDd.lib""
+				OutputFile="$(OutDir)\ichat-gw-jc.exe"
 				LinkIncremental="2"
 				GenerateDebugInformation="true"
 				SubSystem="1"
@@ -90,6 +91,7 @@
 			/>
 			<Tool
 				Name="VCPostBuildEventTool"
+				CommandLine="copy $(TargetPath) $(ProjectDir)..\"
 			/>
 		</Configuration>
 		<Configuration
@@ -117,7 +119,7 @@
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories=""$(ProjectDir)../";"$(ProjectDir)../contrib/openssl/include";"$(ProjectDir)../contrib/openssl/inc32";"$(ProjectDir)../contrib/pcre""
+				AdditionalIncludeDirectories=""$(ProjectDir)../../";"$(ProjectDir)../../contrib/gloox""
 				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;USE_SSL;USE_IPV6"
 				RuntimeLibrary="2"
 				UsePrecompiledHeader="0"
@@ -136,7 +138,8 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="Ws2_32.lib Dnsapi.lib Iphlpapi.lib crypt32.lib "$(ProjectDir)..\contrib\openssl\lib\vc\static\libeay32MD.lib" "$(ProjectDir)..\contrib\openssl\lib\vc\static\ssleay32MD.lib""
+				AdditionalDependencies="Ws2_32.lib Dnsapi.lib Iphlpapi.lib crypt32.lib "$(ProjectDir)..\..\contrib\openssl\lib\vc\static\libeay32MD.lib" "$(ProjectDir)..\..\contrib\openssl\lib\vc\static\ssleay32MD.lib""
+				OutputFile="$(OutDir)\ichat-gw-jc.exe"
 				LinkIncremental="1"
 				GenerateDebugInformation="true"
 				SubSystem="1"
@@ -167,6 +170,7 @@
 			/>
 			<Tool
 				Name="VCPostBuildEventTool"
+				CommandLine="copy $(TargetPath) $(ProjectDir)..\"
 			/>
 		</Configuration>
 	</Configurations>
@@ -179,51 +183,27 @@
 			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
 			>
 			<File
-				RelativePath=".\AddressTranslator.cxx"
+				RelativePath=".\ichat-gw-jc.cxx"
 				>
 			</File>
 			<File
-				RelativePath=".\AppSubsystem.cxx"
+				RelativePath=".\IChatUser.cxx"
 				>
 			</File>
 			<File
-				RelativePath=".\B2BSession.cxx"
+				RelativePath="..\IPCThread.cxx"
 				>
 			</File>
 			<File
-				RelativePath=".\ConfigParser.cxx"
+				RelativePath=".\JabberComponent.cxx"
 				>
 			</File>
 			<File
-				RelativePath=".\ichat-gw.cxx"
+				RelativePath=".\JabberUserDb.cxx"
 				>
 			</File>
 			<File
-				RelativePath=".\IChatIPPortData.cxx"
-				>
-			</File>
-			<File
-				RelativePath=".\IPCThread.cxx"
-				>
-			</File>
-			<File
-				RelativePath=".\MediaRelay.cxx"
-				>
-			</File>
-			<File
-				RelativePath=".\MediaRelayPort.cxx"
-				>
-			</File>
-			<File
-				RelativePath=".\Server.cxx"
-				>
-			</File>
-			<File
-				RelativePath=".\SipRegistration.cxx"
-				>
-			</File>
-			<File
-				RelativePath=".\Thread.cxx"
+				RelativePath="..\Thread.cxx"
 				>
 			</File>
 		</Filter>
@@ -233,55 +213,23 @@
 			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
 			>
 			<File
-				RelativePath=".\AddressTranslator.hxx"
-				>
-			</File>
-			<File
-				RelativePath=".\AppSubsystem.hxx"
-				>
-			</File>
-			<File
-				RelativePath=".\B2BSession.hxx"
-				>
-			</File>
-			<File
-				RelativePath=".\ConfigParser.hxx"
-				>
-			</File>
-			<File
-				RelativePath=".\IChatGatewayCmds.hxx"
-				>
-			</File>
-			<File
-				RelativePath=".\IChatIPPortData.hxx"
-				>
-			</File>
-			<File
-				RelativePath=".\IPCThread.hxx"
-				>
-			</File>
-			<File
-				RelativePath=".\MediaRelay.hxx"
-				>
-			</File>
-			<File
-				RelativePath=".\MediaRelayPort.hxx"
+				RelativePath=".\IChatUser.hxx"
 				>
 			</File>
 			<File
-				RelativePath=".\Server.hxx"
+				RelativePath="..\IPCThread.hxx"
 				>
 			</File>
 			<File
-				RelativePath=".\SipRegistration.hxx"
+				RelativePath=".\JabberComponent.hxx"
 				>
 			</File>
 			<File
-				RelativePath=".\Thread.hxx"
+				RelativePath=".\JabberUserDb.hxx"
 				>
 			</File>
 			<File
-				RelativePath=".\Version.hxx"
+				RelativePath="..\Thread.hxx"
 				>
 			</File>
 		</Filter>
diff --git a/reTurn/test/stunTestVectors_9_0.vcproj b/apps/ichat-gw/jabberconnector/jabberconnector_9_0.vcproj
similarity index 62%
copy from reTurn/test/stunTestVectors_9_0.vcproj
copy to apps/ichat-gw/jabberconnector/jabberconnector_9_0.vcproj
index 683dced..281403b 100644
--- a/reTurn/test/stunTestVectors_9_0.vcproj
+++ b/apps/ichat-gw/jabberconnector/jabberconnector_9_0.vcproj
@@ -2,9 +2,9 @@
 <VisualStudioProject
 	ProjectType="Visual C++"
 	Version="9.00"
-	Name="stunTestVectors"
-	ProjectGUID="{73151749-13F0-4093-97F1-B6952ECECDCF}"
-	RootNamespace="stunTestVectors"
+	Name="jabberconnector"
+	ProjectGUID="{A77DCD34-20CA-4602-B185-EE3EAD97713D}"
+	RootNamespace="jabberconnector"
 	Keyword="Win32Proj"
 	TargetFrameworkVersion="131072"
 	>
@@ -18,11 +18,10 @@
 	<Configurations>
 		<Configuration
 			Name="Debug|Win32"
-			OutputDirectory="Debug"
-			IntermediateDirectory="Debug"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
 			ConfigurationType="1"
-			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
-			CharacterSet="2"
+			CharacterSet="1"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -42,8 +41,8 @@
 			<Tool
 				Name="VCCLCompilerTool"
 				Optimization="0"
-				AdditionalIncludeDirectories="../../contrib/asio;../../contrib/boost_1_34_1;../../;../../../contrib/OpenSSL/include;../../contrib/OpenSSL/inc32"
-				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;BOOST_ALL_NO_LIB;_WIN32_WINNT=0x0501;USE_SSL;LEAK_CHECK;ASIO_ENABLE_CANCELIO"
+				AdditionalIncludeDirectories=""$(ProjectDir)../../../";"$(ProjectDir)../../../contrib/gloox""
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;USE_SSL;USE_IPV6"
 				MinimalRebuild="true"
 				BasicRuntimeChecks="3"
 				RuntimeLibrary="3"
@@ -63,11 +62,10 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="winmm.lib "$(ProjectDir)..\..\contrib\openssl\out32.dbg\libeay32.lib" "$(ProjectDir)..\..\contrib\openssl\out32.dbg\ssleay32.lib" iphlpapi.lib"
-				OutputFile="$(OutDir)/stunTestVectors.exe"
+				AdditionalDependencies="Ws2_32.lib winmm.lib Dnsapi.lib Iphlpapi.lib crypt32.lib"
+				OutputFile="$(OutDir)\ichat-gw-jc.exe"
 				LinkIncremental="2"
 				GenerateDebugInformation="true"
-				ProgramDatabaseFile="$(OutDir)/stunTestVectors.pdb"
 				SubSystem="1"
 				RandomizedBaseAddress="1"
 				DataExecutionPrevention="0"
@@ -93,15 +91,16 @@
 			/>
 			<Tool
 				Name="VCPostBuildEventTool"
+				CommandLine="copy $(TargetPath) $(ProjectDir)..\"
 			/>
 		</Configuration>
 		<Configuration
 			Name="Release|Win32"
-			OutputDirectory="Release"
-			IntermediateDirectory="Release"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
 			ConfigurationType="1"
-			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
-			CharacterSet="2"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"
@@ -120,8 +119,8 @@
 			/>
 			<Tool
 				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="../../contrib/asio;../../contrib/boost_1_34_1;../../;../../contrib/OpenSSL/include;../../contrib/OpenSSL/inc32"
-				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;BOOST_ALL_NO_LIB;_WIN32_WINNT=0x0501;USE_SSL;ASIO_ENABLE_CANCELIO"
+				AdditionalIncludeDirectories=""$(ProjectDir)../../../";"$(ProjectDir)../../../contrib/gloox""
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;USE_SSL;USE_IPV6"
 				RuntimeLibrary="2"
 				UsePrecompiledHeader="0"
 				WarningLevel="3"
@@ -139,8 +138,8 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="winmm.lib "$(ProjectDir)..\..\contrib\openssl\out32\libeay32.lib" "$(ProjectDir)..\..\contrib\openssl\out32\ssleay32.lib" iphlpapi.lib"
-				OutputFile="$(OutDir)/stunTestVectors.exe"
+				AdditionalDependencies="Ws2_32.lib winmm.lib Dnsapi.lib Iphlpapi.lib crypt32.lib"
+				OutputFile="$(OutDir)\ichat-gw-jc.exe"
 				LinkIncremental="1"
 				GenerateDebugInformation="true"
 				SubSystem="1"
@@ -170,6 +169,7 @@
 			/>
 			<Tool
 				Name="VCPostBuildEventTool"
+				CommandLine="copy $(TargetPath) $(ProjectDir)..\"
 			/>
 		</Configuration>
 	</Configurations>
@@ -178,23 +178,31 @@
 	<Files>
 		<Filter
 			Name="Source Files"
-			Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
 			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
 			>
 			<File
-				RelativePath="..\ReTurnSubsystem.cxx"
+				RelativePath=".\ichat-gw-jc.cxx"
 				>
 			</File>
 			<File
-				RelativePath="..\StunMessage.cxx"
+				RelativePath=".\IChatUser.cxx"
 				>
 			</File>
 			<File
-				RelativePath=".\stunTestVectors.cxx"
+				RelativePath="..\IPCThread.cxx"
 				>
 			</File>
 			<File
-				RelativePath="..\StunTuple.cxx"
+				RelativePath=".\JabberComponent.cxx"
+				>
+			</File>
+			<File
+				RelativePath=".\JabberUserDb.cxx"
+				>
+			</File>
+			<File
+				RelativePath="..\Thread.cxx"
 				>
 			</File>
 		</Filter>
@@ -204,21 +212,29 @@
 			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
 			>
 			<File
-				RelativePath="..\ReTurnSubsystem.hxx"
+				RelativePath=".\IChatUser.hxx"
+				>
+			</File>
+			<File
+				RelativePath="..\IPCThread.hxx"
+				>
+			</File>
+			<File
+				RelativePath=".\JabberComponent.hxx"
 				>
 			</File>
 			<File
-				RelativePath="..\StunMessage.hxx"
+				RelativePath=".\JabberUserDb.hxx"
 				>
 			</File>
 			<File
-				RelativePath="..\StunTuple.hxx"
+				RelativePath="..\Thread.hxx"
 				>
 			</File>
 		</Filter>
 		<Filter
 			Name="Resource Files"
-			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
 			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
 			>
 		</Filter>
diff --git a/apps/sipdial/Makefile.am b/apps/sipdial/Makefile.am
index 6be9403..6f9066e 100644
--- a/apps/sipdial/Makefile.am
+++ b/apps/sipdial/Makefile.am
@@ -22,6 +22,9 @@ libsipdial_la_LDFLAGS += -export-dynamic
 
 bin_PROGRAMS = sipdialer
 sipdialer_LDADD = libsipdial.la
+sipdialer_LDADD += ../../resip/dum/libdum.la
+sipdialer_LDADD += ../../resip/stack/libresip.la
+sipdialer_LDADD += ../../rutil/librutil.la
 
 libsipdial_la_SOURCES = \
         DialInstance.cpp \
diff --git a/apps/sipdial/Makefile.in b/apps/sipdial/Makefile.in
index 3f1ac01..95cfe7e 100644
--- a/apps/sipdial/Makefile.in
+++ b/apps/sipdial/Makefile.in
@@ -89,7 +89,8 @@ libsipdial_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
 PROGRAMS = $(bin_PROGRAMS)
 am_sipdialer_OBJECTS = sipdialer.$(OBJEXT)
 sipdialer_OBJECTS = $(am_sipdialer_OBJECTS)
-sipdialer_DEPENDENCIES = libsipdial.la
+sipdialer_DEPENDENCIES = libsipdial.la ../../resip/dum/libdum.la \
+	../../resip/stack/libresip.la ../../rutil/librutil.la
 DEFAULT_INCLUDES = -I. at am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
 am__depfiles_maybe = depfiles
@@ -287,7 +288,8 @@ libsipdial_la_LIBADD = ../../resip/dum/libdum.la \
 	@LIBSSL_LIBADD@ -lpthread
 libsipdial_la_LDFLAGS = -version-info 0:0:0 -release @ABIVERSION@ \
 	-export-dynamic
-sipdialer_LDADD = libsipdial.la
+sipdialer_LDADD = libsipdial.la ../../resip/dum/libdum.la \
+	../../resip/stack/libresip.la ../../rutil/librutil.la
 libsipdial_la_SOURCES = \
         DialInstance.cpp \
         MyInviteSessionHandler.cpp \
diff --git a/config.h.in b/config.h.in
index 4142663..73672f9 100644
--- a/config.h.in
+++ b/config.h.in
@@ -3,6 +3,9 @@
 /* BUILD_APPS */
 #undef BUILD_APPS
 
+/* BUILD_ICHAT_GW */
+#undef BUILD_ICHAT_GW
+
 /* BUILD_P2P */
 #undef BUILD_P2P
 
diff --git a/configure b/configure
index 65dc979..6778b9e 100755
--- a/configure
+++ b/configure
@@ -752,6 +752,8 @@ BUILD_P2P_FALSE
 BUILD_P2P_TRUE
 BUILD_RECON_FALSE
 BUILD_RECON_TRUE
+BUILD_ICHAT_GW_FALSE
+BUILD_ICHAT_GW_TRUE
 BUILD_APPS_FALSE
 BUILD_APPS_TRUE
 BUILD_TFM_FALSE
@@ -910,6 +912,7 @@ with_geoip
 with_radius
 with_tfm
 with_apps
+with_ichat_gw
 with_recon
 with_p2p
 enable_maintainer_mode
@@ -1574,6 +1577,7 @@ Optional Packages:
   --with-radius           Link against RADIUS client libraries
   --with-tfm              Build TFM, links against Netxx and cppunit
   --with-apps             Build apps, links against various things
+  --with-ichat-gw         Build iChat gateway, links against gloox
   --with-recon            Build recon, links against sipX
   --with-p2p              Build P2P, links against S2C and SSL, unfinished
 
@@ -5109,13 +5113,13 @@ if test "${lt_cv_nm_interface+set}" = set; then :
 else
   lt_cv_nm_interface="BSD nm"
   echo "int some_variable = 0;" > conftest.$ac_ext
-  (eval echo "\"\$as_me:5112: $ac_compile\"" >&5)
+  (eval echo "\"\$as_me:5116: $ac_compile\"" >&5)
   (eval "$ac_compile" 2>conftest.err)
   cat conftest.err >&5
-  (eval echo "\"\$as_me:5115: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+  (eval echo "\"\$as_me:5119: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
   (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
   cat conftest.err >&5
-  (eval echo "\"\$as_me:5118: output\"" >&5)
+  (eval echo "\"\$as_me:5122: output\"" >&5)
   cat conftest.out >&5
   if $GREP 'External.*some_variable' conftest.out > /dev/null; then
     lt_cv_nm_interface="MS dumpbin"
@@ -6321,7 +6325,7 @@ ia64-*-hpux*)
   ;;
 *-*-irix6*)
   # Find out which ABI we are using.
-  echo '#line 6324 "configure"' > conftest.$ac_ext
+  echo '#line 6328 "configure"' > conftest.$ac_ext
   if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
   (eval $ac_compile) 2>&5
   ac_status=$?
@@ -8377,11 +8381,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:8380: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:8384: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:8384: \$? = $ac_status" >&5
+   echo "$as_me:8388: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -8716,11 +8720,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:8719: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:8723: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:8723: \$? = $ac_status" >&5
+   echo "$as_me:8727: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -8821,11 +8825,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:8824: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:8828: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:8828: \$? = $ac_status" >&5
+   echo "$as_me:8832: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -8876,11 +8880,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:8879: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:8883: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:8883: \$? = $ac_status" >&5
+   echo "$as_me:8887: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -11260,7 +11264,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11263 "configure"
+#line 11267 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -11356,7 +11360,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11359 "configure"
+#line 11363 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -13312,11 +13316,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:13315: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:13319: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:13319: \$? = $ac_status" >&5
+   echo "$as_me:13323: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -13411,11 +13415,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:13414: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:13418: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:13418: \$? = $ac_status" >&5
+   echo "$as_me:13422: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -13463,11 +13467,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:13466: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:13470: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:13470: \$? = $ac_status" >&5
+   echo "$as_me:13474: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -14986,7 +14990,7 @@ cat >>confdefs.h <<_ACEOF
 #define USE_MYSQL /**/
 _ACEOF
 
- LIBMYSQL_LIBADD="-lmysqlclient"
+ LIBMYSQL_LIBADD="-lmysqlclient_r"
 
   if true; then
   USE_MYSQL_TRUE=
@@ -15121,6 +15125,33 @@ fi
 
 
  if false; then
+  BUILD_ICHAT_GW_TRUE=
+  BUILD_ICHAT_GW_FALSE='#'
+else
+  BUILD_ICHAT_GW_TRUE='#'
+  BUILD_ICHAT_GW_FALSE=
+fi
+
+
+# Check whether --with-ichat-gw was given.
+if test "${with_ichat_gw+set}" = set; then :
+  withval=$with_ichat_gw;
+cat >>confdefs.h <<_ACEOF
+#define BUILD_ICHAT_GW /**/
+_ACEOF
+
+  if true; then
+  BUILD_ICHAT_GW_TRUE=
+  BUILD_ICHAT_GW_FALSE='#'
+else
+  BUILD_ICHAT_GW_TRUE='#'
+  BUILD_ICHAT_GW_FALSE=
+fi
+
+fi
+
+
+ if false; then
   BUILD_RECON_TRUE=
   BUILD_RECON_FALSE='#'
 else
@@ -15832,6 +15863,14 @@ if test -z "${BUILD_APPS_TRUE}" && test -z "${BUILD_APPS_FALSE}"; then
   as_fn_error $? "conditional \"BUILD_APPS\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${BUILD_ICHAT_GW_TRUE}" && test -z "${BUILD_ICHAT_GW_FALSE}"; then
+  as_fn_error $? "conditional \"BUILD_ICHAT_GW\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${BUILD_ICHAT_GW_TRUE}" && test -z "${BUILD_ICHAT_GW_FALSE}"; then
+  as_fn_error $? "conditional \"BUILD_ICHAT_GW\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${BUILD_RECON_TRUE}" && test -z "${BUILD_RECON_FALSE}"; then
   as_fn_error $? "conditional \"BUILD_RECON\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
diff --git a/configure.ac b/configure.ac
index 4a25f84..93aac92 100644
--- a/configure.ac
+++ b/configure.ac
@@ -172,7 +172,7 @@ AM_CONDITIONAL(USE_MYSQL, false)
 AC_ARG_WITH(mysql,
 [  --with-mysql            Link against MySQL client libraries],
  [AC_DEFINE_UNQUOTED(USE_MYSQL, , USE_MYSQL)
- AC_SUBST(LIBMYSQL_LIBADD, "-lmysqlclient")
+ AC_SUBST(LIBMYSQL_LIBADD, "-lmysqlclient_r")
  AM_CONDITIONAL(USE_MYSQL, true)], 
  [ AC_SUBST(LIBMYSQL_LIBADD, "")])
 
@@ -204,6 +204,12 @@ AC_ARG_WITH(apps,
  [AC_DEFINE_UNQUOTED(BUILD_APPS, , BUILD_APPS)
  AM_CONDITIONAL(BUILD_APPS, true)], )
 
+AM_CONDITIONAL(BUILD_ICHAT_GW, false)
+AC_ARG_WITH(ichat-gw,
+[  --with-ichat-gw         Build iChat gateway, links against gloox],
+ [AC_DEFINE_UNQUOTED(BUILD_ICHAT_GW, , BUILD_ICHAT_GW)
+ AM_CONDITIONAL(BUILD_ICHAT_GW, true)], )
+
 AM_CONDITIONAL(BUILD_RECON, false)
 AC_ARG_WITH(recon,
 [  --with-recon            Build recon, links against sipX],
diff --git a/repro/AsyncProcessor.hxx b/repro/AsyncProcessor.hxx
index 8a283fb..96a089e 100644
--- a/repro/AsyncProcessor.hxx
+++ b/repro/AsyncProcessor.hxx
@@ -46,12 +46,6 @@ class AsyncProcessorMessage;
 //   {
 //   }
 //
-//   MyAsyncProcessorAsyncMessage(const MyAsyncProcessorAsyncMessage& orig):
-//      AsyncProcessorMessage(orig)
-//   {
-//   }
-//
-//   virtual Message* clone() const { return new MyAsyncProcessorAsyncMessage(*this); }
 //   virtual EncodeStream& encode(EncodeStream& strm) const { strm << "MyAsyncProcessorAsyncMessage(tid="<<mTid<<")"; return strm; }
 //
 //   Data mDataRequiredToCallBlockingFunction;
diff --git a/repro/AsyncProcessorMessage.hxx b/repro/AsyncProcessorMessage.hxx
index a63b6e5..a1cd062 100644
--- a/repro/AsyncProcessorMessage.hxx
+++ b/repro/AsyncProcessorMessage.hxx
@@ -18,13 +18,8 @@ public:
    {
    }
 
-   AsyncProcessorMessage(const AsyncProcessorMessage& orig):
-      ProcessorMessage(orig),
-      mAsyncProcessor(orig.mAsyncProcessor)
-   {
-   }
+   virtual Message* clone() const { assert(false); return 0; }
 
-   virtual Message* clone() const = 0;
    virtual EncodeStream& encode(EncodeStream& strm) const { strm << "AsyncProcessorMessage(tid="<<mTid<<")"; return strm; }
    virtual EncodeStream& encodeBrief(EncodeStream& strm) const { return encode(strm);}
 
diff --git a/repro/FilterStore.cxx b/repro/FilterStore.cxx
index d1fb482..728c75d 100644
--- a/repro/FilterStore.cxx
+++ b/repro/FilterStore.cxx
@@ -36,7 +36,7 @@ FilterStore::FilterStore(AbstractDb& db):
       filter.pcond2 = 0;
       
       int flags = REG_EXTENDED;
-      if(filter.filterRecord.mActionData.find("$") == Data::npos )
+      if(filter.filterRecord.mActionData.find("$") == Data::npos)
       {
          flags |= REG_NOSUB;
       }
diff --git a/repro/MySqlDb.cxx b/repro/MySqlDb.cxx
index 18d4be4..2c76f47 100644
--- a/repro/MySqlDb.cxx
+++ b/repro/MySqlDb.cxx
@@ -28,6 +28,42 @@ using namespace std;
 
 #define RESIPROCATE_SUBSYSTEM Subsystem::REPRO
 
+extern "C"
+{
+   void mysqlThreadEnd(void*)
+   {
+      mysql_thread_end();
+   }
+}
+
+// This class helps ensure that each thread using the MySQL API's 
+// initialize by calling mysql_thread_init before calling any mySQL functions
+class MySQLInitializer
+{
+   public:
+      MySQLInitializer()
+      {
+         ThreadIf::tlsKeyCreate(mThreadStorage, mysqlThreadEnd);
+      }
+      ~MySQLInitializer()
+      {
+         ThreadIf::tlsKeyDelete(mThreadStorage);
+      }
+      void setInitialized()
+      {
+         ThreadIf::tlsSetValue(mThreadStorage, (void*) true);
+      }
+      bool isInitialized()
+      {
+         // Note:  if value is not set yet then 0 (false) is returned
+         return ThreadIf::tlsGetValue(mThreadStorage) != 0; 
+      }
+
+   private:
+      ThreadIf::TlsKey mThreadStorage;
+};
+static MySQLInitializer g_MySQLInitializer;
+
 MySqlDb::MySqlDb(const Data& server, 
                  const Data& user, 
                  const Data& password, 
@@ -50,7 +86,15 @@ MySqlDb::MySqlDb(const Data& server,
       mResult[i]=0;
    }
 
-   connectToDatabase();
+   mysql_library_init(0, 0, 0);
+   if(!mysql_thread_safe())
+   {
+      ErrLog( << "Repro uses MySQL from multiple threads - you MUST link with a thread safe version of the mySQL client library!");
+   }
+   else
+   {
+      connectToDatabase();
+   }
 }
 
 
@@ -60,6 +104,16 @@ MySqlDb::~MySqlDb()
 }
 
 void
+MySqlDb::initialize() const
+{
+   if(!g_MySQLInitializer.isInitialized())
+   {
+      g_MySQLInitializer.setInitialized();
+      mysql_thread_init();
+   }
+}
+
+void
 MySqlDb::disconnectFromDatabase() const
 {
    if(mConn)
@@ -104,6 +158,7 @@ MySqlDb::connectToDatabase() const
                                    mDBPort,             // port
                                    0,                   // unix socket file
                                    0);                  // client flags
+
    if (ret == 0)
    { 
       int rc = mysql_errno(mConn);
@@ -120,13 +175,16 @@ MySqlDb::connectToDatabase() const
    }
 }
 
-int  
-MySqlDb::query(const Data& queryCommand) const
+int
+MySqlDb::query(const Data& queryCommand, MYSQL_RES** result) const
 {
    int rc = 0;
 
+   initialize();
+
    DebugLog( << "MySqlDb::query: executing query: " << queryCommand);
 
+   Lock lock(mMutex);
    if(mConn == 0 || !mConnected)
    {
       rc = connectToDatabase();
@@ -161,6 +219,20 @@ MySqlDb::query(const Data& queryCommand) const
       }
    }
 
+   // Now store result - if pointer to result pointer was supplied and no errors
+   if(rc == 0 && result)
+   {
+      *result = mysql_store_result(mConn);
+      if(*result == 0)
+      {
+         rc = mysql_errno(mConn);
+         if(rc != 0)
+         {
+            ErrLog( << "MySQL store result failed: error=" << rc << ": " << mysql_error(mConn));
+         }
+      }
+   }
+
    if(rc != 0)
    {
       ErrLog( << " SQL Command was: " << queryCommand) ;
@@ -168,26 +240,26 @@ MySqlDb::query(const Data& queryCommand) const
    return rc;
 }
 
-
 int
-MySqlDb::singleResultQuery(const Data& queryCommand, Data& resultData) const
+MySqlDb::singleResultQuery(const Data& queryCommand, std::vector<Data>& fields) const
 {
-   int rc = query(queryCommand);
+   MYSQL_RES* result=0;
+   int rc = query(queryCommand, &result);
       
    if(rc == 0)
    {
-      MYSQL_RES* result = mysql_store_result(mConn);
       if(result == 0)
       {
-         rc = mysql_errno(mConn);
-         ErrLog( << "MySQL store result failed: error=" << rc << ": " << mysql_error(mConn));
          return rc;
       }
 
       MYSQL_ROW row = mysql_fetch_row(result);
       if(row)
       {
-         resultData = Data(row[0]);
+         for(unsigned int i = 0; i < result->field_count; i++)
+         {
+            fields.push_back(Data(row[i]));
+         }
       }
       else
       {
@@ -202,6 +274,12 @@ MySqlDb::singleResultQuery(const Data& queryCommand, Data& resultData) const
    return rc;
 }
 
+resip::Data& 
+MySqlDb::escapeString(const resip::Data& str, resip::Data& escapedStr) const
+{
+   escapedStr.truncate2(mysql_real_escape_string(mConn, (char*)escapedStr.getBuf(str.size()*2+1), str.c_str(), str.size()));
+   return escapedStr;
+}
 
 bool 
 MySqlDb::addUser(const AbstractDb::Key& key, const AbstractDb::UserRecord& rec)
@@ -218,7 +296,7 @@ MySqlDb::addUser(const AbstractDb::Key& key, const AbstractDb::UserRecord& rec)
          << "', forwardAddress='" << rec.forwardAddress
          << "'";
    }
-   return query(command) == 0;
+   return query(command, 0) == 0;
 }
 
 
@@ -231,7 +309,7 @@ MySqlDb::eraseUser(const AbstractDb::Key& key )
       ds << "DELETE FROM users ";
       userWhereClauseToDataStream(key, ds);
    }
-   query(command);
+   query(command, 0);
 }
 
 
@@ -247,12 +325,12 @@ MySqlDb::getUser( const AbstractDb::Key& key ) const
       userWhereClauseToDataStream(key, ds);
    }
    
-   if(query(command) != 0)
+   MYSQL_RES* result=0;
+   if(query(command, &result) != 0)
    {
       return ret;
    }
    
-   MYSQL_RES* result = mysql_store_result(mConn);
    if (result==0)
    {
       ErrLog( << "MySQL store result failed: error=" << mysql_errno(mConn) << ": " << mysql_error(mConn));
@@ -280,7 +358,7 @@ MySqlDb::getUser( const AbstractDb::Key& key ) const
 resip::Data 
 MySqlDb::getUserAuthInfo(  const AbstractDb::Key& key ) const
 { 
-   Data ret;
+   std::vector<Data> ret;
 
    Data command;
    {
@@ -301,14 +379,14 @@ MySqlDb::getUserAuthInfo(  const AbstractDb::Key& key ) const
       }
    }
 
-   if(singleResultQuery(command, ret) != 0)
+   if(singleResultQuery(command, ret) != 0 || ret.size() == 0)
    {
       return Data::Empty;
    }
    
-   DebugLog( << "Auth password is " << ret);
+   DebugLog( << "Auth password is " << ret.front());
    
-   return ret;
+   return ret.front();
 }
 
 
@@ -324,12 +402,11 @@ MySqlDb::firstUserKey()
    
    Data command("SELECT user, domain FROM users");
 
-   if(query(command) != 0)
+   if(query(command, &mResult[UserTable]) != 0)
    {
       return Data::Empty;
    }
 
-   mResult[UserTable] = mysql_store_result(mConn);
    if(mResult[UserTable] == 0)
    {
       ErrLog( << "MySQL store result failed: error=" << mysql_errno(mConn) << ": " << mysql_error(mConn));
@@ -372,13 +449,14 @@ MySqlDb::dbWriteRecord(const Table table,
    // Check if there is a secondary key or not and get it's value
    char* secondaryKey;
    unsigned int secondaryKeyLen;
+   Data escapedKey;
    if(AbstractDb::getSecondaryKey(table, pKey, pData, (void**)&secondaryKey, &secondaryKeyLen) == 0)
    {
       Data sKey(Data::Share, secondaryKey, secondaryKeyLen);
       DataStream ds(command);
       ds << "REPLACE INTO " << tableName(table)
-         << " SET attr='" << pKey
-         << "', attr2='" << sKey
+         << " SET attr='" << escapeString(pKey, escapedKey)
+         << "', attr2='" << escapeString(sKey, escapedKey)
          << "', value='"  << pData.base64encode()
          << "'";
    }
@@ -386,34 +464,34 @@ MySqlDb::dbWriteRecord(const Table table,
    {
       DataStream ds(command);
       ds << "REPLACE INTO " << tableName(table) 
-         << " SET attr='" << pKey 
+         << " SET attr='" << escapeString(pKey, escapedKey)
          << "', value='"  << pData.base64encode()
          << "'";
    }
 
-   return query(command) == 0;
+   return query(command, 0) == 0;
 }
 
-
 bool 
 MySqlDb::dbReadRecord(const Table table, 
                       const resip::Data& pKey, 
                       resip::Data& pData) const
 { 
    Data command;
+   Data escapedKey;
    {
       DataStream ds(command);
       ds << "SELECT value FROM " << tableName(table) 
-         << " WHERE attr='" << pKey 
+         << " WHERE attr='" << escapeString(pKey, escapedKey)
          << "'";
    }
 
-   if(query(command) != 0)
+   MYSQL_RES* result = 0;
+   if(query(command, &result) != 0)
    {
       return false;
    }
 
-   MYSQL_RES* result = mysql_store_result(mConn);
    if (result == 0)
    {
       ErrLog( << "MySQL store result failed: error=" << mysql_errno(mConn) << ": " << mysql_error(mConn));
@@ -442,17 +520,18 @@ MySqlDb::dbEraseRecord(const Table table,
    Data command;
    {
       DataStream ds(command);
+      Data escapedKey;
       ds << "DELETE FROM " << tableName(table);
       if(isSecondaryKey)
       {
-         ds << " WHERE attr2='" << pKey << "'";
+         ds << " WHERE attr2='" << escapeString(pKey, escapedKey) << "'";
       }
       else
       {
-         ds << " WHERE attr='" << pKey << "'";
+         ds << " WHERE attr='" << escapeString(pKey, escapedKey) << "'";
       }
    }   
-   query(command);
+   query(command, 0);
 }
 
 
@@ -474,22 +553,23 @@ MySqlDb::dbNextKey(const Table table, bool first)
          ds << "SELECT attr FROM " << tableName(table);
       }
       
-      if(query(command) != 0)
+      if(query(command, &mResult[table]) != 0)
       {
          return Data::Empty;
       }
 
-      mResult[table] = mysql_store_result(mConn);
       if (mResult[table] == 0)
       {
          ErrLog( << "MySQL store result failed: error=" << mysql_errno(mConn) << ": " << mysql_error(mConn));
          return Data::Empty;
       }
    }
-   
-   if (mResult[table] == 0)
-   { 
-      return Data::Empty;
+   else
+   {
+      if (mResult[table] == 0)
+      { 
+         return Data::Empty;
+      }
    }
    
    MYSQL_ROW row = mysql_fetch_row(mResult[table]);
@@ -526,9 +606,10 @@ MySqlDb::dbNextRecord(const Table table,
          ds << "SELECT value FROM " << tableName(table);
          if(!key.empty())
          {
+            Data escapedKey;
             // dbNextRecord is used to iterator through database tables that support duplication records
             // it is only appropriate for MySQL tables that contain the attr2 non-unique index (secondary key)
-            ds << " WHERE attr2='" << key << "'";
+            ds << " WHERE attr2='" << escapeString(key, escapedKey) << "'";
          }
          if(forUpdate)
          {
@@ -536,12 +617,11 @@ MySqlDb::dbNextRecord(const Table table,
          }
       }
 
-      if(query(command) != 0)
+      if(query(command, &mResult[table]) != 0)
       {
          return false;
       }
 
-      mResult[table] = mysql_store_result(mConn);
       if (mResult[table] == 0)
       {
          ErrLog( << "MySQL store result failed: error=" << mysql_errno(mConn) << ": " << mysql_error(mConn));
@@ -571,10 +651,10 @@ bool
 MySqlDb::dbBeginTransaction(const Table table)
 {
    Data command("SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ");
-   if(query(command) == 0)
+   if(query(command, 0) == 0)
    {
       command = "START TRANSACTION";
-      return query(command) == 0;
+      return query(command, 0) == 0;
    }
    return false;
 }
@@ -583,14 +663,14 @@ bool
 MySqlDb::dbCommitTransaction(const Table table)
 {
    Data command("COMMIT");
-   return query(command) == 0;
+   return query(command, 0) == 0;
 }
 
 bool 
 MySqlDb::dbRollbackTransaction(const Table table)
 {
    Data command("ROLLBACK");
-   return query(command) == 0;
+   return query(command, 0) == 0;
 }
 
 static const char usersavp[] = "usersavp";
diff --git a/repro/MySqlDb.hxx b/repro/MySqlDb.hxx
index 7c05506..6286c83 100644
--- a/repro/MySqlDb.hxx
+++ b/repro/MySqlDb.hxx
@@ -39,7 +39,8 @@ class MySqlDb: public AbstractDb
       virtual Key firstUserKey();// return empty if no more
       virtual Key nextUserKey(); // return empty if no more 
 
-      virtual int singleResultQuery(const resip::Data& queryCommand, resip::Data& resultData) const;
+      // Perform a query that expects a single result/row - returns all column/field data in a vector
+      virtual int singleResultQuery(const resip::Data& queryCommand, std::vector<resip::Data>& fields) const;
 
    private:
       // Db manipulation routines
@@ -63,9 +64,11 @@ class MySqlDb: public AbstractDb
       virtual bool dbCommitTransaction(const Table table);
       virtual bool dbRollbackTransaction(const Table table);
 
+      void initialize() const;
       void disconnectFromDatabase() const;
       int connectToDatabase() const;
-      int query(const resip::Data& queryCommand) const;
+      int query(const resip::Data& queryCommand, MYSQL_RES** result) const;
+      resip::Data& escapeString(const resip::Data& str, resip::Data& escapedStr) const;
 
       resip::Data mDBServer;
       resip::Data mDBUser;
@@ -76,7 +79,11 @@ class MySqlDb: public AbstractDb
 
       mutable MYSQL* mConn;
       mutable MYSQL_RES* mResult[MaxTable];
-      mutable bool mConnected;
+      mutable volatile bool mConnected;
+      // when multiple threads are in use with the same connection, you need to 
+      // mutex calls to mysql_query and mysql_store_result: 
+      // http://dev.mysql.com/doc/refman/5.1/en/threaded-clients.html
+      mutable resip::Mutex mMutex;  
 
       const char* tableName( Table table ) const;
       void userWhereClauseToDataStream(const Key& key, resip::DataStream& ds) const;
diff --git a/repro/ReproRunner.cxx b/repro/ReproRunner.cxx
index 0485bc0..33a089b 100644
--- a/repro/ReproRunner.cxx
+++ b/repro/ReproRunner.cxx
@@ -251,6 +251,7 @@ ReproRunner::run(int argc, char** argv)
    }
 
    mRunning = true;
+
    return true;
 }
 
@@ -487,6 +488,7 @@ ReproRunner::createSipStack()
    InteropHelper::setOutboundVersion(mProxyConfig->getConfigInt("OutboundVersion", 5626));
    InteropHelper::setOutboundSupported(mProxyConfig->getConfigBool("DisableOutbound", false) ? false : true);
    InteropHelper::setRRTokenHackEnabled(mProxyConfig->getConfigBool("EnableFlowTokens", false));
+   InteropHelper::setAssumeFirstHopSupportsOutboundEnabled(mProxyConfig->getConfigBool("AssumeFirstHopSupportsOutbound", false));
    Data clientNATDetectionMode = mProxyConfig->getConfigData("ClientNatDetectionMode", "DISABLED");
    if(isEqualNoCase(clientNATDetectionMode, "ENABLED"))
    {
@@ -1240,7 +1242,7 @@ ReproRunner::makeRequestProcessorChain(ProcessorChain& chain)
       // add simple static route monkey
       chain.addProcessor(std::auto_ptr<Processor>(new SimpleStaticRoute(*mProxyConfig))); 
    }
-      
+
    // Add location server monkey
    chain.addProcessor(std::auto_ptr<Processor>(new LocationServer(*mProxyConfig, *mRegistrationPersistenceManager, mAuthRequestDispatcher)));
 
diff --git a/repro/RouteStore.cxx b/repro/RouteStore.cxx
index 506d816..50e844d 100644
--- a/repro/RouteStore.cxx
+++ b/repro/RouteStore.cxx
@@ -32,22 +32,22 @@ RouteStore::RouteStore(AbstractDb& db):
       route.key = key;
       route.preq = 0;
       
-      if( !route.routeRecord.mMatchingPattern.empty() )
+      if(!route.routeRecord.mMatchingPattern.empty())
       {
-        int flags = REG_EXTENDED;
-        if( route.routeRecord.mRewriteExpression.find("$") == Data::npos )
-        {
-          flags |= REG_NOSUB;
-        }
-        route.preq = new regex_t;
-        int ret = regcomp( route.preq, route.routeRecord.mMatchingPattern.c_str(), flags );
-        if( ret != 0 )
-        {
-          delete route.preq;
-          ErrLog( << "Routing rule has invalid match expression: "
-                  << route.routeRecord.mMatchingPattern );
-          route.preq = 0;
-        }
+         int flags = REG_EXTENDED;
+         if(route.routeRecord.mRewriteExpression.find("$") == Data::npos)
+         {
+            flags |= REG_NOSUB;
+         }
+         route.preq = new regex_t;
+         int ret = regcomp(route.preq, route.routeRecord.mMatchingPattern.c_str(), flags);
+         if(ret != 0)
+         {
+            delete route.preq;
+            ErrLog(<< "Routing rule has invalid match expression: "
+                   << route.routeRecord.mMatchingPattern);
+            route.preq = 0;
+         }
       }
 
       mRouteOperators.insert( route );
diff --git a/repro/SiloStore.cxx b/repro/SiloStore.cxx
index 615d3b8..9f083de 100644
--- a/repro/SiloStore.cxx
+++ b/repro/SiloStore.cxx
@@ -3,7 +3,6 @@
 #include "rutil/Lock.hxx"
 
 #include "resip/stack/SipMessage.hxx"
-#include "resip/stack/ExtensionHeader.hxx"
 
 #include "repro/SiloStore.hxx"
 #include "rutil/WinLeakCheck.hxx"
diff --git a/repro/monkeys/MessageSilo.cxx b/repro/monkeys/MessageSilo.cxx
index f04ea0c..d2dc3be 100644
--- a/repro/monkeys/MessageSilo.cxx
+++ b/repro/monkeys/MessageSilo.cxx
@@ -33,13 +33,7 @@ public:
    {
    }
 
-   AsyncAddToSiloMessage(const AsyncAddToSiloMessage& orig):
-      AsyncProcessorMessage(orig)
-   {
-   }
-
-   virtual Message* clone() const { return new AsyncAddToSiloMessage(*this); }
-   virtual EncodeStream& encode(EncodeStream& strm) const { strm << "AsyncAddToSiloMessage(tid=" << mTid << ")"; return strm; }
+   virtual EncodeStream& encode(EncodeStream& strm) const { strm << "AsyncAddToSiloMessage(tid=" << mTid << ", aor=" << mDestUri << ")"; return strm; }
 
    Data mDestUri;
    Data mSourceUri;
@@ -58,13 +52,7 @@ public:
    {
    }
 
-   AsyncDrainSiloMessage(const AsyncAddToSiloMessage& orig):
-      AsyncProcessorMessage(orig)
-   {
-   }
-
-   virtual Message* clone() const { return new AsyncDrainSiloMessage(*this); }
-   virtual EncodeStream& encode(EncodeStream& strm) const { strm << "AsyncDrainSiloMessage(tid=" << mTid << ")"; return strm; }
+   virtual EncodeStream& encode(EncodeStream& strm) const { strm << "AsyncDrainSiloMessage(aor=" << mAor << ")"; return strm; }
 
    Data mAor;
    ContactList mRequestContacts;
@@ -88,7 +76,7 @@ MessageSilo::MessageSilo(ProxyConfig& config, Dispatcher* asyncDispatcher) :
    if(!destFilterRegex.empty())
    {
       mDestFilterRegex= new regex_t;
-      int ret = regcomp(mDestFilterRegex, destFilterRegex.c_str(), REG_EXTENDED);
+      int ret = regcomp(mDestFilterRegex, destFilterRegex.c_str(), REG_EXTENDED | REG_NOSUB);
       if( ret != 0 )
       {
          delete mDestFilterRegex;
@@ -99,7 +87,7 @@ MessageSilo::MessageSilo(ProxyConfig& config, Dispatcher* asyncDispatcher) :
    if(!mimeTypeFilterRegex.empty())
    {
       mMimeTypeFilterRegex= new regex_t;
-      int ret = regcomp(mMimeTypeFilterRegex, mimeTypeFilterRegex.c_str(), REG_EXTENDED);
+      int ret = regcomp(mMimeTypeFilterRegex, mimeTypeFilterRegex.c_str(), REG_EXTENDED | REG_NOSUB);
       if( ret != 0 )
       {
          delete mMimeTypeFilterRegex;
diff --git a/repro/monkeys/RequestFilter.cxx b/repro/monkeys/RequestFilter.cxx
index 96e0f9d..5cb58c5 100644
--- a/repro/monkeys/RequestFilter.cxx
+++ b/repro/monkeys/RequestFilter.cxx
@@ -35,17 +35,11 @@ public:
    {
    }
 
-   RequestFilterAsyncMessage(const RequestFilterAsyncMessage& orig):
-      AsyncProcessorMessage(orig)
-   {
-   }
-
-   virtual Message* clone() const { return new RequestFilterAsyncMessage(*this); }
-   virtual EncodeStream& encode(EncodeStream& strm) const { strm << "RequestFilterAsyncMessage(tid="<<mTid<<")"; return strm; }
+   virtual EncodeStream& encode(EncodeStream& strm) const { strm << "RequestFilterAsyncMessage(tid=" << mTid << ")"; return strm; }
 
    Data mQuery;
    int mQueryResult;
-   Data mQueryResultData;
+   std::vector<Data> mQueryResultData;
 };
 
 RequestFilter::RequestFilter(ProxyConfig& config,
@@ -146,14 +140,16 @@ RequestFilter::process(RequestContext &rc)
 
    if (async)
    {
-      InfoLog(<< "RequestFilter query complete: queryResult=" << async->mQueryResult << ", resultData=" << async->mQueryResultData);
-
-      if(async->mQueryResult == 0)  // If query was successful, then get query result
+      if(async->mQueryResult == 0 && async->mQueryResultData.size() > 0)  // If query was successful, then get query result
       {
-         return applyActionResult(rc, async->mQueryResultData);
+         InfoLog(<< "RequestFilter query completed successfully: queryResult=" << async->mQueryResult << ", resultData=" << async->mQueryResultData.front());
+
+         return applyActionResult(rc, async->mQueryResultData.front());
       }
       else
       {
+         InfoLog(<< "RequestFilter query failed: queryResult=" << async->mQueryResult);
+
          return applyActionResult(rc, mDefaultDBErrorBehavior);
       }
    }
diff --git a/repro/repro.config b/repro/repro.config
index 284c4c4..be19cfd 100644
--- a/repro/repro.config
+++ b/repro/repro.config
@@ -323,6 +323,19 @@ DisableOutbound = true
 # (ie. 5, 8, etc.)
 OutboundVersion = 5626
 
+# There are cases where the first hop in a particular network supports the concept of outbound
+# and ensures all messaging for a client is delivered over the same connection used for
+# registration.  This could be a SBC or other NAT traversal aid router that uses the Path 
+# header.  However such endpoints may not be 100% compliant with outbound RFC and may not 
+# include a ;ob parameter in the path header.  This parameter is required in order for repro
+# to have knowledge that the first hop does support outbound, and it will reject registrations
+# that appear to be using outboud (ie. instanceId and regId) with a 439 (First Hop Lacks Outbound
+# Support).  In this case it can be desirable when using repro as the registrar to not reject
+# REGISTRATION requests that contain an instanceId and regId with a 439.
+# If this setting is enabled, then repro will assume the first hop supports outbound 
+# and not return this error.
+AssumeFirstHopSupportsOutbound = false
+
 # Enable use of flow-tokens in non-outbound cases
 # WARNING: Before enabling this, ensure you have a RecordRouteUri setup, or are using
 # the alternate transport specification mechanism and defining a RecordRouteUri per
diff --git a/repro/repro_10_0.vcxproj b/repro/repro_10_0.vcxproj
index c5cf84d..0e567a8 100644
--- a/repro/repro_10_0.vcxproj
+++ b/repro/repro_10_0.vcxproj
@@ -269,7 +269,7 @@ endlocal
 </Command>
     </PreBuildEvent>
     <ClCompile>
-      <AdditionalIncludeDirectories>$(ProjectDir)../;$(ProjectDir)../resip/stack;$(ProjectDir)../contrib/db/build_windows;$(ProjectDir)../contrib/MySQLConnectorC/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(ProjectDir)../;$(ProjectDir)../resip/stack;$(ProjectDir)../contrib/db/build_windows;$(ProjectDir)../contrib/MySQLConnectorC/include;$(ProjectDir)../contrib/pcre;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;USE_IPV6;USE_MYSQL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <RuntimeTypeInfo>true</RuntimeTypeInfo>
diff --git a/resip/dum/RegistrationHandler.cxx b/resip/dum/RegistrationHandler.cxx
index 15fcad2..c05fe85 100644
--- a/resip/dum/RegistrationHandler.cxx
+++ b/resip/dum/RegistrationHandler.cxx
@@ -8,12 +8,12 @@
 
 using namespace resip;
 
-void 
-ClientRegistrationHandler::onFlowTerminated(ClientRegistrationHandle h)
-{
+void 
+ClientRegistrationHandler::onFlowTerminated(ClientRegistrationHandle h)
+{
    InfoLog (<< "ClientRegistrationHandler::onFlowTerminated, refreshing registration to open new flow");
    h->requestRefresh();
-}
+}
 
 void
 ServerRegistrationHandler::getGlobalExpires(const SipMessage& msg, SharedPtr<MasterProfile> masterProfile,
diff --git a/resip/dum/ServerRegistration.cxx b/resip/dum/ServerRegistration.cxx
index 8e2ab23..44fe12a 100644
--- a/resip/dum/ServerRegistration.cxx
+++ b/resip/dum/ServerRegistration.cxx
@@ -461,7 +461,8 @@ ServerRegistration::tryFlow(ContactInstanceRecord& rec,
          resip::NameAddr& contact(rec.mContact);
          if(contact.exists(p_Instance) && contact.exists(p_regid))
          {
-            if(!msg.empty(h_Paths) && msg.header(h_Paths).back().uri().exists(p_ob))
+            if(!msg.empty(h_Paths) && (msg.header(h_Paths).back().uri().exists(p_ob) ||
+                                       InteropHelper::getAssumeFirstHopSupportsOutboundEnabled()))
             {
                rec.mRegId=contact.param(p_regid);
                // Edge-proxy is directly connected to the client, and ready to 
diff --git a/resip/stack/InteropHelper.cxx b/resip/stack/InteropHelper.cxx
index aa467ba..3a8cbf5 100644
--- a/resip/stack/InteropHelper.cxx
+++ b/resip/stack/InteropHelper.cxx
@@ -8,6 +8,7 @@ unsigned int InteropHelper::flowTimerSeconds=0;  // 0 = disabled
 unsigned int InteropHelper::flowTimerGracePeriodSeconds=30;
 bool InteropHelper::useRRTokenHack=false;
 InteropHelper::ClientNATDetectionMode InteropHelper::clientNATDetection=InteropHelper::ClientNATDetectionDisabled;
+bool InteropHelper::assumeFirstHopSupportsOutbound=false;
 }
 
 /* ====================================================================
diff --git a/resip/stack/InteropHelper.hxx b/resip/stack/InteropHelper.hxx
index 21098dc..edf8224 100644
--- a/resip/stack/InteropHelper.hxx
+++ b/resip/stack/InteropHelper.hxx
@@ -57,6 +57,20 @@ class InteropHelper
       static InteropHelper::ClientNATDetectionMode getClientNATDetectionMode(){return clientNATDetection;}
       static void setClientNATDetectionMode(InteropHelper::ClientNATDetectionMode mode) {clientNATDetection=mode;}
 
+      // There are cases where the first hop in a particular network supports the concept of outbound
+      // and ensures all messaging for a client is delivered over the same connection used for
+      // registration.  This could be a SBC or other NAT traversal aid router that uses the Path 
+      // header.  However such endpoints may not be 100% compliant with outbound RFC and may not 
+      // include a ;ob parameter in the path header.  This parameter is required in order for repro
+      // to have knowledge that the first hop does support outbound, and it will reject registrations
+      // that appear to be using outboud (ie. instanceId and regId) with a 439 (First Hop Lacks Outbound
+      // Support).  In this case it can be desirable when using repro as the registrar to not reject
+      // REGISTRATION requests that contain an instanceId and regId with a 439.
+      // If this setting is enabled, then repro will assume the first hop supports outbound 
+      // and not return this error.
+      static bool getAssumeFirstHopSupportsOutboundEnabled(){return assumeFirstHopSupportsOutbound;}
+      static void setAssumeFirstHopSupportsOutboundEnabled(bool enabled) {assumeFirstHopSupportsOutbound=enabled;}
+
    private:
       InteropHelper();
       ~InteropHelper();
@@ -67,6 +81,7 @@ class InteropHelper
       static unsigned int flowTimerGracePeriodSeconds;
       static bool useRRTokenHack;
       static ClientNATDetectionMode clientNATDetection;
+      static bool assumeFirstHopSupportsOutbound;
 };
 }
 
diff --git a/resip/stack/TransportSelector.cxx b/resip/stack/TransportSelector.cxx
index 1317bdc..d21151a 100644
--- a/resip/stack/TransportSelector.cxx
+++ b/resip/stack/TransportSelector.cxx
@@ -563,13 +563,14 @@ TransportSelector::findTransportByVia(SipMessage* msg, const Tuple& target,
    assert(!msg->const_header(h_Vias).empty());
    const Via& via = msg->const_header(h_Vias).front();
 
-   if (via.sentHost().empty())
+   if (via.sentHost().empty() && via.transport().empty())
    {
-      return NULL;
+      return 0;
    }
 
    // XXX: Is there better way to do below (without the copy)?
-   source = Tuple(via.sentHost(), via.sentPort(), target.ipVersion(), target.getType());
+   source = Tuple(via.sentHost(), via.sentPort(), target.ipVersion(), 
+      via.transport().empty() ? target.getType() : toTransportType(via.transport()));  // Transport type is pre-populated in via, lock to it
 
    if ( target.mFlowKey!=0 && (source.getPort()==0 || source.isAnyInterface()) )
    {
@@ -585,6 +586,7 @@ TransportSelector::findTransportByVia(SipMessage* msg, const Tuple& target,
       // transmit() will later use determineSourceInterface() to
       // get the actual interface to populate the Contact & Via headers.
       // Not sure if we should support this case or just assert.
+      // .gh. This should be supported, in case only the transport part of the Via is set
       msg->header(h_Vias).front().sentHost().clear();
    }
 

-- 
reSIProcate



More information about the Pkg-voip-commits mailing list